In breve:se stai portando un file binario compilato da un host a un altro utilizzando la stessa architettura (o una compatibile) , potresti benissimo portarlo su un'altra distribuzione . Tuttavia, con l'aumentare della complessità del codice, aumenta la probabilità di essere collegati a una libreria non installata; installato in un altro luogo; o installato in una versione diversa, aumenta. Prendendo ad esempio il tuo codice, per il quale ldd
riporta le seguenti dipendenze quando compilato con gcc -o exit-test exit-test.c
su un host Ubuntu Linux (derivato da Debian):
$ ldd exit-test
linux-gate.so.1 => (0xb7748000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757b000)
/lib/ld-linux.so.2 (0x8005a000)
Ovviamente questo binario non funzionerà se lo trasferisco, ad esempio, su un Mac (./exit-test: cannot execute binary file: Exec format error
). Proviamo a spostarlo in una casella RHEL:
$ ./exit-test
-bash: ./exit-test: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
Oh caro. Perché potrebbe essere?
$ ls /lib/ld-l* # reference the `ldd` output above
ls: cannot access /lib/ld-l*: No such file or directory
Anche per questo caso d'uso, il forklifting non è riuscito a causa della mancanza di librerie condivise.
Tuttavia, se lo compilo con gcc -static exit-test-static exit-test.c
, il porting sul sistema senza le librerie funziona perfettamente. A scapito, ovviamente, dello spazio su disco:
$ ls -l ./exit-test{,-static}
-rwxr-xr-x 1 username groupname 7312 Jan 29 14:18 ./exit-test
-rwxr-xr-x 1 username groupname 728228 Jan 29 14:27 ./exit-test-static
Un'altra soluzione praticabile sarebbe quella di installare le librerie richieste sul nuovo host.
Come per molte cose nell'universo di U&L, questo è un gatto con molte pelli, due delle quali sono descritte sopra.
Dipende. Qualcosa compilato per IA-32 (Intel a 32 bit) può essere eseguito su amd64 poiché Linux su Intel mantiene la retrocompatibilità con le applicazioni a 32 bit (con software adatto installato). Ecco il tuo code
compilato su un sistema RedHat 7.3 a 32 bit (circa 2002, gcc versione 2.96) e poi il binario copiato ed eseguito su un sistema Centos 7.4 a 64 bit (circa 2017):
-bash-4.2$ file code
code: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped
-bash-4.2$ ./code
-bash: ./code: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
-bash-4.2$ sudo yum -y install glibc.i686
...
-bash-4.2$ ./code ; echo $?
99
L'antico RedHat 7.3 a Centos 7.4 (essenzialmente RedHat Enterprise Linux 7.4) rimane nella stessa famiglia di "distribuzioni", quindi probabilmente avrà una portabilità migliore rispetto al passaggio da un'installazione casuale "Linux da zero" dal 2002 a qualche altra distribuzione Linux casuale nel 2018 .
Qualcosa compilato per amd64 non funzionerebbe solo su versioni a 32 bit di Linux (il vecchio hardware non conosce il nuovo hardware). Questo vale anche per il nuovo software compilato su sistemi moderni destinati a essere eseguiti su cose vecchie e antiche, poiché le librerie e persino le chiamate di sistema potrebbero non essere portabili all'indietro, quindi potrebbero richiedere trucchi di compilazione o ottenere un vecchio compilatore e così via, o forse invece compilazione sul vecchio sistema. (Questo è un buon motivo per tenere in giro macchine virtuali di cose antiche.)
L'architettura conta; amd64 (o IA-32) è molto diverso da ARM o MIPS, quindi non ci si aspetta che il binario di uno di questi venga eseguito su un altro. A livello di assembly il main
sezione del tuo codice su IA-32 compila tramite gcc -S code.c
a
main:
pushl %ebp
movl %esp,%ebp
movl $99,%eax
popl %ebp
ret
che un sistema amd64 può gestire (su un sistema Linux -- OpenBSD al contrario su amd64 non supporta binari a 32 bit; la retrocompatibilità con i vecchi archi offre agli aggressori spazio di manovra, ad es. CVE-2014-8866 e amici). Nel frattempo su un sistema MIPS big-endian main
compila invece in:
main:
.frame $fp,8,$31
.mask 0x40000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
addiu $sp,$sp,-8
sw $fp,4($sp)
move $fp,$sp
li $2,99
move $sp,$fp
lw $fp,4($sp)
addiu $sp,$sp,8
j $31
nop
con cui un processore Intel non avrà idea di cosa fare, e allo stesso modo per l'assembly Intel su MIPS.
Potresti usare QEMU o qualche altro emulatore per eseguire codice esterno (forse molto, molto lentamente).
Tuttavia! Il tuo codice è un codice molto semplice, quindi avrà meno problemi di portabilità rispetto a qualsiasi altra cosa; i programmi tipicamente fanno uso di librerie che sono cambiate nel tempo (glibc, openssl, ...); per quelli potrebbe anche essere necessario installare versioni precedenti di varie librerie (RedHat, ad esempio, in genere inserisce "compat" da qualche parte nel nome del pacchetto per tali)
compat-glibc.x86_64 1:2.12-4.el7.centos
o forse preoccuparti delle modifiche ABI (Application Binary Interface) per cose molto vecchie che usano glibc, o modifiche più recenti dovute a C++ 11 o altre versioni di C++. Si potrebbe anche compilare statico (aumentando notevolmente la dimensione binaria su disco) per cercare di evitare problemi con la libreria, anche se il fatto che qualche vecchio binario lo abbia fatto dipende dal fatto che la vecchia distribuzione Linux stesse compilando quasi tutto ciò che è dinamico (RedHat:sì) o meno. D'altra parte, cose come patchelf
può rejigger dinamico (ELF, ma probabilmente non a.out
format) binari per utilizzare altre librerie.
Tuttavia! Essere in grado di eseguire un programma è una cosa, e fare effettivamente qualcosa di utile con esso un'altra. I vecchi binari Intel a 32 bit possono avere problemi di sicurezza se dipendono da una versione di OpenSSL che contiene qualche problema di sicurezza orribile e senza backport, oppure il programma potrebbe non essere in grado di negoziare affatto con i server Web moderni (come il moderno i server rifiutano i vecchi protocolli e le cifrature del vecchio programma), oppure il protocollo SSH versione 1 non è più supportato, oppure...
In aggiunta alle eccellenti risposte di @thrig e @DopeGhoti:i sistemi operativi Unix o simili a Unix, incluso Linux, sono stati tradizionalmente progettati e allineati più per la portabilità del codice sorgente che per i binari.
Se non hai nulla di specifico sull'hardware o sei una semplice fonte come nel tuo esempio, puoi spostarlo senza alcun problema tra praticamente qualsiasi versione di Linux o architettura come codice sorgente purché i server di destinazione abbiano i pacchetti di sviluppo C installati , le librerie necessarie e le corrispondenti librerie di sviluppo installate.
Per quanto riguarda il porting di codice più avanzato da versioni precedenti di Linux lontane nel tempo, o programmi più specifici come i moduli del kernel per diverse versioni del kernel, potresti dover adattare e modificare il codice sorgente per tenere conto di librerie/API/ABI deprecate.