Как использовать динамическую перекомпиляцию? - PullRequest
5 голосов
/ 03 апреля 2010

Мне стало известно, что некоторые эмуляторы и виртуальные машины используют динамическую перекомпиляцию. Как они это делают? В Си я знаю, как вызвать функцию в оперативной памяти, используя приведение типов (хотя я никогда не пробовал), но как можно прочитать коды операций и сгенерировать код для нее? Нужно ли человеку иметь готовые сборочные блоки и копировать / пакетировать их вместе? сборка написана на C? Если да, то как вы найдете длину кода? Как вы учитываете системные прерывания?

-edit-

системные прерывания и то, как (пере) компилировать данные - это то, что меня больше всего интересует. После дополнительных исследований я узнал, что один человек (источник недоступен) использовал js, прочитал машинный код, вывел js source и использовал eval «скомпилировать» источник js. Интересно.

Ответы [ 7 ]

1 голос
/ 13 апреля 2010

Похоже, я ДОЛЖЕН знать машинный код целевой платформы для динамической перекомпиляции

Да, абсолютно. Вот почему части виртуальной машины Java должны быть переписаны (а именно JIT) для каждой архитектуры.

Когда вы пишете виртуальную машину, вы имеете в виду определенную архитектуру хоста и определенную гостевую архитектуру. Портативную виртуальную машину лучше называть эмулятором, поскольку вы будете эмулировать каждую инструкцию гостевой архитектуры (гостевые регистры будут представлены в виде переменных хоста, а не регистров хоста).

Когда гостевая и хост-архитектура одинаковы, как у VMWare, существует масса (довольно аккуратных) оптимизаций, которые вы можете сделать, чтобы ускорить виртуализацию - сегодня мы находимся на этапе, когда этот тип виртуальной машины ВРЕМЕННО медленнее, чем работает непосредственно на процессоре. Конечно, это крайне архитектурно-зависимо - вам, вероятно, было бы лучше переписать большую часть VMWare с нуля, чем пытаться ее портировать.

1 голос
/ 16 апреля 2010

Это широко открытый вопрос, не уверен, куда вы хотите пойти с ним. Википедия охватывает общую тему с общим ответом. Собственный код, который эмулируется или виртуализируется, заменяется собственным кодом. Чем больше кода выполняется, тем больше заменяется.

Я думаю, что вам нужно сделать несколько вещей, сначала решите, говорите ли вы об эмуляции или виртуальной машине, такой как vmware или virtualbox. Эмуляция процессора и аппаратного обеспечения эмулируется с помощью программного обеспечения, поэтому эмулятор читает следующую инструкцию, код операции разбирается на части по коду, и вы сами определяете, что с ним делать. Я занимался эмуляцией 6502 и статическим двоичным переводом, который представляет собой динамическую перекомпиляцию, но предварительно обработанную вместо реального времени. Таким образом, ваш эмулятор может взять LDA # 10, загрузить с немедленной, эмулятор видит загрузку немедленной инструкции, знает, что он должен прочитать следующий байт, который является непосредственным, эмулятор имеет переменную в коде для регистра A и помещает непосредственное значение в этой переменной. Перед выполнением инструкции эмулятор должен обновить флаги, в этом случае нулевой флаг очищен, N-флаг очищен, C и V не тронуты. Но что, если следующая инструкция была немедленной загрузкой X? Ничего страшного, верно? Что ж, load x также изменит флаги z и n, поэтому в следующий раз, когда вы выполните команду load, вы можете выяснить, что вам не нужно вычислять флаги, потому что они будут уничтожены, это мертвый код в эмуляции. Вы можете продолжить такое мышление, скажем, вы видите код, который копирует регистр x в регистр a, затем помещает регистр в стек, затем копирует регистр y в регистр и помещает в стек, вы можете заменить этот блок просто нажав регистры x и y в стеке. Или вы можете увидеть пару операций добавления с переносами, соединенных вместе для выполнения 16-разрядного добавления и сохранения результата в смежных ячейках памяти. По сути, поиск операций, которые не может выполнять эмулируемый процессор, но легко выполняется в эмуляции. Статическая двоичная трансляция, которую я предлагаю вам изучить перед динамической перекомпиляцией, выполняет этот анализ и трансляцию статическим образом, как, например, перед запуском кода. Вместо того, чтобы эмулировать, вы, например, переводите коды операций в C и удаляете столько мертвого кода, сколько можете (приятная особенность - компилятор C может удалить больше мертвого кода за вас).

Как только концепция эмуляции и перевода понятна, вы можете попытаться сделать это динамически, это, конечно, не тривиально. Я бы предложил попробовать снова выполнить статический перевод двоичного кода в машинный код целевого процессора, что является хорошим упражнением. Я не буду пытаться динамически оптимизировать время выполнения, пока мне не удастся выполнить их статически по отношению к / двоичному файлу.

Виртуализация - это отдельная история, вы говорите о запуске одного и того же процессора на одном процессоре. Так х86 на х86 например. прелесть здесь в том, что, используя не старые процессоры x86, вы можете взять виртуализированную программу и запускать действительные коды операций на реальном процессоре, без эмуляции. Вы настраиваете ловушки, встроенные в процессор, для захвата вещей, поэтому загрузка значений в AX и добавление BX и т. Д. Все это происходит в реальном времени на процессоре, когда AX хочет читать или записывать память, это зависит от вашего механизма прерываний, если адреса находятся в пределах пространство памяти виртуальных машин, ловушек нет, но, скажем, программа записывает на адрес, который является виртуализированным UART, у вас есть ловушка процессора, которая затем vmware или любой другой декодер, который записывает и эмулирует его, взаимодействуя с реальным последовательным портом. Хотя эта инструкция не была в реальном времени, ее выполнение заняло много времени. Что бы вы могли сделать, если бы выбрали это, замените эту инструкцию или набор инструкций, которые записывают значение в виртуализированный последовательный порт и, возможно, затем записывают его на другой адрес, который может быть реальным последовательным портом или каким-либо другим местоположением, которое не будет вызвать ошибку, приводящую к тому, что vm manager должен эмулировать инструкцию. Или добавьте некоторый код в пространство виртуальной памяти, которое выполняет запись в uart без прерывания, и вместо этого добавьте этот код в эту процедуру записи uart. В следующий раз, когда вы нажмете этот кусок кода, он будет запущен в реальном времени.

Еще одна вещь, которую вы можете сделать, это, например, эмулировать и, по ходу дела, переводить в виртуальный промежуточный байт-код, такой как llvm. Оттуда вы можете перевести с промежуточного компьютера на собственный компьютер, в конечном итоге заменяя большие разделы программы, если не все. Вам все равно придется иметь дело с периферией и вводом / выводом.

1 голос
/ 10 апреля 2010

Вполне возможно - хотя, очевидно, нетривиально - дизассемблировать код из указателя памяти, оптимизировать код каким-либо образом, а затем записать обратно оптимизированный код - либо в исходное местоположение, либо в новое место с переходом, исправленным в исходное местоположение.

Конечно, эмуляторам и виртуальным машинам не нужно перезаписывать, они могут делать это во время загрузки.

0 голосов
/ 16 апреля 2010

Этот подход обычно используется средами с промежуточным представлением байтового кода (например, Java, .net). Байт-код содержит достаточно «высокоуровневых» структур (высокий уровень в терминах более высокого уровня, чем машинный код), так что виртуальная машина может извлекать куски из байт-кода и заменять его скомпилированным блоком памяти. Виртуальная машина обычно решает, какая часть компилируется, подсчитывая, сколько раз код уже был интерпретирован, поскольку сама компиляция является сложным и трудоемким процессом. Поэтому полезно компилировать только те части, которые выполняются много раз.

а как читать коды операций и генерировать для них код?

Схема кодов операций определяется спецификацией ВМ, поэтому ВМ открывает файл программы и интерпретирует его в соответствии со спецификацией.

Нужно ли человеку иметь готовые сборочные блоки и копировать / пакетировать их вместе? сборка написана на C?

Этот процесс является деталью реализации виртуальной машины, обычно в нее встроен компилятор, который способен преобразовывать поток кода операции виртуальной машины в машинный код.

Как вы учитываете системные прерывания?

Очень просто: нет. Код в ВМ не может взаимодействовать с реальным оборудованием. ВМ взаимодействует с ОС и передает события ОС в код путем перехода / вызова определенных частей внутри интерпретируемого кода. Каждое событие в коде или из ОС должно проходить через ВМ.

Также продукты виртуализации оборудования могут использовать какой-то JIT. Типичные случаи использования в мире X86 - это перевод 16-битного кода реального режима в 32 или 64-битный код защищенного режима, чтобы не заставлять эмулировать процессор в реальном режиме. Кроме того, только программная виртуальная машина заменяет инструкции перехода в исполняемом коде переходами в управляющее программное обеспечение виртуальной машины, которое в каждой ветви просматривает следующий путь кода для сканирования инструкций перехода и их замены, прежде чем перейти к реальному назначению кода. Но я сомневаюсь, что замена перехода квалифицируется как компиляция JIT.

0 голосов
/ 15 апреля 2010

Вот объяснение того, как они выполняют динамическую перекомпиляцию для Rubyus-интерпретатора Rubinius:

http://www.engineyard.com/blog/2010/making-ruby-fast-the-rubinius-jit/

0 голосов
/ 12 апреля 2010

Виртуальная машина загружает «байт-код» или «промежуточный язык», а не машинный код, поэтому, я полагаю, она просто более эффективно перекомпилирует байт-код, как только у нее будет больше данных времени выполнения.

http://en.wikipedia.org/wiki/Just-in-time_compilation

0 голосов
/ 09 апреля 2010

IIS делает это путем теневого копирования: после компиляции он копирует сборки в какое-то временное место и запускает их из temp.

Представьте, что пользователь изменяет некоторые файлы. Затем IIS будет перекомпилировать сборки следующим образом:

  1. Перекомпилировать (все запросы обрабатываются старым кодом)
  2. Копирует новые сборки (все запросы обрабатываются старым кодом)
  3. Все новые запросы будут обрабатываться новым кодом, все запросы - старым.

Надеюсь, это будет полезно.

...