Un eseguibile Linux compilato su un "sapore" di Linux verrà eseguito su un altro?


59

L'eseguibile di un programma piccolo, estremamente semplice, come quello mostrato di seguito, compilato su una versione di Linux, funzionerà su una versione diversa? O dovrebbe essere ricompilato?

L'architettura della macchina è importante in un caso come questo?

int main()
{
  return (99);
}

2
Grazie a tutti per le risposte eccezionali! Ho imparato molto più di quanto mi aspettassi. Ho reso il codice artificialmente semplice apposta in modo che dipendesse dal minor numero possibile di librerie; ma avrei dovuto dirlo in anticipo. La maggior parte della mia codifica C ++ su piattaforme implicava lo sviluppo con uno strumento Microsoft come Visual Studio e il trasferimento del codice su un sistema * nix e la ricompilazione.
JCDeen

4
Le molte sfaccettature e considerazioni espresse qui mi hanno stupito! Sinceramente vorrei poterne scegliere diverse come LA risposta. Grazie ancora a tutti! Cordiali saluti.
JCDeen

2
Android è anche un sistema operativo basato su Linux. Buona fortuna, tuttavia, eseguendo qualsiasi codice compilato glibclì, o viceversa. Certo, non è del tutto impossibile .
undercat

2
Per la massima compatibilità di uno strumento da riga di comando, potresti voler usare uClibc, musl o dietlibc invece di glibc e collegare staticamente il tuo eseguibile a 32 bit ( gcc -m32 -static). In questo modo, qualsiasi Linux i386 o amd64 sarà in grado di eseguire l'eseguibile.
punti

10
Dovresti tornare 42 ! :)
Homunculus Reticulli,

Risposte:


49

Dipende. Qualcosa compilato per IA-32 (Intel a 32 bit) potrebbe essere eseguito su amd64 poiché Linux su Intel mantiene la retrocompatibilità con le applicazioni a 32 bit (con software adeguato installato). Ecco il tuo codecompilato sul sistema RedHat 7.3 a 32 bit (circa 2002, gcc versione 2.96) e poi il binario copiato su 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'antica RedHat da 7.3 a Centos 7.4 (essenzialmente RedHat Enterprise Linux 7.4) si trova nella stessa famiglia "distributiva", quindi probabilmente avrà una portabilità migliore rispetto a passare da un'installazione casuale "Linux da zero" dal 2002 ad un'altra distribuzione Linux casuale nel 2018 .

Qualcosa compilato per amd64 non funzionerebbe solo su versioni a 32 bit di Linux (il vecchio hardware non è a conoscenza del nuovo hardware). Ciò vale anche per i nuovi software compilati su sistemi moderni destinati a essere eseguiti su vecchie cose antiche, poiché le librerie e persino le chiamate di sistema potrebbero non essere portatili all'indietro, quindi potrebbero richiedere trucchi di compilazione o ottenere un vecchio compilatore e così via, o forse invece compilando sul vecchio sistema. (Questa è una buona ragione per tenere in giro macchine virtuali di vecchie cose antiche.)

L'architettura è importante; amd64 (o IA-32) è molto diverso da ARM o MIPS quindi il binario di uno di questi non dovrebbe funzionare su un altro. A livello di assembly la mainsezione del codice su IA-32 viene compilata tramite gcc -S code.ca

main:
    pushl %ebp
    movl %esp,%ebp
    movl $99,%eax
    popl %ebp
    ret

con cui un sistema amd64 è in grado di gestire (su un sistema Linux - OpenBSD, al contrario, su amd64 non supporta i binari a 32 bit; la retrocompatibilità con i vecchi archi lascia spazio agli aggressori, ad esempio CVE-2014-8866 e amici). Nel frattempo su un sistema MIPS big-endian main invece compila per:

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

che un processore Intel non avrà idea di cosa fare e allo stesso modo per l'assemblaggio Intel su MIPS.

È possibile utilizzare QEMU o un altro emulatore per eseguire il codice esterno (forse molto, molto lentamente).

Però! Il tuo codice è un codice molto semplice, quindi avrà meno problemi di portabilità rispetto a qualsiasi altra cosa; i programmi in genere fanno uso di librerie che sono cambiate nel tempo (glibc, openssl, ...); per quelli potrebbe anche essere necessario installare versioni precedenti di varie librerie (ad esempio RedHat generalmente inserisce "compat" nel nome del pacchetto)

compat-glibc.x86_64                     1:2.12-4.el7.centos

o forse preoccuparsi delle modifiche ABI (Application Binary Interface) per cose vecchie che usano glibc, o più recenti modifiche 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 stia compilando quasi tutto dinamico (RedHat: yes) o no. D'altra parte, cose come patchelfpossono riattivare i binari dinamici (ELF, ma probabilmente non a.outformattarli) per usare altre librerie.

Però! Essere in grado di eseguire un programma è una cosa, e in realtà fare qualcosa di utile con un'altra. I vecchi binari Intel a 32 bit possono avere problemi di sicurezza se dipendono da una versione di OpenSSL che ha in sé qualche problema di sicurezza orribile e non backport, oppure il programma potrebbe non essere in grado di negoziare affatto con i moderni server Web (come i moderni i server rifiutano i vecchi protocolli e le cifre del vecchio programma), oppure il protocollo SSH versione 1 non è più supportato, oppure ...


14
per quanto riguarda il primo paragrafo: no, Intel lo chiama "Intel 64" (in questi giorni, dopo aver passato altri nomi in precedenza). IA-64 si riferisce a Itanium, non è compatibile con x86.
Hobbs

1
@hobbs grazie, ho sostituito quei riferimenti con amd64; Lascerò la denominazione delle cose al reparto marketing di Intel.
thrig

3
perché non menzionare il collegamento statico?
Dal

2
Non cambiano solo le ABI delle librerie, ma anche l'interfaccia syscall del kernel viene estesa nel tempo. Notare il for GNU/Linux 2.6.32(o tale) nell'output di file /usr/bin/ls.
Charles Duffy,

1
@Wilbert Probabilmente ti manca quel thrig che si riferiva a Red Hat Linux che è distinto da Red Hat Enterprise Linux.
Bob

67

In breve: se stai portando un binario compilato da un host a un altro usando la stessa (o compatibile) architettura , potresti andare perfettamente in 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'altra posizione; o installato in una versione diversa, aumenta. Prendendo ad esempio il tuo codice, per il quale lddriporta le seguenti dipendenze quando compilato gcc -o exit-test exit-test.csu 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 lancio su, diciamo, su un Mac ( ./exit-test: cannot execute binary file: Exec format error). Proviamo a spostarlo in una scatola 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 forklift è fallito a causa della mancanza di librerie condivise.

Tuttavia, se lo compilo gcc -static exit-test-static exit-test.c, il porting sul sistema senza le librerie funziona bene. A spese, 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 necessarie sul nuovo host.

Come per molte cose nell'universo U&L, questo è un gatto con molte pelli, due delle quali sono descritte sopra.


4
In effetti, mi sono dimenticato dei binari statici. Alcuni fornitori adottano binari statici e anche alcuni autori di malware per massimizzare la compatibilità binaria tra le versioni Linux della stessa architettura
Rui F Ribeiro,

8
Inforcabilita ...?
user253751

2
@immibis Penso che significhi copiare i dati (l'eseguibile) da un ambiente (distro) a un altro, in cui i dati non sono progettati per l'ambiente di destinazione.
wjandrea,

13
Il tuo esempio di Linux è purtroppo piuttosto artificiale e illustra il tuo punto di vista sulle architetture piuttosto che sulle distribuzioni: hai creato un binario a 32 bit su Debian e hai provato a eseguirlo su RHEL a 64 bit; quelle sono architetture diverse ... I binari della stessa architettura con così poche dipendenze di libreria possono essere copiati bene.
Stephen Kitt,

7
@MSalters Non sto dicendo che sia irragionevole, sto dicendo che è un cattivo esempio dato il punto che DopeGhoti sta cercando di fare (non è possibile copiare i binari da una distribuzione all'altra - il che è sbagliato). Ovviamente Linux a 64 bit su Intel supporta anche l'esecuzione di file eseguibili a 32 bit, con l'infrastruttura appropriata. Un esempio valido in questo caso IMO potrebbe essere la costruzione di un amd64binario e l'esecuzione su un'altra amd64distribuzione, oppure la costruzione di un i386binario e l'esecuzione su un'altra i386distribuzione.
Stephen Kitt,

25

In aggiunta alle eccellenti risposte @thrig e @DopeGhoti: i sistemi operativi Unix o Unix, incluso Linux, erano tradizionalmente sempre progettati e allineati più per la portabilità del codice sorgente che per i binari.

Se non si ha nulla di specifico sull'hardware o si tratta di un semplice sorgente come nell'esempio, è possibile 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 librerie di sviluppo corrispondenti installate.

Per quanto riguarda il porting di codice più avanzato da versioni precedenti di Linux distanti nel tempo, o programmi più specifici come moduli del kernel per diverse versioni del kernel, potrebbe essere necessario adattare e modificare il codice sorgente per tenere conto di librerie / API / ABI obsolete.


19

Per impostazione predefinita , quasi sicuramente incontrerai problemi con le librerie esterne. Alcune delle altre risposte approfondiscono questi problemi, quindi non duplicherò il loro lavoro.

È possibile , tuttavia, compilare molti programmi - anche quelli non banali - per essere portabile tra i sistemi Linux. La chiave è un toolkit chiamato Linux Standard Base . Il LSB è stato progettato per creare solo questi tipi di applicazioni portatili. Compilare un'applicazione per LSB v5.0 e verrà eseguita su qualsiasi altro ambiente Linux (della stessa architettura) che implementa LSB v5.0. Alcune distro Linux sono conformi a LSB e altre includono toolkit / librerie LSB come pacchetto installabile. Se costruisci la tua applicazione usando gli strumenti LSB (come il lsbccwrapper per gcc) e ti colleghi alla versione LSB delle librerie, creerai un'applicazione portatile.


e con qemute puoi anche eseguire programmi compilati per diverse archtecture, (non ad alte prestazioni, ma puoi eseguirli)
Jasen

1
Non ero nemmeno a conoscenza del toolkit Linux Standard Base, quindi grazie! Ho iniziato a lavorare con C / C ++ molto tempo fa, quindi molte delle informazioni in queste risposte sono nuove per me. E molto utile.
JCDeen

1
L'articolo di Wikipedia dice che Debian e Ubuntu non implementano l'LSB (e non hanno intenzione di farlo.)
BlackJack

2
@ BlackJack- La distribuzione stessa non implementa il 100% di essa come parte del sistema operativo principale, ma è possibile installare librerie e toolkit compatibili con LSB come pacchetti opzionali. Ho usato Ubuntu (per esempio) per costruire programmi compatibili con LSB che poi giravano su Suse e Centos, hai solo bisogno di apt-get installun paio di pacchetti.
BTA

10

Può essere.

Le cose che tendono a romperlo includono.

  1. Diverse architetture. Ovviamente architetture totalmente diverse non funzioneranno (a meno che tu non abbia qualcosa come la modalità utente qemu con binfmt_misc ma non è una configurazione normale). i binari x86 possono funzionare su amd64 ma solo se sono disponibili le librerie a 32 bit richieste.
  2. Versioni della libreria. Se la sovversione è sbagliata, non troverà affatto la libreria. Se la sovversione è la stessa ma il binario è costruito su una versione più recente della libreria rispetto a quella in esecuzione, potrebbe non riuscire a caricarsi a causa di nuovi simboli o nuove versioni di simboli. In particolare, glibc è un grande utilizzatore del versioning dei simboli, quindi è molto probabile che i binari creati con un nuovo glibc falliscano con un vecchio glibc.

Se si evita di utilizzare librerie che cambiano rapidamente, evitare i cambiamenti dell'architettura e basarsi sulla distro più vecchia che si desidera indirizzare, si hanno buone probabilità di far funzionare un binario su molte distro.


4

Oltre ad alcune delle cose menzionate in precedenza, sono state apportate alcune modifiche al formato del file eseguibile. Per la maggior parte, Linux usa ELF, ma le versioni precedenti usavano a.out o COFF.

L'inizio di un wikihole:

https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats

Potrebbe esserci un modo per ottenere versioni precedenti per eseguire formati più recenti, ma personalmente non l'ho mai esaminato.


"vecchio" è a questo punto molto vecchio ora però.
washwash
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.