2008-09-05 15 views
19

Как виртуальная машина генерирует собственный машинный код на лету и выполняет его?Методы генерации кода JIT

Предполагая, что вы можете выяснить, какие наборы машинного кода вы хотите испустить, как вы его используете?

Это что-то нехорошее, как сопоставление мнемонических инструкций с двоичными кодами, наложение его на указатель char * и литье его как функции и выполнение?

Или вы создадите временную разделяемую библиотеку (DLL или .so или что-то еще) и загрузите ее в память с помощью стандартных функций, таких как LoadLibrary?

ответ

8

Вы можете просто указать program counter точку на код, который вы хотите выполнить. Помните, что данные могут быть данными или кодом. На x86 счетчик программ - это регистр EIP. IP-часть EIP обозначает указатель на инструкцию. Команда JMP вызывается для перехода к адресу. После прыжка EIP будет содержать этот адрес.

Это что-то такое хакерское, как сопоставление мнемонических инструкций с двоичными кодами, наложение его на указатель char * и литье его как функции и выполнение?

Да. Это один из способов сделать это. Полученный код будет отлит до pointer to function в C.

1

Насколько я знаю, он компилирует все в памяти, потому что для оптимизации кода (например, вставки с течением времени) требуется выполнить некоторую эвристику, но вы можете взглянуть на роторный выпуск Shared Source Common Language Infrastructure 2.0. Вся база кода идентична .NET, за исключением Jitter и GC.

6

Это что-то нехорошее, как сопоставление мнемонических инструкций с двоичными кодами, наложение его на указатель char * и литье его как функции и выполнение?

Да, если вы делали это на C или C++ (или что-то подобное), это именно то, что вы сделали бы.

Это кажется взломанным, но это фактически артефакт языкового дизайна. Помните, что фактический алгоритм, который вы хотите использовать, очень прост: определите, какие инструкции вы хотите использовать, загрузите их в буфер в памяти и перейдите к началу этого буфера.

Если вы действительно пытаетесь это сделать, убедитесь, что вы получили соглашение о вызове, когда вы вернетесь в свою программу на C. Я думаю, что если бы я хотел создать код, я бы искал библиотеку, чтобы позаботиться об этом для меня. Недавно Наноит был в новостях; вы можете посмотреть на это.

4

Yup. Вы просто создаете char * и выполняете его. Однако вам нужно отметить пару деталей. Символ * должен находиться в исполняемом разделе памяти и должен иметь правильное выравнивание.

В дополнение к nanojit вы также можете проверить LLVM, который является другой библиотекой, способной скомпилировать различные представления программ до указателя функции. Интерфейс чистый, и сгенерированный код имеет тенденцию быть эффективным.

1

О создании библиотеки DLL: дополнительный необходимый ввод-вывод для этого, плюс связывание, а также сложность генерации DLL-формата сделали бы это намного более сложным, и, прежде всего, они убьют производительность; Кроме того, в конце вы все еще вызываете указатель на загруженный код, поэтому ... Кроме того, компиляция JIT может происходить по одному методу за раз, и если вы хотите сделать это, вы создадите большое количество небольших DLL.

О требовании «исполняемого раздела», вызывающем mprotect() в системах POSIX, можно исправить разрешения (есть аналогичный API на Win32). Вы должны сделать это для большого сегмента памяти, а не для одного метода, поскольку в противном случае это будет слишком медленным.

На простой x86 вы не заметите проблемы, на x86 с PAE или 64-разрядными 64-разрядными 64-разрядными машинами AMD64/Intel вы получите segfault.

1

Это то, как Hacky, как отображение мнемонические инструкции двоичных кодов , запихивая его в символ * указателя и приведения его в качестве функции и исполняющего?

Да, это работает.

Для этого в окнах необходимо установить PAGE_EXECUTE_READWRITE в выделенном блоке:

void (*MyFunc)() = (void (*)()) VirtualAlloc(NULL, sizeofblock, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 

//Now fill up the block with executable code and issue- 

MyFunc();