Bisakah kode mesin diterjemahkan ke arsitektur yang berbeda?

11

Jadi ini terkait dengan pertanyaan tentang menjalankan server Windows pada ARM . Jadi premis dari pertanyaan saya adalah, dapatkah kode mesin diterjemahkan dari satu arsitektur ke arsitektur lain untuk mengeksekusi biner pada arsitektur yang berbeda dari yang dikompilasi untuk dijalankan.

QEMU dan emulator lain dapat menerjemahkan instruksi dengan cepat, dan karenanya menjalankan executable di komputer yang tidak dikompilasi untuknya. Mengapa tidak melakukan terjemahan ini sebelumnya, alih-alih dengan cepat untuk mempercepat proses? Dari pengetahuan saya yang agak terbatas tentang perakitan, sebagian besar instruksi suka MOV, ADDdan yang lainnya harus portabel di seluruh arsitektur.

Apa pun yang tidak memiliki pemetaan langsung dapat dipetakan ke beberapa set instruksi lain, karena semua mesin Turing Lengkap. Apakah melakukan ini terlalu rumit? Apakah itu tidak bekerja sama sekali untuk beberapa alasan yang saya tidak kenal? Apakah ini akan berhasil, tetapi tidak menghasilkan hasil yang lebih baik daripada menggunakan emulator?

Kibbee
sumber
Teknik ini kemungkinan besar tidak disukai karena (selain sifatnya yang serpihan) tidak diperlukan banyak. Portabilitas / standardisasi (sedikit) lebih baik hari ini (jika hanya karena Wintel telah mengambil alih dunia), dan, di mana emulasi lintas-mesin sangat dibutuhkan (misalnya, untuk emulator telepon di lingkungan pengembangan aplikasi), emulasi langsung memberikan hasil yang lebih andal dan akurat. Plus, prosesor cukup cepat sehingga biaya emulasi tidak separah masalah di masa lalu.
Daniel R Hicks

Jawaban:

6

Jawaban singkatnya : Anda tidak dapat menerjemahkan executable yang dikompilasi dan dikompilasi. Meskipun secara teknis memungkinkan, sangat tidak mungkin untuk dicapai (lihat di bawah). Namun , jika Anda memiliki file sumber rakitan (berisi instruksi dan label), sangat mungkin dilakukan (walaupun jika Anda mendapatkan sumber rakitan, kecuali jika program tersebut ditulis dalam rakitan, Anda harus memiliki kode sumber program asli sebagai baik, jadi Anda sebaiknya mengkompilasinya untuk arsitektur yang berbeda untuk memulai).


Jawaban panjangnya :

QEMU dan emulator lain dapat menerjemahkan instruksi dengan cepat, dan karenanya menjalankan executable di komputer yang tidak dikompilasi untuknya. Mengapa tidak melakukan terjemahan ini sebelumnya, alih-alih dengan cepat untuk mempercepat proses?

Saya tahu prinsipnya mungkin tampak mudah, tetapi dalam praktiknya, hampir tidak mungkin karena beberapa alasan utama. Untuk memulai, set instruksi yang berbeda menggunakan mode pengalamatan yang sangat berbeda, struktur opcode yang berbeda, ukuran kata yang berbeda, dan beberapa bahkan tidak memiliki instruksi yang Anda butuhkan.

Katakanlah Anda perlu mengganti instruksi XYZdengan dua instruksi lagi, ABCdan DEF. Sekarang Anda telah secara efektif menggeser semua alamat relatif / offset di seluruh program sejak saat itu, jadi Anda perlu menganalisis dan menelusuri seluruh program dan memperbarui offset (baik sebelum dan sesudah perubahan). Sekarang, katakanlah salah satu dari perubahan offset secara signifikan - sekarang Anda perlu mengubah mode pengalamatan, yang mungkin mengubah ukuran alamat. Ini lagi akan memaksa Anda untuk memindai ulang seluruh file dan menghitung kembali semua alamat, dan seterusnya dan keempat.

Saat Anda menulis program perakitan, Anda mungkin menggunakan label, tetapi CPU tidak - ketika file dirakit, semua label dihitung sebagai lokasi relatif, absolut, atau offset. Anda dapat melihat mengapa ini dengan cepat menjadi tugas yang tidak sepele, dan hampir tidak mungkin. Mengganti instruksi tunggal mungkin mengharuskan Anda melewati seluruh program ratusan kali sebelum melanjutkan.

Dari pengetahuan perakitan yang agak terbatas, sebagian besar instruksi seperti MOV, ADD, dan lainnya harus portabel di seluruh arsitektur.

Ya, tetapi lihat masalah yang saya uraikan di atas. Bagaimana dengan ukuran kata mesin? Panjang alamat? Apakah ia bahkan memiliki mode pengalamatan yang sama? Sekali lagi, Anda tidak bisa hanya "menemukan dan mengganti" instruksi. Setiap segmen program memiliki alamat yang ditentukan secara khusus. Melompat ke label lain diganti dengan alamat memori literal atau offset ketika suatu program dirakit.

Apa pun yang tidak memiliki pemetaan langsung dapat dipetakan ke beberapa set instruksi lain, karena semua mesin Turing Lengkap. Apakah melakukan ini terlalu rumit? Apakah itu tidak bekerja sama sekali untuk beberapa alasan yang saya tidak kenal? Apakah ini akan berhasil, tetapi tidak menghasilkan hasil yang lebih baik daripada menggunakan emulator?

Anda 100% benar bahwa keduanya mungkin , dan akan jauh lebih cepat . Namun, menulis sebuah program untuk mencapai ini sangat sulit dan sangat tidak mungkin, jika tidak untuk apa pun kecuali masalah yang saya uraikan di atas.

Jika Anda memiliki kode sumber rakitan yang sebenarnya, akan mudah untuk menerjemahkan kode mesin ke arsitektur kumpulan instruksi lain. Namun, kode mesin sendiri dirakit , jadi tanpa sumber perakitan (yang berisi berbagai label yang digunakan untuk menghitung alamat memori), itu menjadi sangat sulit. Sekali lagi, mengubah satu instruksi dapat mengubah offset memori di seluruh program, dan membutuhkan ratusan lintasan untuk menghitung ulang alamat.

Melakukan ini untuk program dengan beberapa ribu instruksi akan membutuhkan puluhan bahkan ratusan ribu lintasan. Untuk program yang relatif kecil, ini dimungkinkan, tetapi ingat bahwa jumlah lintasan akan meningkat secara eksponensial dengan jumlah instruksi mesin dalam program. Untuk setiap program dengan ukuran yang cukup layak, hampir tidak mungkin.

Penerobosan
sumber
Pada dasarnya apa yang harus dilakukan adalah "mendekompilasi" atau "membongkar" kode objek sumber. Untuk kode yang relatif lurus (terutama kode yang dihasilkan oleh kompiler tertentu atau paket pembuatan kode di mana ada "gaya" yang dikenal) penyisipan ulang label dan sejenisnya cukup sederhana. Namun, tentu saja, kompiler baru yang sangat mengoptimalkan akan menghasilkan kode yang jauh lebih sulit untuk "grock" dengan cara ini.
Daniel R Hicks
@ Tidak Jika Anda memiliki kode objek sumber, Anda cukup banyak memiliki sumber perakitan ( bukan kode mesin). File objek berisi urutan kode mesin yang dinamai (baca: berlabel) untuk dihubungkan. Masalahnya muncul ketika Anda menautkan file kode objek ke file yang dapat dieksekusi. Segmen yang lebih kecil ini dapat ditangani (atau direkayasa balik) jauh lebih mudah daripada seluruh eksekusi yang terhubung.
Terobosan
Tentu saja, format file objek tertentu membuat pekerjaan sedikit lebih mudah. Beberapa bahkan mungkin berisi info debug, memungkinkan Anda untuk mengembalikan sebagian besar label. Lainnya kurang membantu. Dalam beberapa kasus banyak info ini disimpan bahkan dalam format file tertaut, dalam kasus lain tidak. Ada banyak sekali format file yang berbeda.
Daniel R Hicks
2

Ya, apa yang Anda sarankan dapat dan telah dilakukan. Ini tidak terlalu umum, dan saya tidak tahu ada sistem saat ini yang menggunakan teknik ini, tapi itu pasti baik dalam bidang kelayakan teknis.

Dulu banyak dilakukan untuk memungkinkan porting kode dari satu sistem ke sistem lain, sebelum ada orang yang mencapai "portabilitas" mentah yang kita miliki sekarang. Itu membutuhkan analisis kompleks dari "sumber" dan dapat dihalangi oleh modifikasi kode dan praktik aneh lainnya, tetapi itu masih dilakukan.

Baru-baru ini, sistem seperti IBM System / 38 - iSeries - System i telah memanfaatkan portabilitas dari kode perantara (mirip dengan bytecodes Java) yang disimpan dengan program yang dikompilasi untuk memungkinkan portabilitas antara arsitektur kumpulan instruksi yang tidak kompatibel.

Daniel R Hicks
sumber
Setuju bahwa ini telah dilakukan, biasanya dengan set instruksi yang jauh lebih tua (lebih sederhana). Ada proyek IBM pada 1970-an untuk mengubah program biner 7xx lama ke System / 360.
serbuk gergaji
1

Kode mesin itu sendiri adalah spesifik arsitektur.

Bahasa yang memungkinkan portabilitas mudah di berbagai arsitektur (Java mungkin yang paling terkenal) cenderung tingkat yang sangat tinggi, membutuhkan penerjemah atau kerangka kerja yang harus dipasang pada mesin agar mereka dapat bekerja.

Kerangka kerja atau juru bahasa ini ditulis untuk setiap arsitektur sistem khusus yang akan mereka jalankan dan tidak, dalam dan dari diri mereka sendiri, lebih portabel daripada program "normal".

music2myear
sumber
2
Bahasa yang dikompilasi juga portabel, bukan hanya bahasa yang ditafsirkan, itu adalah kompiler yang spesifik arsitektur karena itulah yang akhirnya menerjemahkan kode untuk apa platform itu pada dapat mengenali. Satu-satunya perbedaan adalah bahwa bahasa yang dikompilasi diterjemahkan pada waktu kompilasi dan bahasa yang ditafsirkan diterjemahkan baris demi baris sesuai kebutuhan.
MaQleod
1

Tentu saja, itu mungkin. Apa itu kode mesin? Itu hanya bahasabahwa komputer tertentu mengerti. Pikirkan diri Anda sebagai komputer dan Anda mencoba memahami buku yang ditulis dalam bahasa Jerman. Anda tidak dapat melakukannya, karena Anda tidak mengerti bahasa. Sekarang jika Anda mengambil kamus bahasa Jerman dan mencari kata "Kopf", Anda akan melihatnya menerjemahkannya ke kata "head" dalam bahasa Inggris. Kamus yang Anda gunakan adalah apa yang disebut lapisan emulasi di dunia komputer. Mudah bukan? Nah, itu semakin sulit. Ambil kata Jerman "Schadenfruede," dan terjemahkan ke bahasa Inggris. Anda akan melihat tidak ada kata dalam bahasa Inggris, tetapi ada definisi. Masalah yang sama ada di dunia komputer, menerjemahkan hal-hal yang tidak memiliki kata yang setara. Hal ini membuat port langsung menjadi sulit karena pengembang dari lapisan emulasi harus membuat interpretasi tentang apa arti kata itu dan membuat komputer host memahaminya. Kadang-kadang itu tidak bekerja seperti yang diharapkan. Kita semua pernah melihat terjemahan buku, frasa, dll yang lucu di internet, kan?

Keltari
sumber
1

Proses yang Anda gambarkan disebut Static Recompilation, dan sudah dilakukan, hanya saja tidak dengan cara yang berlaku umum. Berarti itu tidak mungkin, sudah dilakukan berkali-kali, tetapi itu membutuhkan pekerjaan manual.

Ada banyak contoh sejarah yang layak untuk diteliti, tetapi mereka kurang mampu menunjukkan kepedulian modern. Saya telah menemukan dua contoh yang pada dasarnya harus membuat skeptis lengkap mempertanyakan orang-orang yang mengklaim segalanya sulit adalah mustahil.

Pertama, orang ini membuat Platform DAN Platform Statis untuk NES ROM. http://andrewkelley.me/post/jamulator.html

Dia membuat beberapa poin yang sangat bagus, tetapi menyimpulkan bahwa JIT masih lebih praktis. Saya sebenarnya tidak yakin mengapa dia belum tahu bahwa untuk situasi ini, ini mungkin jenis situasi yang dipertimbangkan kebanyakan orang. Tidak mengambil jalan pintas, menuntut akurasi siklus penuh, dan pada dasarnya tidak menggunakan ABI sama sekali. Jika hanya itu yang ada, kita bisa membuang konsep itu ke tempat sampah dan menyebutnya sehari, tapi tidak semuanya dan tidak pernah ada .... Bagaimana kita tahu ini? Karena semua proyek yang berhasil tidak menggunakan pendekatan ini.

Sekarang untuk kemungkinan yang kurang jelas, Leverage platform yang sudah Anda miliki ... Starcraft pada Linux ARM handheld? Yup, pendekatan ini bekerja ketika Anda tidak membatasi tugas untuk apa yang Anda lakukan secara dinamis. Dengan menggunakan Winlib, panggilan platform Windows semuanya asli, yang perlu kita khawatirkan adalah Arsitektur.

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

Saya akan melemparkan dolar kepada donat bahwa pelambatannya hampir dapat diabaikan, mengingat pandora genggam ARM hanya sedikit lebih kuat daripada Pi. Alat-alat yang dia gunakan ada di repositori ini.

https://github.com/notaz/ia32rtools

Orang itu terurai sangat manual, saya percaya proses itu dapat diotomatisasi secara signifikan dengan lebih sedikit pekerjaan ... tetapi masih tenaga kerja saat ini. Jangan biarkan siapa pun memberi tahu Anda sesuatu yang tidak mungkin, bahkan jangan biarkan saya memberi tahu Anda itu tidak praktis ... Itu bisa praktis, segera setelah Anda berinovasi cara baru untuk membuatnya begitu.

JM Becker
sumber
0

Secara teoritis, ya ini bisa dilakukan. Masalah yang lebih besar yang muncul adalah menerjemahkan aplikasi untuk satu sistem operasi (atau kernel) ke yang lain. Ada perbedaan signifikan antara operasi tingkat rendah Windows, Linux, OSX dan iOS, yang harus digunakan semua aplikasi untuk perangkat tersebut.

Sekali lagi, secara teoritis, seseorang dapat menulis aplikasi yang dapat menguraikan aplikasi serta semua kode mesin yang terkait dengan sistem operasi yang dikompilasikan untuk dijalankan dan kemudian mengkompilasi ulang semua kode mesin itu untuk perangkat lain. Namun, itu akan sangat ilegal di hampir setiap kasus dan akan sangat sulit untuk ditulis. Faktanya, roda gigi di kepalaku mulai menangkap hanya memikirkannya.

MEMPERBARUI

Beberapa komentar di bawah ini tampaknya tidak setuju dengan tanggapan saya, namun, saya pikir mereka kehilangan maksud saya. Sepengetahuan saya, tidak ada aplikasi yang dapat mengambil urutan byte yang dapat dieksekusi untuk satu arsitektur, mendekomposisinya pada tingkat bytecode, termasuk semua panggilan yang diperlukan ke perpustakaan eksternal termasuk panggilan ke kernel OS yang mendasarinya dan berkumpul kembali untuk sistem lain dan menyimpan dihasilkan bytecode dieksekusi . Dengan kata lain, tidak ada aplikasi yang dapat mengambil sesuatu yang sesederhana Notepad.exe, menguraikan file 190k yang kecil itu, dan 100% menyusunnya kembali menjadi aplikasi yang dapat berjalan di Linux atau OSX.

Ini adalah pemahaman saya bahwa penanya pertanyaan ingin tahu bahwa jika kita dapat melakukan virtualisasi perangkat lunak atau menjalankan aplikasi melalui program seperti Wine atau Parallels, mengapa kita tidak dapat dengan mudah menerjemahkan kembali kode byte untuk sistem yang berbeda. Alasannya adalah jika Anda ingin merakit kembali sepenuhnya aplikasi untuk arsitektur lain, Anda harus menguraikan semua kode byte yang diperlukan untuk menjalankannya sebelum memasang kembali. Ada lebih banyak untuk setiap aplikasi daripada hanya file exe, katakanlah, untuk mesin Windows. Semua aplikasi Windows menggunakan objek dan fungsi kernel Windows tingkat rendah untuk membuat menu, area teks, metode untuk mengubah ukuran jendela, menggambar ke tampilan, mengirim / menerima pesan OS, dan seterusnya dan seterusnya ...

Semua byte-code harus dibongkar jika Anda ingin memasang kembali aplikasi dan menjalankannya pada arsitektur yang berbeda.

Aplikasi seperti Wine menafsirkan binari Windows pada tingkat byte. Mereka mengenali panggilan ke kernel dan menerjemahkan panggilan itu ke fungsi Linux terkait atau meniru lingkungan Windows. Tapi, itu bukan terjemahan byte-untuk-byte (atau opcode untuk opcode). Ini lebih dari terjemahan fungsi-untuk-fungsi dan itu sedikit berbeda.

RLH
sumber
Itu sama sekali bukan teori. Dan ada banyak aplikasi yang menjalankan binari lain pada sistem operasi yang berbeda. Pernahkah Anda mendengar tentang Wine? Ini menjalankan binari Windows pada OS yang berbeda, seperti Linux, Solaris, Mac OSX, BSD, dan lainnya.
Keltari
Perbedaan dalam sistem operasi dapat dengan mudah dilakukan pada kebanyakan sistem dengan menggunakan hypervisor untuk menjalankan beberapa sistem operasi (atau untuk menjalankan "lapisan" seperti Wine pada satu sistem yang meniru yang lain). AFAIK, semua prosesor non-embedded "modern" adalah "virtual", jadi ini tidak memerlukan emulasi / terjemahan set instruksi.
Daniel R Hicks
0

Tampaknya semua ahli kehilangan poin ini: 'Terjemahan' itu rumit tetapi sangat cocok untuk komputer (tidak cerdas, hanya susah payah). Tetapi setelah terjemahan, program membutuhkan dukungan OS, mis: GetWindowVersion tidak ada di Linux. Ini biasanya disediakan oleh emulator (sangat besar). Jadi, Anda bisa 'pra-menerjemahkan' program sederhana tetapi Anda harus menautkan ke perpustakaan besar untuk berjalan secara mandiri. Pencitraan setiap program windows dilengkapi dengan kernel.dll + user.dll + shell.dll ...

qak
sumber
Itu tidak hanya melelahkan, itu membutuhkan kecerdasan. Misalnya, Anda melihat beberapa perhitungan yang hasilnya menentukan alamat yang Anda lompati, yang mungkin berada di tengah-tengah sesuatu yang tampaknya merupakan satu instruksi.
David Schwartz