Warum stimmen zwei Binärdateien von Programmen mit nur geänderten Kommentaren in gcc nicht genau überein?

110

Ich habe zwei C-Programme erstellt

  1. Programm 1

    int main()
    {
    }
  2. Programm 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK, beim Kompilieren sollte der Compiler (gcc) die Kommentare und redundanten Leerzeichen ignorieren, und daher muss die Ausgabe ähnlich sein.

Aber als ich die md5-Summen der Ausgabe-Binärdateien überprüft habe, stimmen sie nicht überein. Ich habe auch versucht mit Optimierung kompilieren -O3und -Ofastaber sie haben immer noch nicht überein.

Was passiert hier?

EDIT: die genauen Befehle und dort sind md5sums (t1.c ist Programm 1 und t2.c ist Programm 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

Und ja, md5sums stimmen über mehrere Kompilierungen mit denselben Flags hinweg überein.

Übrigens ist mein System gcc (GCC) 5.2.0undLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux

registrierter Nutzer
quelle
17
Bitte geben Sie Ihre genauen Befehlszeilen-Flags an. Sind beispielsweise Debug-Informationen überhaupt in den Binärdateien enthalten? Wenn ja, würde sich die Änderung der Zeilennummern offensichtlich darauf auswirken ...
Jon Skeet
4
Ist die MD5-Summe über mehrere Builds desselben Codes hinweg konsistent?
unenthusiasticuser
3
Ich kann das nicht reproduzieren. Ich hätte vermutet, dass dies darauf zurückzuführen ist, dass der GCC beim Kompilieren eine ganze Reihe von Metadaten in Binärdateien einbettet (einschließlich Zeitstempel). Wenn Sie die genauen Befehlszeilenflags hinzufügen könnten, die Sie verwendet haben, wäre dies nützlich.
Cyphar
2
Anstatt nur MD5-Summen zu überprüfen und stecken zu bleiben, Hexdump und Diff, um genau zu sehen, welche Bytes sich unterscheiden
MM
12
Obwohl die Antwort auf die Frage "Was ist zwischen den beiden Compiler-Ausgaben unterschiedlich?" Interessant ist, dass die Frage eine ungerechtfertigte Annahme hat: dass die beiden Ausgänge gleich sein sollten und dass wir eine Erklärung benötigen, warum sie unterschiedlich sind. Der Compiler verspricht Ihnen lediglich, dass die Ausgabe eine legale ausführbare Datei ist, die dieses Programm implementiert, wenn Sie ihm ein legales C-Programm geben. Dass zwei beliebige Ausführungen des Compilers dieselbe Binärdatei erzeugen, ist keine Garantie für den C-Standard.
Eric Lippert

Antworten:

159

Dies liegt daran, dass die Dateinamen unterschiedlich sind (obwohl die Ausgabe der Zeichenfolgen gleich ist). Wenn Sie versuchen, die Datei selbst zu ändern (anstatt zwei Dateien zu haben), werden Sie feststellen, dass sich die Ausgabe-Binärdateien nicht mehr unterscheiden. Wie sowohl Jens als auch ich sagten, liegt es daran, dass GCC eine ganze Menge Metadaten in die von ihm erstellten Binärdateien speichert , einschließlich des genauen Quelldateinamens (und AFAICS klirrt auch).

Versuche dies:

$ 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

Dies erklärt, warum sich Ihre md5sums nicht zwischen Builds ändern, sondern sich zwischen verschiedenen Dateien unterscheiden. Wenn Sie möchten, können Sie das tun, was Jens vorgeschlagen hat, und die Ausgabe stringsfür jede Binärdatei vergleichen. Sie werden feststellen, dass die Dateinamen in die Binärdatei eingebettet sind. Wenn Sie dies "beheben" möchten, können Sie stripdie Binärdateien und die Metadaten entfernen:

$ 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
Cyphar
quelle
BEARBEITEN: Aktualisiert, um anzuzeigen, dass Sie die Binärdateien entfernen können, um das Problem zu "beheben".
Cyphar
30
Aus diesem Grund sollten Sie die Assembly-Ausgabe vergleichen, nicht die MD5-Prüfsummen.
Leichtigkeitsrennen im Orbit
1
Ich habe eine Follow-up - Frage gestellt hier .
Federico Poloni
4
Abhängig vom Objektdateiformat wird die Kompilierungszeit auch in den Objektdateien gespeichert. Die Verwendung von COFF-Dateien, zum Beispiel der Dateien a und a2, wäre also nicht identisch.
Martin Rosenau
28

Der häufigste Grund sind Dateinamen und Zeitstempel, die vom Compiler hinzugefügt wurden (normalerweise im Debug-Info-Teil der ELF-Abschnitte).

Versuche zu rennen

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

und Sie könnten den Grund sehen. Ich habe dies einmal verwendet, um herauszufinden, warum dieselbe Quelle beim Kompilieren in verschiedenen Verzeichnissen unterschiedlichen Code verursacht. Das Ergebnis war, dass das __FILE__Makro zu einem absoluten Dateinamen erweitert wurde, der sich in beiden Bäumen unterscheidet.

Jens
quelle
1
Laut gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (veraltet, ich weiß) speichern sie keine Zeitstempel und es könnte ein Linkerproblem sein. Ich erinnere mich jedoch, dass ich kürzlich eine Geschichte darüber gelesen habe, wie eine Sicherheitsfirma die Arbeitsgewohnheiten eines Hacking-Teams anhand der GCC-Zeitstempelinformationen in ihren Binärdateien profiliert hat.
Cyphar
3
Und ganz zu schweigen davon, dass OP angibt, dass "md5sums über mehrere Kompilierungen hinweg mit denselben Flags übereinstimmen", was darauf hinweist, dass es wahrscheinlich keine Zeitstempel sind, die das Problem verursachen. Dies liegt wahrscheinlich daran, dass es sich um unterschiedliche Dateinamen handelt.
Cyphar
1
@cyphar Verschiedene Dateinamen sollten auch vom Strings / Diff-Ansatz erfasst werden.
Jens
15

Hinweis : Denken Sie daran, dass der Name der Quelldatei in die nicht entfernte Binärdatei eingeht, sodass zwei Programme, die aus Quelldateien mit unterschiedlichen Namen stammen, unterschiedliche Hashes haben.

In ähnlichen Situationen können Sie Folgendes versuchen , falls dies nicht zutrifft :

  • Laufen stripgegen die Binärdatei, um etwas Fett zu entfernen. Wenn die entfernten Binärdateien identisch sind, waren einige Metadaten für die Programmoperation nicht unbedingt erforderlich.
  • Generieren einer Baugruppenzwischenausgabe, um zu überprüfen, ob der Unterschied nicht in den tatsächlichen CPU-Anweisungen enthalten ist (oder um besser zu bestimmen, wo der Unterschied tatsächlich liegt )
  • Verwenden Sie stringsoder sichern Sie beide Programme, um einen Hex auf den beiden Hex-Dumps zu hexen und auszuführen. Sobald Sie die Unterschiede gefunden haben, können Sie versuchen, herauszufinden, ob sie einen Reim oder einen Grund haben (PID, Zeitstempel, Zeitstempel der Quelldatei ...). Beispielsweise könnte eine Routine den Zeitstempel zur Kompilierungszeit für Diagnosezwecke speichern .
LSerni
quelle
Mein System ist gcc (GCC) 5.2.0undLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Registrierter Benutzer
2
Sie sollten versuchen , tatsächlich zwei separate Dateien zu machen. Ich konnte es auch nicht reproduzieren, indem ich eine einzelne Datei änderte.
Cyphar
Ja, Dateinamen sind schuld. Ich kann die gleichen MD5-Summen erhalten, wenn ich die Programme mit dem gleichen Namen kompiliere.
Registrierter Benutzer