Perché due binari di programmi con solo commenti modificati non corrispondono esattamente in gcc?


110

Ho creato due programmi C.

  1. Programma 1

    int main()
    {
    }
  2. Programma 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK, durante la compilazione, il compilatore (gcc) dovrebbe ignorare i commenti e gli spazi bianchi ridondanti, e quindi l'output deve essere simile.

Ma quando ho controllato gli md5sums dei binari di output, non corrispondono. Ho anche provato a compilare con l'ottimizzazione -O3e -Ofastma ancora non corrispondevano.

Cosa sta succedendo qui?

EDIT: i comandi esatti e le somme md5 sono (t1.c è il programma 1 e t2.c è il programma 2)

gcc ./t1.c -o aaa
gcc ./t2.c -o bbb
98c1a86e593fd0181383662e68bac22f  aaa
c10293cbe6031b13dc6244d01b4d2793  bbb

gcc ./t2.c -Ofast -o bbb
gcc ./t1.c -Ofast -o aaa
2f65a6d5bc9bf1351bdd6919a766fa10  aaa
c0bee139c47183ce62e10c3dbc13c614  bbb


gcc ./t1.c -O3 -o aaa
gcc ./t2.c -O3 -o bbb
564a39d982710b0070bb9349bfc0e2cd  aaa
ad89b15e73b26e32026fd0f1dc152cd2  bbb

E sì, md5sums corrisponde a più compilation con gli stessi flag.

BTW il mio sistema è gcc (GCC) 5.2.0eLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux


17
Includere i propri flag esatti della riga di comando. Ad esempio, le informazioni di debug sono incluse nei file binari? Se è così, il cambiamento dei numeri di riga lo influenzerebbe ovviamente ...
Jon Skeet

4
La somma MD5 è coerente tra più build dello stesso codice?
poco entusiasta

3
Non posso riprodurlo. Avrei immaginato che ciò sia causato dal fatto che GCC incorpora un intero gruppo di metadati nei binari durante la loro compilazione (compresi i timestamp). Se potessi aggiungere i flag della riga di comando precisi che hai usato, sarà utile.
cyphar

2
Invece di controllare semplicemente MD5sums e rimanere bloccato, hexdump e diff per vedere esattamente quali byte differiscono
MM

12
Anche se la risposta alla domanda "cosa c'è di diverso tra i due output del compilatore?" È interessante, noto che la domanda ha un presupposto ingiustificato: che i due output dovrebbero essere gli stessi e che abbiamo bisogno di una spiegazione del motivo per cui sono diversi. Tutto ciò che il compilatore ti promette è che quando gli dai un programma C legale, l'output è un eseguibile legale che implementa quel programma. Che due esecuzioni qualsiasi del compilatore producano lo stesso binario non è una garanzia dello standard C.
Eric Lippert

Risposte:


159

È perché i nomi dei file sono diversi (sebbene l'output delle stringhe sia lo stesso). Se provi a modificare il file stesso (invece di avere due file), noterai che i binari di output non sono più diversi. Come abbiamo detto sia io che Jens, è perché GCC scarica un intero carico di metadati nei binari che compila, incluso il nome esatto del file sorgente (e anche AFAICS fa clang).

Prova questo:

$ cp code.c code2.c subdir/code.c
$ gcc code.c -o a
$ gcc code2.c -o b
$ gcc subdir/code.c -o a2
$ diff a b
Binary files a and b differ
$ diff a2 b
Binary files a2 and b differ
$ diff -s a a2
Files a and a2 are identical

Questo spiega perché i tuoi md5sums non cambiano tra le build, ma sono diversi tra i diversi file. Se vuoi, puoi fare quello che Jens ha suggerito e confrontare l'output di stringsper ogni binario noterai che i nomi dei file sono incorporati nel binario. Se vuoi "aggiustare" questo, puoi striprimuovere i file binari e i metadati:

$ strip a a2 b
$ diff -s a b
Files a and b are identical
$ diff -s a2 b
Files a2 and b are identical
$ diff -s a a2
Files a and a2 are identical

EDIT: aggiornato per dire che puoi rimuovere i binari per "risolvere" il problema.
cyphar

30
Ed è per questo che dovresti confrontare l'output dell'assembly, non i checksum MD5.
Gare di leggerezza in orbita il

1
Ho posto una domanda di follow-up qui .
Federico Poloni

4
A seconda del formato del file oggetto, l'ora di compilazione viene memorizzata anche nei file oggetto. Quindi l'utilizzo di file COFF per esempio i file a e a2 non sarebbe identico.
Martin Rosenau

28

Il motivo più comune sono i nomi di file e le marche temporali aggiunti dal compilatore (di solito nella parte delle informazioni di debug delle sezioni ELF).

Prova a correre

 $ strings -a program > x
 ...recompile program...
 $ strings -a program > y
 $ diff x y

e potresti vedere il motivo. Una volta l'ho usato per scoprire perché la stessa fonte avrebbe causato codice diverso se compilato in directory diverse. La scoperta è stata che la __FILE__macro si è espansa a un nome file assoluto , diverso in entrambi gli alberi.


1
Secondo gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (obsoleto, lo so) non salvano i timestamp e potrebbe essere un problema del linker. Tuttavia, ricordo di aver letto di recente una storia su come un'azienda di sicurezza ha profilato le abitudini di lavoro di un team di hacker utilizzando le informazioni di timestamp di GCC nei loro binari.
cyphar

3
E per non parlare del fatto che OP afferma che "md5sums corrisponde a più compilation con gli stessi flag", il che indica che probabilmente non sono i timestamp a causare il problema. Probabilmente è causato dal fatto che sono nomi di file diversi.
cyphar

1
@cyphar Anche i nomi di file diversi dovrebbero essere catturati dall'approccio strings / diff.
Jens

15

Nota : ricorda che il nome del file di origine va nel binario unstripped, quindi due programmi provenienti da file di origine con nomi diversi avranno hash diversi.

In situazioni simili, se quanto sopra non si applica , puoi provare:

  • correre stripcontro il binario per rimuovere un po 'di grasso. Se i file binari rimossi sono gli stessi, si trattava di alcuni metadati non essenziali per il funzionamento del programma.
  • generare un output intermedio dell'assembly per verificare che la differenza non sia nelle istruzioni effettive della CPU (o, comunque, per individuare meglio dove si trova effettivamente la differenza )
  • utilizzare stringso eseguire il dump di entrambi i programmi in hex ed eseguire un diff sui due dump hex. Una volta individuate le differenze, potresti provare a vedere se c'è qualche rima o motivo per loro (PID, timestamp, timestamp del file sorgente ...). Ad esempio, potresti avere una routine che memorizza il timestamp in fase di compilazione per scopi diagnostici.

Il mio sistema è gcc (GCC) 5.2.0eLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Utente registrato

2
Si dovrebbe cercare in realtà facendo due file separati. Non potrei nemmeno riprodurlo modificando un singolo file.
cyphar

Sì, i nomi dei file sono colpevoli. Posso ottenere gli stessi md5sums se compilo i programmi con lo stesso nome.
Utente registrato il
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.