Чи можна машинний код перекласти в іншу архітектуру?


11

Отже, це свого роду пов'язане з питанням про запуск Windows-сервера на ARM . Отже, передумова мого питання полягає в тому, чи можна машинний код переводити з однієї архітектури в іншу , щоб виконати бінарний архітектуру, відмінну від тієї, на якій він був складений.

QEMU та інші емулятори можуть перекладати інструкції на ходу, а тому запускати виконуваний файл на комп'ютері, для якого він не був складений. Чому б не зробити цей переклад достроково, а не на ходу, щоб прискорити процес? З моїх дещо обмежених знань про складання, більшість інструкцій на кшталт MOV, ADDа інші повинні бути переносними в архітектурах.

Все, що не має прямого відображення, може бути відображене в якомусь іншому наборі інструкцій, оскільки всі машини Turing Complete. Це було б занадто складно? Хіба це взагалі не вийшло з якихось незнайомих мені причин? Це буде працювати, але не дасть кращих результатів, ніж використання емулятора?


Ця техніка, ймовірно, впала в недоброзичливість, оскільки (окрім своєї крихкості) вона не дуже потрібна. Переносність / стандартизація (дещо) краща в ці дні (хоча б тому, що Wintel перейняв світ), і там, де насправді потрібна перехресна емуляція (наприклад, для емулятора телефону в середовищі розробки додатків), пряма емуляція забезпечує більш надійний і точний результат. Плюс, процесори досить швидкі, що вартість емуляції не є настільки серйозною проблемою, як раніше.
Daniel R Hicks

Відповіді:


6

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


Довга відповідь :

QEMU та інші емулятори можуть перекладати інструкції на ходу, а тому запускати виконуваний файл на комп'ютері, для якого він не був складений. Чому б не зробити цей переклад достроково, а не на ходу, щоб прискорити процес?

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

Скажімо, вам потрібно було замінити інструкцію ще XYZдвома інструкціями ABCта DEF. Тепер ви фактично змістили всі відносні / зміщені адреси у всій програмі з цього моменту, тому вам потрібно буде проаналізувати та пройти всю програму та оновити компенсації (як до, так і після зміни). Скажімо, один із зрушень суттєво зміниться - тепер вам потрібно змінити режими адресації, які можуть змінити розмір адреси. Це знову змусить вас повторно сканувати весь файл і перерахувати всі адреси тощо, і так далі, і четверте.

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

З моїх дещо обмежених знань про складання, більшість інструкцій, таких як MOV, ADD та інші, повинні бути переносними в архітектурах.

Так, але подивіться на проблеми, які я окреслив вище. А як щодо розміру слова машини? Довжина адреси? Чи навіть у нього є однакові режими адресації? Знову ж таки, ви не можете просто «знайти та замінити» інструкції. Кожен сегмент програми має конкретно визначену адресу. При зібранні програми переходи до інших міток замінюються буквальними або зміщеними адресами пам'яті.

Все, що не має прямого відображення, може бути відображене в якомусь іншому наборі інструкцій, оскільки всі машини Turing Complete. Це було б занадто складно? Хіба це взагалі не вийшло з якихось незнайомих мені причин? Це буде працювати, але не дасть кращих результатів, ніж використання емулятора?

Ви на 100% вірні, що це і можливо , і було б набагато швидше . Однак написати програму для її виконання надзвичайно складно і вкрай малоймовірно, якщо не для чого, крім тих питань, які я окреслив вище.

Якщо у вас був фактичний вихідний код збірки, було б тривіально перекласти машинний код в іншу архітектуру набору інструкцій. Однак сам машинний код зібраний , тому без джерела складання (який містить різні мітки, які використовуються для обчислення адрес пам'яті), стає неймовірно складно. Знову ж таки, зміна однієї інструкції може змінити зсуви пам’яті у всій програмі та вимагати сотні пропусків для повторного обчислення адрес.

Для виконання програми з кількома тисячами інструкцій потрібно десятки, якщо не сотні тисяч пропусків. Для відносно невеликих програм це може бути можливим, але пам’ятайте, що кількість пропусків буде експоненціально збільшуватися із кількістю машинних інструкцій у програмі. Для будь-якої програми досить гідного розміру це майже неможливо.


По суті, потрібно зробити «декомпілювати» або «розібрати» вихідний об'єктний код. Для відносно прямого коду (особливо коду, згенерованого певними компіляторами або пакетами генерації коду, де є відомий "стиль"), повторне вставлення міток тощо може бути досить простим. Безумовно, проте новіші високооптимізаційні компілятори генеруватимуть код, який набагато важче "обмацати" таким чином.
Даніель Р Хікс

@DanH, якщо у вас є вихідний об'єктний код, у вас майже є джерело збірки ( не машинний код). Об'єктний файл містить іменовані (прочитані: мічені) послідовності машинного коду, які повинні бути пов'язані між собою. Проблема виникає, коли ви зв'язуєте файли об'єктного коду у виконуваний файл. Цими меншими сегментами можна обробляти (або рееструвати назад) набагато простіше, ніж цілий пов'язаний виконуваний файл.
Прорив

Звичайно, певні формати файлів об'єктів полегшують роботу. Деякі можуть навіть містити інформацію про налагодження, що дозволяє відновити більшість міток. Інші менш корисні. У деяких випадках значна частина цієї інформації зберігається навіть у зв'язаному форматі файлу, в інших випадках - ні. Існує величезна кількість різних форматів файлів.
Даніель Р Хікс

2

Так, те, що ви пропонуєте, може бути і було зроблено. Це не надто часто, і я не знаю жодної сучасної системи, яка використовує цю техніку, але це, безумовно, добре в межах технічної можливості.

Раніше було зроблено багато для того, щоб дозволити перенесення коду з однієї системи в іншу, перш ніж хто-небудь домігся навіть грубої «портативності», яку ми маємо зараз. Це вимагало складного аналізу "джерела" і його можна було стримувати за допомогою модифікації коду та інших дивних методів, але це все-таки робилося.

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


Погодьтеся, що це було зроблено, як правило, із значно старшими (простішими) наборами інструкцій. У 1970-х роках був проект IBM з перетворення старих двійкових програм 7xx в System / 360.
тирса

1

Сам машинний код залежить від архітектури.

Мови, що дозволяють легко переносити в декількох архітектурах (Java, мабуть, найбільш відома), як правило, є дуже високим рівнем, що вимагає, щоб інтерпретатори або рамки були встановлені на машині для того, щоб вони працювали.

Ці рамки або інтерпретатори написані для кожної конкретної архітектури системи, на якій вони будуть працювати, і самі по собі не є портативнішими, ніж "звичайна" програма.


2
Мови компіляції теж є портативними, а не просто інтерпретованими мовами, саме компілятор є специфічним для архітектури, оскільки саме він в кінцевому підсумку переводить код на те, що платформа, на якій він може розпізнати. Єдина відмінність полягає в тому, що компільовані мови перекладаються під час компіляції, а інтерпретовані мови перекладаються по рядках за потребою.
MaQleod

1

Абсолютно, можливо. Що таке машинний код? Це просто моващо конкретний комп'ютер розуміє. Подумайте про себе як про комп'ютер, і ви намагаєтесь зрозуміти книгу, написану німецькою мовою. Ви не можете це зробити, тому що не розумієте мови. Тепер, якби ви взяли німецький словник і шукали слово "Kopf", ви б побачили, що це перекладається на англійське слово "head". Словник, який ви використовували, називається шаром емуляції в комп'ютерному світі. Легко так? Що ж, стає складніше. Візьміть німецьке слово "Schadenfruede" і перекладіть його на англійську мову. Ви побачите, що в англійській мові немає жодного слова, але є визначення. Ця ж проблема існує і в комп'ютерному світі, перекладаючи речі, які не мають рівнозначного слова. Це ускладнює прямі порти, оскільки розробникам рівня емуляції доводиться тлумачити, що означає це слово, і змусити хазяйський комп'ютер зрозуміти це. Іноді це просто не працює так, як можна було б очікувати. Ми всі бачили смішні переклади книг, фраз тощо в Інтернеті?


1

Процес, який ви описуєте, називається статичною рекомпіляцією, і це було зроблено, тільки не загальноприйнятним способом. Це означає, що це неможливо, це робилося багато разів, але це вимагало ручної роботи.

Існує багато історичних прикладів, які варто вивчити, але вони менш здатні продемонструвати сучасні проблеми. Я знайшов два приклади, які по суті повинні змусити будь-яких повних скептиків поставити під сумнів людей, які стверджують, що все важко, неможливо.

По-перше, цей хлопець зробив повну статичну архітектуру І платформу для NES ROM. http://andrewkelley.me/post/jamulator.html

Він робить деякі дуже хороші моменти, але робить висновок, що JIT все-таки більш практичний. Я насправді не впевнений, чому він ще не знав, що для цієї ситуації це може бути саме такий тип ситуації, який вважають люди. Не маючи ярликів, не вимагаючи повної точності циклу, а по суті не використовуючи ABI. Якби це все було, ми могли б викинути концепцію в смітник і назвати це на день, але це ще не все і ніколи не було .... Як ми це знаємо? Тому що всі успішні проекти не використовували такий підхід.

Тепер для можливостей менш очевидних, використовуйте платформу, яку ви вже маєте ... Starcraft на портативному ПК ARM Linux? Так, підхід працює, коли ви не обмежуєте завдання саме тим, що ви робили б динамічно. За допомогою Winlib всі дзвінки на платформу Windows є рідними, все, про що ми повинні турбуватися, - це архітектура.

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

Я б кинув долари на пончики, що уповільнення майже незначне, враховуючи, що ручна пандора ARM лише трохи сильніша за Pi. Інструменти, які він використовував, є у цьому сховищі.

https://github.com/notaz/ia32rtools

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


0

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

Ще раз теоретично можна було написати програму, яка могла б розкласти програму, а також увесь машинний код, пов'язаний з операційною системою, з якої вона була складена, а потім перекомпілювати весь цей машинний код для іншого пристрою. Однак це було б вкрай незаконно у кожному конкретному випадку і було б надзвичайно складно написати. Фактично, шестерні в моїй голові починають захоплюватись, просто думаючи про це.

ОНОВЛЕННЯ

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

Наскільки я розумію, що запитуючий питання хотів знати, що якщо ми можемо віртуалізувати програмне забезпечення або запускати програми через такі програми, як Wine або Parallels, то чому ми не можемо просто перекласти байт-код для різних систем. Причина полягає в тому, що якщо ви хочете повністю зібрати додаток для іншої архітектури, ви повинні розкласти весь байт-код, необхідний для його запуску, перш ніж його скомпонувати. Для кожної програми є більше, ніж лише файл exe, скажімо, для машини Windows. Усі програми Windows використовують об'єкти та функції ядра Windows низького рівня для створення меню, текстових областей, методів зміни розміру вікон, малювання на дисплей, надсилання / отримання повідомлень ОС тощо і так далі ...

Весь цей байт-код потрібно розібрати, якщо ви хочете зібрати до програми та змусити його працювати на іншій архітектурі.

Такі програми, як Wine, інтерпретують бінарні файли Windows на рівні байтів. Вони розпізнають виклики до ядра та переводять ці виклики у відповідні функції Linux або ж імітують середовище Windows. Але це не байт за байтом (або опкод для перекодування). Це більше переклад від функції до функції, і це зовсім інше.


Це зовсім не теоретично. І є безліч додатків, які запускають інші бінарні файли в різних операційних системах. Ви чули про Вино? Він запускає бінарні файли Windows на різних ОС, таких як Linux, Solaris, Mac OSX, BSD та інші.
Келтарі

Різниця в операційних системах може бути легко виправлена ​​в більшості систем за допомогою гіпервізора для запуску декількох операційних систем (або для запуску "шару", такого як Wine в одній системі, що емулює іншу). AFAIK, усі "сучасні" невбудовані процесори є "віртуалізаційними", тому для цього не потрібна емуляція / переклад набору інструкцій.
Daniel R Hicks

0

Здається, всі експерти не вистачають цього пункту: "Переклад" є складним, але дуже підходить для комп'ютера (не розумний, просто працездатний). Але після перекладу програма потребує підтримки ОС, напр .: GetWindowVersion не існує в Linux. Зазвичай це постачається емулятором (дуже великим). Таким чином, ви можете "перекласти" прості програми, але вам доведеться зв’язатися з величезною бібліотекою, щоб працювати незалежно. Зображення всіх програм Windows поставляється зі своїм власним kernel.dll + user.dll + shell.dll ...


Це не просто трудомістко, воно вимагає інтелекту. Наприклад, скажіть, ви бачите деякі обчислення, результат яких визначає адресу, на яку ви переходите, і може бути посеред того, що, схоже, є однією інструкцією.
Девід Шварц
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.