11

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

QEMU и другие эмуляторы могут переводить инструкции на лету и, следовательно, запускать исполняемый файл на компьютере, для которого он не был скомпилирован. Почему бы не сделать этот перевод заранее, а не на лету, чтобы ускорить процесс? Исходя из моего немного ограниченного знания сборки, большинство инструкций, таких как MOV , ADD и другие, должны переноситься на другие архитектуры.

Все, что не имеет прямого сопоставления, может быть сопоставлено с каким-то другим набором инструкций, так как все машины являются Turing Complete. Будет ли это слишком сложно? Разве это не сработает вообще по какой-то причине, с которой я не знаком? Будет ли это работать, но не даст лучших результатов, чем использование эмулятора?

7 ответов7

6

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


Длинный ответ:

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

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

Допустим, вам нужно было заменить инструкцию XYZ двумя инструкциями, ABC и DEF . Теперь вы эффективно сместили все относительные / смещенные адреса во всей программе с этого момента, поэтому вам нужно будет проанализировать и пройти всю программу и обновить смещения (как до, так и после изменения). Теперь, скажем, одно из смещений существенно меняется - теперь вам нужно изменить режимы адресации, которые могут изменить размер адреса. Это снова заставит вас пересмотреть весь файл и пересчитать все адреса, и так далее, и так далее.

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

Исходя из моего немного ограниченного знания сборки, большинство инструкций, таких как MOV, ADD и другие, должны переноситься на другие архитектуры.

Да, но посмотрите на вопросы, которые я изложил выше. Как насчет размера слова машины? Длина адреса? У него вообще есть одинаковые режимы адресации? Опять же, вы не можете просто "найти и заменить" инструкции. Каждый сегмент программы имеет определенный адрес. Переходы к другим меткам заменяются литеральными или смещенными адресами памяти при сборке программы.

Все, что не имеет прямого сопоставления, может быть сопоставлено с каким-то другим набором инструкций, так как все машины являются Turing Complete. Будет ли это слишком сложно? Разве это не сработает вообще по какой-то причине, с которой я не знаком? Будет ли это работать, но не даст лучших результатов, чем использование эмулятора?

Вы на 100% правы, что это возможно и будет намного быстрее. Тем не менее, написание программы для достижения этой цели невероятно сложно и невероятно, если не для чего-либо, кроме вопросов, которые я изложил выше.

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

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

2

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

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

В последнее время такие системы, как IBM System/38 - iSeries - System i, воспользовались преимуществами переносимости промежуточного кода (аналогичного байт-кодам Java), хранимого в скомпилированных программах, для обеспечения переносимости между несовместимыми архитектурами набора команд.

1

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

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

Сначала этот парень сделал полную статическую архетектуру и платформу для NES ROM. http://andrewkelley.me/post/jamulator.html

Он делает несколько очень хороших замечаний, но приходит к выводу, что JIT все еще более практичен. Я на самом деле не уверен, почему он еще не знал, что для этой ситуации, это может быть тип ситуации, которую большинство людей рассматривают. Не требуя ярлыков, требуя точности полного цикла и практически не используя ABI. Если бы это было все, что было, мы могли бы выбросить концепцию в мусорную корзину и назвать это днем, но это не все и никогда не было .... Откуда нам это знать? Потому что все успешные проекты не использовали этот подход.

Теперь, когда возможности менее очевидны, используйте уже имеющуюся платформу ... Starcraft на Linux ARM ручной? Да, подход работает, когда вы не ограничиваете задачу именно тем, что делаете динамически. При использовании Winlib все вызовы платформы Windows являются родными, и все, о чем мы должны беспокоиться - это архитектура.

http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/

Я бы бросил доллары в пончики, что замедление практически ничтожно, учитывая, что ручной пандора ARM лишь немного сильнее, чем Pi. Инструменты, которые он использовал, находятся в этом хранилище.

https://github.com/notaz/ia32rtools

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

1

Сам машинный код зависит от архитектуры.

Языки, обеспечивающие легкую переносимость между несколькими архитектурами (вероятно, наиболее известной из них является Java), как правило, имеют очень высокий уровень, требующий установки интерпретаторов или структур на компьютере для их работы.

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

1

Абсолютно, это возможно. Что такое машинный код? Это просто язык, который понимает конкретный компьютер. Думайте о себе как о компьютере, и вы пытаетесь понять книгу, написанную на немецком языке. Вы не можете сделать это, потому что вы не понимаете язык. Теперь, если бы вы взяли словарь немецкого языка и посмотрели слово "Kopf", вы бы увидели, что оно переводится на английское слово "head". Используемый вами словарь называется эмуляционным слоем в компьютерном мире. Легко ли? Ну, это становится сложнее. Возьмите немецкое слово "Schadenfruede" и переведите его на английский. Вы увидите, что в английском языке нет слова, но есть определение. Та же проблема существует в компьютерном мире, переводя вещи, которые не имеют эквивалентного слова. Это затрудняет прямые порты, так как разработчикам уровня эмуляции приходится интерпретировать, что означает это слово, и заставить главный компьютер его понимать. Иногда это не работает так, как можно было бы ожидать. Мы все видели забавные переводы книг, фраз и т.д. В Интернете, верно?

0

Кажется, что все эксперты упускают этот момент: «перевод» сложен, но очень подходит для компьютера (не умный, просто трудолюбивый). Но после перевода программы нуждаются в поддержке ОС, например: GetWindowVersion не существует в Linux. Обычно это обеспечивается эмулятором (очень большой). Таким образом, вы могли бы «предварительно перевести» простые программы, но вам нужно создать ссылку на огромную библиотеку, чтобы работать независимо. Imaging каждой программы Windows поставляется с собственным kernel.dll+user.dll+shell.dll ...

0

Теоретически, да, это может быть сделано. Большая проблема, которая входит в игру, - это перевод приложения для одной операционной системы (или ядра) в другую. Существуют значительные различия между операциями низкого уровня в ядрах Windows, Linux, OSX и iOS, которые должны использоваться всеми приложениями для этих устройств.

Еще раз, теоретически, можно написать приложение, которое могло бы разложить приложение, а также весь машинный код, связанный с операционной системой, для которой оно было скомпилировано, и затем перекомпилировать весь этот машинный код для другого устройства. Тем не менее, это было бы крайне незаконно почти в каждом случае и было бы чрезвычайно трудно написать. Фактически, шестеренки в моей голове начинают заедать, просто думая об этом.

ОБНОВИТЬ

Несколько комментариев ниже, кажется, не согласны с моим ответом, однако, я думаю, что они упускают мою точку зрения. Насколько мне известно, нет приложения, которое может взять последовательность исполняемых байтов для одной архитектуры, разложить ее на уровне байт-кода, включая все необходимые вызовы внешних библиотек, включая вызовы ядра базовой ОС, и собрать его для другой системы и сохранить результирующий исполняемый байт-код. Другими словами, нет приложения, которое могло бы взять что-то столь же простое, как Notepad.exe, разложить небольшой файл размером 190 КБ и на 100% собрать его в приложение, которое могло бы работать в Linux или OSX.

Насколько я понимаю, задающий вопрос хотел знать, что если мы можем виртуализировать программное обеспечение или запускать приложения с помощью таких программ, как Wine или Parallels, то почему мы не можем просто повторно перевести байт-код для разных систем. Причина в том, что если вы хотите полностью пересобрать приложение для другой архитектуры, вы должны разложить весь байт-код, необходимый для его запуска, перед повторной сборкой. В каждом приложении есть нечто большее, чем просто исполняемый файл, скажем, для Windows-машины. Все приложения Windows используют низкоуровневые объекты и функции ядра Windows для создания меню, текстовых областей, методов изменения размера окна, рисования на дисплее, отправки / получения сообщений ОС и т.д. И т.д. И т.д.

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

Такие приложения, как Wine, интерпретируют двоичные файлы Windows на уровне байтов. Они распознают вызовы ядра и переводят эти вызовы либо в связанные функции Linux, либо эмулируют среду Windows. Но это не байт-байт (или код операции для кода операции) ретрансляции. Это скорее перевод из функции в функцию, и это немного отличается.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .