Tujuan saya adalah untuk dapat mengembangkan Linux tertanam. Saya memiliki pengalaman pada sistem tertanam bare-metal menggunakan ARM.
Saya punya beberapa pertanyaan umum tentang pengembangan untuk target cpu yang berbeda. Pertanyaan saya adalah sebagai berikut:
Jika saya memiliki aplikasi yang dikompilasi untuk dijalankan pada ' target x86, linux OS versi xyz ', dapatkah saya menjalankan biner yang dikompilasi yang sama pada sistem lain ' target ARM, OS linux versi xyz '?
Jika di atas tidak benar, satu-satunya cara adalah mendapatkan kode sumber aplikasi untuk membangun kembali / mengkompilasi ulang menggunakan toolchain yang relevan 'misalnya, arm-linux-gnueabi'?
Demikian pula, jika saya memiliki modul kernel (driver perangkat) yang dapat dimuat yang bekerja pada ' target x86, OS linux versi xyz ', dapatkah saya memuat / menggunakan .ko yang dikompilasi sama pada sistem lain ' target ARM, OS Linux versi xyz ' ?
Jika di atas tidak benar, satu-satunya cara adalah mendapatkan kode sumber driver untuk membangun kembali / mengkompilasi ulang menggunakan toolchain yang relevan 'misalnya, arm-linux-gnueabi'?
Jawaban:
Tidak. Binari harus dikompilasi ulang untuk arsitektur target, dan Linux tidak menawarkan apa pun seperti biner gemuk . Alasannya adalah karena kode tersebut dikompilasi ke kode mesin untuk arsitektur tertentu, dan kode mesin sangat berbeda antara sebagian besar keluarga prosesor (ARM dan x86 misalnya sangat berbeda).
EDIT: perlu dicatat bahwa beberapa arsitektur menawarkan tingkat kompatibilitas ke belakang (dan bahkan lebih jarang, kompatibilitas dengan arsitektur lain); pada CPU 64-bit, umumnya memiliki kompatibilitas mundur ke edisi 32-bit (tapi ingat: pustaka dependen Anda juga harus 32-bit, termasuk pustaka standar C Anda, kecuali jika Anda terhubung secara statis ). Yang juga perlu disebutkan adalah Itanium , di mana dimungkinkan untuk menjalankan kode x86 (hanya 32-bit), meskipun sangat lambat; kecepatan eksekusi kode x86 yang buruk setidaknya merupakan bagian dari alasan itu tidak terlalu sukses di pasar.
Ingatlah bahwa Anda masih tidak dapat menggunakan binari yang dikompilasi dengan instruksi yang lebih baru pada CPU lama, bahkan dalam mode kompatibilitas (misalnya, Anda tidak dapat menggunakan AVX dalam biner 32-bit pada prosesor Nehalem x86 ; CPU hanya tidak mendukungnya.
Perhatikan bahwa modul kernel harus dikompilasi untuk arsitektur yang relevan; selain itu, modul kernel 32-bit tidak akan berfungsi pada kernel 64-bit atau sebaliknya.
Untuk informasi tentang binari lintas-kompilasi (sehingga Anda tidak harus memiliki toolchain pada perangkat ARM target), lihat jawaban komprehensif grochmal di bawah ini.
sumber
Elizabeth Myers benar, setiap arsitektur memerlukan biner yang dikompilasi untuk arsitektur yang dimaksud. Untuk membangun binari untuk arsitektur yang berbeda dari yang dijalankan oleh sistem Anda, Anda memerlukan
cross-compiler
.Dalam kebanyakan kasus, Anda perlu mengkompilasi cross compiler. Saya hanya punya pengalaman dengan
gcc
(tapi saya percaya itullvm
, dan kompiler lain, memiliki parameter yang sama). Sebuahgcc
cross-compiler dicapai dengan menambahkan--target
ke configure:Anda perlu mengkompilasi
gcc
,glibc
danbinutils
dengan parameter-parameter ini (dan berikan header kernel dari kernel pada mesin target).Dalam prakteknya ini jauh lebih rumit dan kesalahan pembangunan yang berbeda muncul pada sistem yang berbeda.
Ada beberapa panduan di luar sana tentang cara mengkompilasi GNU toolchain tetapi saya akan merekomendasikan Linux From Scratch , yang terus dipelihara dan melakukan pekerjaan yang sangat baik dalam menjelaskan apa yang dilakukan perintah yang disajikan.
Pilihan lain adalah kompilasi bootstrap dari cross-compiler. Berkat perjuangan menyusun kompiler silang untuk berbagai arsitektur pada arsitektur yang berbeda
crosstool-ng
telah dibuat. Ini memberikan bootstrap di atas toolchain yang dibutuhkan untuk membangun kompiler silang.crosstool-ng
mendukung beberapa kembar tiga target pada arsitektur yang berbeda, pada dasarnya ini adalah bootstrap di mana orang mendedikasikan waktu mereka untuk menyelesaikan masalah yang terjadi selama kompilasi toolchain lintas-kompiler.Beberapa distro menyediakan cross-compiler sebagai paket:
arch
menyediakan mingw cross-compiler dan dan arm eabi cross compiler di luar kotak. Terlepas dari kompiler silang lainnya di AUR.fedora
berisi beberapa kompiler silang yang dikemas .ubuntu
menyediakan arm cross-compiler juga.debian
memiliki seluruh repositori dari cross-toolchainsDengan kata lain, periksa apa yang tersedia di distro Anda dalam hal kompiler silang. Jika distro Anda tidak memiliki cross compiler untuk kebutuhan Anda, Anda selalu dapat mengompilasinya sendiri.
Referensi:
Modul kernel perlu diperhatikan
Jika Anda mengkompilasi cross-compiler dengan tangan, Anda memiliki semua yang Anda perlukan untuk mengkompilasi modul kernel. Ini karena Anda memerlukan header kernel untuk dikompilasi
glibc
.Tetapi, jika Anda menggunakan cross-compiler yang disediakan oleh distro Anda, Anda akan membutuhkan header kernel dari kernel yang berjalan pada mesin target.
sumber
crosstool-ng
. Anda mungkin ingin menambahkannya ke daftar. Juga, mengonfigurasi dan menyusun GNU cross-toolchain dengan tangan untuk arsitektur apa pun sangat melibatkan dan jauh lebih membosankan daripada sekadar--target
flag. Saya menduga itu adalah bagian dari mengapa LLVM mendapatkan popularitas; Ini dirancang sedemikian rupa sehingga Anda tidak perlu membangun kembali untuk menargetkan arsitektur lain - sebagai gantinya Anda dapat menargetkan beberapa backend menggunakan perpustakaan frontend dan optimizer yang sama.Perhatikan bahwa sebagai upaya terakhir (yaitu ketika Anda tidak memiliki kode sumber), Anda dapat menjalankan binari pada arsitektur yang berbeda menggunakan emulator seperti
qemu
,dosbox
atauexagear
. Beberapa emulator dirancang untuk meniru sistem selain Linux (mis.dosbox
Dirancang untuk menjalankan program MS-DOS, dan ada banyak emulator untuk konsol game populer). Emulasi memiliki overhead kinerja yang signifikan: program yang diemulasi berjalan 2-10 kali lebih lambat daripada rekan aslinya.Jika Anda perlu menjalankan modul kernel pada CPU non-asli, Anda harus meniru seluruh OS termasuk kernel untuk arsitektur yang sama. AFAIK tidak mungkin menjalankan kode asing di dalam kernel Linux.
sumber
Tidak hanya binari yang tidak portabel antara x86 dan ARM, ada berbagai rasa ARM .
Yang mungkin Anda temui dalam praktik adalah ARMv6 vs ARMv7. Raspberry Pi 1 adalah ARMv6, versi selanjutnya adalah ARMv7. Jadi dimungkinkan untuk mengkompilasi kode pada kode yang lebih baru yang tidak berfungsi pada Pi 1.
Untungnya salah satu manfaat open source dan perangkat lunak bebas adalah memiliki sumber sehingga Anda dapat membangunnya kembali pada arsitektur apa pun. Meskipun ini mungkin memerlukan beberapa pekerjaan.
(Versi ARM membingungkan, tetapi jika ada V sebelum angka itu berbicara tentang arsitektur set instruksi (ISA). Jika tidak ada, itu adalah nomor model seperti "Cortex M0" atau "ARM926EJS". Nomor model tidak ada hubungannya dengan lakukan dengan nomor ISA.)
sumber
Anda selalu perlu menargetkan sebuah platform yang. Dalam kasus yang paling sederhana, CPU target langsung menjalankan kode yang dikompilasi dalam biner (ini kira-kira sesuai dengan executable COM MS DOS). Mari kita pertimbangkan dua platform berbeda yang baru saja saya temukan - Armistice dan Intellio. Dalam kedua kasus, kita akan memiliki program hello world sederhana yang menghasilkan 42 di layar. Saya juga akan berasumsi bahwa Anda menggunakan bahasa multi-platform dengan cara platform-agnostik, jadi kode sumbernya sama untuk keduanya:
Pada Armistice, Anda memiliki driver perangkat sederhana yang menangani pencetakan angka, jadi yang harus Anda lakukan hanyalah output ke port. Dalam bahasa rakitan portabel kami, ini akan sesuai dengan sesuatu seperti ini:
Namun, atau sistem Intellio tidak memiliki hal seperti itu, sehingga harus melalui lapisan lain:
Ups, kami sudah memiliki perbedaan yang signifikan antara keduanya, bahkan sebelum kami sampai ke kode mesin! Ini kira-kira akan sesuai dengan jenis perbedaan yang Anda miliki antara Linux dan MS DOS, atau IBM PC dan X-Box (meskipun keduanya dapat menggunakan CPU yang sama).
Tapi itulah gunanya OS. Mari kita asumsikan kita memiliki HAL yang memastikan bahwa semua konfigurasi perangkat keras yang berbeda ditangani dengan cara yang sama pada lapisan aplikasi - pada dasarnya, kita akan menggunakan pendekatan Intellio bahkan pada Gencatan Senjata, dan kode "rakitan portabel" kami berakhir sama. Ini digunakan oleh sistem mirip Unix modern dan Windows, seringkali bahkan dalam skenario tertanam. Bagus - sekarang kita dapat memiliki kode perakitan yang benar-benar portabel di Armistice dan Intellio. Tapi bagaimana dengan binari?
Seperti yang kita asumsikan, CPU perlu mengeksekusi biner secara langsung. Mari kita lihat baris pertama dari kode kita
mov a, 10h
, di Intellio:Oh Ternyata yang
mov a, constant
sangat populer itu memiliki instruksi sendiri, dengan opcode sendiri. Bagaimana Gencatan Senjata menangani ini?Hmm. Ada opcode untuknya
mov.reg.imm
, jadi kita perlu argumen lain untuk memilih register yang akan kita gunakan. Dan konstanta selalu berupa kata 2-byte, dalam notasi big-endian - begitulah Armistice dirancang, pada kenyataannya, semua instruksi dalam Armistice sepanjang 4 byte, tanpa pengecualian.Sekarang bayangkan menjalankan biner dari Intellio on Armistice: CPU mulai mendekode instruksi, menemukan opcode
20h
. Pada Gencatan Senjata, ini sesuai, katakanlah, denganand.imm.reg
instruksi. Mencoba membaca konstanta 2 byte kata (yang berbunyi10XX
, sudah menjadi masalah), dan kemudian nomor register (yang lainXX
). Kami menjalankan instruksi yang salah, dengan argumen yang salah. Dan lebih buruknya, instruksi selanjutnya akan menjadi palsu, karena kami benar-benar memakan instruksi lain, mengira itu adalah data.Aplikasi tidak memiliki peluang untuk bekerja, dan kemungkinan besar akan crash atau hang segera.
Sekarang, ini tidak berarti bahwa executable selalu perlu mengatakan itu dijalankan pada Intellio atau Armistice. Anda hanya perlu mendefinisikan platform yang independen terhadap CPU (seperti
bash
pada Unix), atau CPU dan OS (seperti Java atau .NET, dan saat ini bahkan JavaScript, semacam). Dalam hal ini, aplikasi dapat menggunakan satu yang dapat dieksekusi untuk semua CPU dan OS yang berbeda, sementara ada beberapa aplikasi atau layanan pada sistem target (yang menargetkan CPU dan / atau OS secara langsung) yang menerjemahkan kode platform-independen ke dalam sesuatu yang CPU sebenarnya bisa dieksekusi. Ini mungkin atau mungkin tidak datang dengan hit kinerja, biaya atau kemampuan.CPU biasanya berasal dari keluarga. Sebagai contoh, semua CPU dari keluarga x86 memiliki serangkaian instruksi umum yang dikodekan dengan cara yang persis sama, sehingga setiap CPU x86 dapat menjalankan setiap program x86, selama ia tidak mencoba menggunakan ekstensi apa pun (misalnya, operasi floating point atau operasi vektor). Pada x86, contoh paling umum saat ini adalah Intel dan AMD, tentu saja. Atmel adalah perusahaan terkenal yang mendesain CPU dalam keluarga ARM, cukup populer untuk perangkat embedded. Apple juga memiliki CPU ARM sendiri, misalnya.
Tetapi ARM sama sekali tidak kompatibel dengan x86 - mereka memiliki persyaratan desain yang sangat berbeda, dan memiliki sedikit persamaan. Instruksi memiliki opcode yang sama sekali berbeda, mereka didekodekan dengan cara yang berbeda, alamat memori diperlakukan berbeda ... Mungkin dimungkinkan untuk membuat biner yang berjalan pada CPU x86 dan CPU ARM, dengan menggunakan beberapa operasi yang aman untuk membedakan antara keduanya dan melompat ke dua set instruksi yang sama sekali berbeda, tetapi itu tetap berarti Anda memiliki instruksi terpisah untuk kedua versi, hanya dengan bootstrapper yang memilih set yang benar saat runtime.
sumber
Dimungkinkan untuk melemparkan kembali pertanyaan ini ke lingkungan yang mungkin lebih akrab. Dengan analogi:
"Saya memiliki program Ruby yang ingin saya jalankan, tetapi platform saya hanya memiliki juru bahasa Python. Dapatkah saya menggunakan juru bahasa Python untuk menjalankan program Ruby saya, atau apakah saya harus menulis ulang program saya dengan Python?"
Arsitektur set instruksi ("target") adalah bahasa - "bahasa mesin" - dan CPU yang berbeda menerapkan bahasa yang berbeda. Jadi meminta ARM CPU untuk menjalankan biner Intel sangat mirip dengan mencoba menjalankan program Ruby menggunakan interpreter Python.
sumber
gcc menggunakan istilah '' arsitektur '' yang berarti '' set instruksi '' dari CPU tertentu, dan "target" mencakup kombinasi CPU dan arsitektur, bersama dengan variabel lain seperti ABI, libc, endian-ness dan banyak lagi (mungkin termasuk "bare metal"). Kompiler tipikal memiliki serangkaian kombinasi target yang terbatas (mungkin satu ABI, satu keluarga CPU, tetapi mungkin keduanya 32- dan 64-bit). Kompilator silang biasanya berarti kompiler dengan target selain sistem yang digunakannya, atau kompilator dengan banyak target atau ABI (lihat juga ini ).
Secara umum, tidak. Biner dalam istilah konvensional adalah kode objek asli untuk CPU atau keluarga CPU tertentu. Tetapi, ada beberapa kasus di mana mereka mungkin cukup portabel:
-march=core2
)Jika Anda entah bagaimana berhasil menyelesaikan masalah ini, masalah biner portabel lainnya dari berbagai versi perpustakaan (glibc saya sedang melihat Anda) kemudian akan muncul dengan sendirinya. (Sebagian besar sistem embedded setidaknya menyelamatkan Anda dari masalah itu.)
Jika Anda belum melakukannya, sekarang adalah waktu yang tepat untuk berlari
gcc -dumpspecs
dangcc --target-help
melihat apa yang Anda hadapi.Binari lemak memiliki berbagai kelemahan , tetapi masih memiliki potensi kegunaan ( EFI ).
Namun ada dua pertimbangan lebih lanjut yang hilang dari jawaban lain: ELF dan penerjemah ELF, dan dukungan kernel Linux untuk format biner yang sewenang-wenang . Saya tidak akan menjelaskan secara terperinci tentang binari atau bytecode untuk prosesor non-nyata di sini, meskipun dimungkinkan untuk memperlakukan ini sebagai "asli" dan mengeksekusi Java atau mengkompilasi binari bytecode Python , biner seperti itu tidak tergantung pada arsitektur perangkat keras (tetapi sebaliknya bergantung pada pada versi VM yang relevan, yang akhirnya menjalankan biner asli).
Setiap sistem Linux kontemporer akan menggunakan binari ELF (perincian teknis dalam PDF ini ), dalam kasus binari ELF dinamis, kernel bertanggung jawab untuk memuat gambar ke dalam memori tetapi ini adalah pekerjaan dari '' juru bahasa '' yang diatur dalam ELF header untuk melakukan angkat berat. Biasanya ini melibatkan memastikan semua pustaka dinamis bergantung tersedia (dengan bantuan bagian '' Dinamis '' yang mencantumkan pustaka dan beberapa struktur lain yang mencantumkan simbol yang diperlukan) - tetapi ini hampir merupakan lapisan tipuan tujuan umum.
(
/lib/ld-linux.so.2
juga merupakan biner ELF, ia tidak memiliki juru bahasa, dan merupakan kode biner asli.)Masalah dengan ELF adalah bahwa header dalam binary (
readelf -h /bin/ls
) menandainya untuk arsitektur, kelas tertentu (32- atau 64-bit), endian-ness dan ABI (binari lemak "universal" Apple menggunakan format biner alternatif Mach-O alih-alih yang memecahkan masalah ini, ini berasal dari NextSTEP). Ini berarti bahwa executable ELF harus cocok dengan sistem yang digunakannya. Salah satu escape escape adalah interpreter, ini bisa berupa apa saja yang dapat dieksekusi (termasuk yang mengekstrak atau memetakan subbagian arsitektur spesifik dari biner aslinya dan memanggil mereka), tetapi Anda masih terkendala oleh jenis ELF yang akan diizinkan dijalankan oleh sistem Anda. . (FreeBSD memiliki cara yang menarik untuk menangani file Linux ELF , yangbrandelf
memodifikasi bidang ELF ABI.)Ada (menggunakan
binfmt_misc
) dukungan untuk Mach-O di linux , ada contoh di sana yang menunjukkan kepada Anda cara membuat dan menjalankan biner (32- & 64-bit) yang gemuk. Fork sumber daya / ADS , seperti yang awalnya dilakukan pada Mac, bisa menjadi solusi, tetapi tidak ada sistem file Linux asli yang mendukung ini.Hal yang kurang lebih sama berlaku untuk modul kernel,
.ko
file juga ELF (meskipun mereka tidak memiliki set juru bahasa). Dalam hal ini ada lapisan tambahan yang menggunakan versi kernel (uname -r
) di jalur pencarian, sesuatu yang secara teoritis dapat dilakukan sebagai gantinya di ELF dengan versi, tetapi pada beberapa kompleksitas dan sedikit keuntungan saya curigai.Seperti dicatat di tempat lain, Linux tidak secara alami mendukung binari lemak, tetapi ada proyek biner lemak aktif: FatELF . Sudah ada selama bertahun-tahun , tidak pernah diintegrasikan ke dalam kernel standar sebagian karena masalah paten (sekarang kedaluwarsa). Pada saat ini membutuhkan dukungan kernel dan toolchain. Itu tidak menggunakan
binfmt_misc
pendekatan, ini langkah-langkah header masalah ELF dan memungkinkan untuk modul kernel lemak juga.Tidak dengan ELF, itu tidak akan membiarkan Anda melakukan ini.
Jawaban sederhananya adalah ya. (Jawaban rumit mencakup emulasi, representasi menengah, penerjemah , dan JIT; kecuali untuk kasus "penurunan versi" biner i686 untuk hanya menggunakan opcode i386, mereka mungkin tidak menarik di sini, dan perbaikan ABI berpotensi sekeras menerjemahkan kode asli. )
Tidak, ELF tidak akan membiarkan Anda melakukan ini.
Jawaban sederhananya adalah ya. Saya percaya FatELF memungkinkan Anda membangun
.ko
yang multi-arsitektur, tetapi pada titik tertentu versi biner untuk setiap arsitektur yang didukung harus dibuat. Hal-hal yang memerlukan modul kernel sering kali datang bersama sumbernya, dan dibuat sesuai kebutuhan, mis. VirtualBox melakukan ini.Ini sudah merupakan jawaban panjang yang mengoceh, hanya ada satu jalan memutar lagi. Kernel sudah memiliki mesin virtual bawaan, meskipun mesin khusus: BPF VM yang digunakan untuk mencocokkan paket. Filter yang dapat dibaca manusia "host foo dan bukan port 22") dikompilasi ke bytecode dan filter paket kernel menjalankannya . EBPF baru tidak hanya untuk paket, dalam teori bahwa kode VM portabel di setiap linux kontemporer, dan llvm mendukungnya tetapi untuk alasan keamanan mungkin tidak akan cocok untuk apa pun selain aturan administratif.
Sekarang, tergantung seberapa murah hati Anda dengan definisi biner yang dapat dieksekusi, Anda dapat (ab) gunakan
binfmt_misc
untuk mengimplementasikan dukungan biner gemuk dengan skrip shell, dan file ZIP sebagai format wadah:Sebut ini "wozbin", dan atur dengan sesuatu seperti:
Ini mendaftarkan
.woz
file dengan kernel,wozbin
skrip dipanggil sebagai gantinya dengan argumen pertama yang ditetapkan ke path.woz
file yang dipanggil .Untuk mendapatkan file portabel (gemuk)
.woz
, cukup buattest.woz
file ZIP dengan hierarki direktori jadi:Di dalam setiap direktori arch / OS / libc (pilihan sewenang-wenang) tempatkan
test
biner dan komponen khusus arsitektur seperti.so
file. Ketika Anda memintanya, subdirektori yang diperlukan diekstraksi ke sistem file in-memory tmpfs (di/mnt/tmpfs
sini) dan dipanggil.sumber
boot berry, pecahkan beberapa masalah Anda .. tetapi itu tidak menyelesaikan masalah cara berjalan di lengan hf, normall / regullAr distro linux untuk x86-32 / 64bit.
Saya pikir itu harus dibangun di dalam isolinux (boatloader linux pada usb) beberapa konverter hidup apa yang bisa mengenali distro regullar dan dalam perjalanan / konversi langsung ke hf.
Mengapa? Karena jika setiap linux dapat dikonversi dengan boot berry untuk bekerja pada arm-hf sehingga bisa membangun mekanisme boot untuk mengisolasi apa yang kita boot menggunakan untuk eacher atau dibangun di ubuntu creat start up disk.
sumber