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 ).
Apakah binari portabel di berbagai arsitektur CPU?
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:
- satu arsitektur adalah superset dari yang lain (biasanya binari x86 menargetkan i386 atau i686 daripada x86 terbaru dan terhebat, misalnya
-march=core2
)
- satu arsitektur menyediakan emulasi asli atau terjemahan yang lain (Anda mungkin pernah mendengar tentang Crusoe ), atau menyediakan co-prosesor yang kompatibel (misalnya PS2 )
- OS dan runtime mendukung multiarch (mis. kemampuan untuk menjalankan binari x86 32-bit pada x86_64), atau membuat VM / JIT mulus (Android menggunakan Dalvik atau ART )
- ada dukungan untuk binari "gemuk" yang pada dasarnya mengandung kode duplikat untuk setiap arsitektur yang didukung
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
dan gcc --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.
$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses \
shared libs), stripped
$ readelf -p .interp /bin/ls
String dump of section '.interp':
[ 0] /lib/ld-linux.so.2
( /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 , yang brandelf
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.
- 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'?
Tidak dengan ELF, itu tidak akan membiarkan Anda melakukan ini.
- 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'?
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. )
- Demikian pula, jika saya memiliki modul kernel (driver perangkat) yang dapat dimuat yang bekerja pada 'target x86, linux OS versi xyz', dapatkah saya memuat / menggunakan .ko yang dikompilasi yang sama pada sistem lain 'target ARM, OS linux versi xyz' ?
Tidak, ELF tidak akan membiarkan Anda melakukan ini.
- 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 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:
#!/bin/bash
name=$1
prog=${1/*\//} # basename
prog=${prog/.woz/} # remove extension
root=/mnt/tmpfs
root=$(TMPDIR= mktemp -d -p ${root} woz.XXXXXX)
shift # drop argv[0], keep other args
arch=$(uname -m) # i686
uname_s=$(uname -s) # Linux
glibc=$(getconf GNU_LIBC_VERSION) # glibc 2.17
glibc=${glibc// /-} # s/ /-/g
# test that "foo.woz" can unzip, and test "foo" is executable
unzip -tqq "$1" && {
unzip -q -o -j -d ${root} "$1" "${arch}/${uname_s}/${glibc}/*"
test -x ${root}/$prog && (
export LD_LIBRARY_PATH="${root}:${LD_LIBRARY_PATH}"
#readlink -f "${root}/${prog}" # for the curious
exec -a "${name}" "${root}/${prog}" "$@"
)
rc=$?
#rm -rf -- "${root}/${prog}" # for the brave
exit $rc
}
Sebut ini "wozbin", dan atur dengan sesuatu seperti:
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
printf ":%s:%s:%s:%s:%s:%s:%s" \
"woz" "E" "" "woz" "" "/path/to/wozbin" "" > /proc/sys/fs/binfmt_misc/register
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 buat test.woz
file ZIP dengan hierarki direktori jadi:
i686/
\- Linux/
\- glibc-2.12/
armv6l/
\- Linux/
\- glibc-2.17/
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.