N64: перекомпилированный - это инструмент для статически перекомпилирования бинарных файлов N64 в код C, который может быть составлен для любой платформы. Это можно использовать для портов или инструментов, а также для моделирования поведения значительно быстрее, чем переводчики или динамическое перекомпиляция. Более широко, его можно использовать в любом контексте, где вы хотите запустить какую -то часть двоичного файла N64 в отдельной среде.
Это не первый проект, который использует статическое перекомпиляцию на двоичных файлах игровой консоли. Хорошо известным примером является Jamulator, который нацелен на двоичные файлы NES. Кроме того, это даже не первый проект, применяющий статическое перекомпиляцию к проектам, связанным с N64: статическое перекомпиляция IDO перекомпиляет компилятор SGI IRIX IDO на современных системах, чтобы способствовать сопоставлению декомпиляции игр N64. Этот проект работает аналогично проекту IDO Static Recomp в некоторых отношениях, и этот проект был моим основным вдохновением для этого.
Через переносчика работает, принимая список символов и метаданных вместе с бинарным с целью разделения входного двоичного файла на функции, которые каждая из них индивидуально переписывается в функцию C, названную в соответствии с метаданными.
Инструкции обрабатываются один за другим, а соответствующий код C испускается, когда каждый обрабатывается. Этот перевод очень буквален, чтобы сохранить сложность низкой. For example, the instruction addiu $r4, $r4, 0x20
, which adds 0x20
to the 32-bit value in the low bytes of register $r4
and stores the sign extended 64-bit result in $r4
, gets recompiled into ctx->r4 = ADD32(ctx->r4, 0X20);
Инструкция jal
(прыжок и ссылка) переписывается непосредственно в функциональный вызов, а инструкции j
или b
(безусловные прыжки и ветви), которые могут быть идентифицированы как оптимизация хвостового вызова, также переписываются в функциональные вызовы. Слоты задержки ветви обрабатываются путем дублирования инструкций по мере необходимости. Существуют и другие конкретные поведения для определенных инструкций, такие как переписка, пытающегося превратить инструкцию jr
в оператор переключения, если он может сказать, что он используется с таблицей прыжков. Черезденье было в основном протестировано на двоичных файлах, построенных со старыми компиляторами MIPS (например, MIPS GCC 2.7.2 и IDO), а также Modern Clang, нацеленные на MIP. Modern MIPS GCC может отключить переписывателя из -за определенных оптимизаций, которые он может сделать, но эти случаи, вероятно, можно избежать, установив определенные флаги компиляции.
Каждая выходная функция, создаваемая перепискателем, в настоящее время выпускается в свой собственный файл. В будущем может быть предоставлена опция для групповых функций в выходные файлы, что должно помочь улучшить время сборки вывода перекомпилятора за счет снижения ввода -вывода файлов в процессе сборки.
Выход перекомпилятора может быть скомпилирован с любым компилятором C (протестирован с помощью MSVC, GCC и Clang). Ожидается, что вывод будет использоваться со временем выполнения, которая может обеспечить необходимую функциональность и реализации макросов для его запуска. Средство выполнения предусмотрено в N64modernruntime, которое можно увидеть в действии в проекте Zelda 64: перекомпилированный.
Статически связанные и перемещаемые наложения могут быть обработаны этим инструментом. В обоих случаях инструмент издает поиск функций для перепрыгивания и регистрации (т.е. указателями функций или виртуальных функций), которые предоставленное время выполнения может реализовать с использованием любой таблицы поиска. Например, инструкция jalr $25
будет переписываться как LOOKUP_FUNC(ctx->r25)(rdram, ctx);
Среда выполнения может затем сохранить список, в каких разделах загружаются разделы программы, и на каком адресу они выполняются, чтобы определить, какую функцию выполнять всякий раз, когда поиск запускается во время выполнения.
Для перемещенных наложений инструмент будет изменять поддерживаемые инструкции, обладающие данными переселения ( lui
, addiu
, загрузки и хранилища), издавая дополнительный макрос, который позволяет во время выполнения переместить поля непосредственного значения инструкции. For example, the instruction lui $24, 0x80C0
in a section beginning at address 0x80BFA100
with a relocation against a symbol with an address of 0x80BFA730
will get recompiled as ctx->r24 = S32(RELOC_HI16(1754, 0X630) << 16);
, где 1754 год является индексом этого раздела. Среда выполнения может затем реализовать макросы RELOC_HI16 и RELOC_LO16, чтобы обрабатывать изменение непосредственного на основе текущего загруженного адреса раздела.
Поддержка переезда для картирования TLB будет в будущем, что добавит возможность предоставить список переездов MIPS32, чтобы время выполнения могла переместить их при нагрузке. Комбинирование этого с функциональностью, используемой для перемещаемых наложений, должно разрешать запуск большинства кода с отображением TLB без штрафа за производительность при каждом доступе к оперативной памяти.
Перепилятор настроен путем предоставления файла TOML для настройки поведения перекомпилятора, который является единственным аргументом, предоставленным для перекомпилятора. TOML - это то, где вы указываете пути ввода и выходных файлов, а также, опционально загрязняющие конкретные функции, пропустить перекомпиляцию конкретных функций и исправлять отдельные инструкции в целевом бинарном бинарии. Также существует запланированная функциональность, чтобы иметь возможность излучать крючки на выходе перекомпилятора, добавив их в разделы Toml ( [[patches.func]]
и [[patches.hook]]
связанных Toml ниже), но это в настоящее время невыражение. Документация по каждому варианту, который предоставляет перекомпилятор, в настоящее время недоступна, но пример TOML можно найти в Zelda 64: перекомпилированный проект здесь.
В настоящее время единственный способ предоставить необходимые метаданные - это передавать файл ELF в этот инструмент. Самый простой способ получить такой эльф - это создать разборку или декомпиляцию целевого бинарника, но есть поддержка для предоставления метаданных через пользовательский формат, чтобы обойти необходимость сделать это в будущем.
Этот инструмент также может быть настроен для перекомпиляции в режиме «Single File Output» через опцию в конфигурации Toml. Это излучит все функции в предоставленном эльфе в один выходной файл. Цель этого режима состоит в том, чтобы иметь возможность компилировать исправленные версии функций из целевого двоичного файла.
Этот режим может быть объединен с функциональностью, предоставленной почти всеми линкерами (LD, LLD, MSVC Link.exe и т. Д.), Чтобы заменить функции от исходного вывода перекомпилятора на модифицированные версии. Эти линкеры ищут символы в статической библиотеке только в том случае, если они еще не были найдены в предыдущем входном файле, поэтому предоставление перекомпилированных исправлений для линкера до предоставления исходного вывода перекомпилятора приведет к тому, что патчи придают приоритет над функциями с одинаковыми именами. Из исходного вывода переписка.
Это экономит огромное количество времени, а также итерация на участках для целевого двоичного файла, так как вы можете обойти повторную компилятор на целевом двоичном двоике, а также собирать исходный выход перекомпилятора. Пример использования этого режима вывода отдельного файла для этой цели можно найти в Zelda 64: перекомпилированный проект здесь, с соответствующим Makefil, который используется для создания эльфа для этих исправлений здесь.
RSP микрокод также может быть перекомпилирован этим инструментом. В настоящее время нет поддержки для перекомпиляции наложений RSP, но они могут быть добавлены в будущем при желании. Документация о том, как использовать эту функциональность, скоро появится.
Этот проект может быть построен с помощью CMAKE 3.20 или выше, а также компилятора C ++, который поддерживает C ++ 20. В этом репо используется подмодулы GIT, поэтому обязательно обязательно рекурсивно клон ( git clone --recurse-submodules
) или инициализировать подмодули рекурсивно после клонирования ( git submodule update --init --recursive
). Оттуда здание идентично любому другому проекту Cmake, например, запустите cmake
в папке Target Build и укажите его в корне этого репо, затем запустите cmake --build .
из этой целевой папки.