Dlaczego dwa pliki binarne programów ze zmienionymi tylko komentarzami nie zgadzają się dokładnie w gcc?


110

Stworzyłem dwa programy w C.

  1. Program 1

    int main()
    {
    }
  2. Program 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK, podczas kompilacji kompilator (gcc) powinien ignorować komentarze i zbędne białe znaki, a zatem dane wyjściowe muszą być podobne.

Ale kiedy sprawdziłem sumy md5 wyjściowych plików binarnych, nie pasują. Próbowałem też kompilować z optymalizacją -O3i-Ofast jednak wciąż nie pasuje.

Co tu się dzieje?

EDYCJA: dokładne polecenia i sumy md5 to (t1.c to program 1, a t2.c to program 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

I tak, sumy md5 pasują do wielu kompilacji z tymi samymi flagami.

Swoją drogą mój system to gcc (GCC) 5.2.0iLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux


17
Podaj dokładne flagi linii poleceń. Na przykład, czy informacje debugowania są w ogóle zawarte w plikach binarnych? Jeśli tak, zmiana numerów linii oczywiście wpłynęłaby na to ...
Jon Skeet

4
Czy suma MD5 jest spójna w wielu kompilacjach tego samego kodu?
unenthusiasticuser

3
Nie mogę tego odtworzyć. Domyślam się, że jest to spowodowane faktem, że GCC osadza całą masę metadanych w plikach binarnych podczas ich kompilacji (w tym sygnatury czasowe). Gdybyś mógł dodać dokładne flagi linii poleceń, których użyłeś, będzie to przydatne.
cyphar

2
Zamiast tylko sprawdzać sumy MD5 i utknąć, wykonaj zrzut heksowy i różnicę, aby zobaczyć dokładnie, które bajty się różnią
MM

12
Chociaż odpowiedź na pytanie "czym różni się między dwoma wyjściami kompilatora?" jest interesujące, zauważam, że pytanie ma nieuzasadnione założenie: że dwa wyjścia powinny być takie same i że potrzebujemy wyjaśnienia, dlaczego są różne. Wszystko, co obiecuje kompilator, to to, że kiedy dasz mu legalny program w C, wynikiem jest legalny plik wykonywalny, który implementuje ten program. To, że dowolne dwa wykonania kompilatora dadzą ten sam plik binarny, nie jest gwarancją standardu C.
Eric Lippert,

Odpowiedzi:


159

Dzieje się tak, ponieważ nazwy plików są różne (chociaż dane wyjściowe są takie same). Jeśli spróbujesz zmodyfikować sam plik (zamiast mieć dwa pliki), zauważysz, że wyjściowe pliki binarne nie są już różne. Jak ja i Jens powiedzieliśmy, dzieje się tak dlatego, że GCC zrzuca cały ładunek metadanych do tworzonych plików binarnych, w tym dokładną nazwę pliku źródłowego dzieje się tak tworzonych (i AFAICS tak robi).

Spróbuj tego:

$ 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

To wyjaśnia, dlaczego sumy md5 nie zmieniają się między kompilacjami, ale są różne w różnych plikach. Jeśli chcesz, możesz zrobić to, co zasugerował Jens i porównać wynik stringskażdego pliku binarnego, a zauważysz, że nazwy plików są osadzone w pliku binarnym. Jeśli chcesz to „naprawić”, możesz stripusunąć pliki binarne i metadane:

$ 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

EDYCJA: Zaktualizowano, aby powiedzieć, że można usunąć pliki binarne, aby „naprawić” problem.
cyphar

30
I dlatego powinieneś porównywać dane wyjściowe zespołu, a nie sumy kontrolne MD5.
Wyścigi lekkości na orbicie

1
Zadałem tutaj dodatkowe pytanie .
Federico Poloni

4
W zależności od formatu pliku obiektowego czas kompilacji jest również przechowywany w plikach obiektowych. Więc użycie plików COFF na przykład plików a i a2 nie byłoby identyczne.
Martin Rosenau,

28

Najczęstszym powodem są nazwy plików i znaczniki czasu dodane przez kompilator (zwykle w części informacji o debugowaniu sekcji ELF).

Spróbuj biegać

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

i możesz zobaczyć powód. Kiedyś użyłem tego, aby dowiedzieć się, dlaczego to samo źródło powodowałoby inny kod, gdy był kompilowany w różnych katalogach. Okazało się, że __FILE__makro rozwinęło się do bezwzględnej nazwy pliku, różnej w obu drzewach.


1
Według gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (nieaktualne, wiem), nie zapisują one sygnatur czasowych i może to być problem z linkerem. Chociaż pamiętam, jak niedawno czytałem historię o tym, jak firma ochroniarska sprofilowała nawyki pracy zespołu hakerskiego, używając informacji o sygnaturze czasowej GCC w swoich plikach binarnych.
cyphar

3
Nie wspominając o tym, że OP stwierdza, że ​​„sumy md5 pasują do wielu kompilacji z tymi samymi flagami”, co oznacza, że ​​prawdopodobnie nie są to sygnatury czasowe, które powodują problem. Jest to prawdopodobnie spowodowane faktem, że są to różne nazwy plików.
cyphar

1
@cyphar Różne nazwy plików powinny być również przechwytywane przez podejście typu string / diff.
Jens

15

Uwaga : pamiętaj, że nazwa pliku źródłowego trafia do pliku binarnego bez ściągnięcia, więc dwa programy pochodzące z plików źródłowych o różnych nazwach będą miały różne skróty.

W podobnych sytuacjach, jeśli powyższe nie ma zastosowania , możesz spróbować:

  • działa stripprzeciwko układowi podwójnemu, aby usunąć trochę tłuszczu. Jeśli usunięte pliki binarne są takie same, to niektóre metadane nie są niezbędne do działania programu.
  • generowanie pośredniego wyjścia zespołu w celu sprawdzenia, czy różnica nie występuje w rzeczywistych instrukcjach procesora (lub jednak, aby lepiej określić, gdzie faktycznie jest różnica )
  • użyj stringslub zrzuć oba programy do szesnastkowego i uruchom różnicę na dwóch zrzutach szesnastkowych. Po zlokalizowaniu różnic, możesz spróbować sprawdzić, czy jest jakiś rym lub powód (PID, znaczniki czasu, znacznik czasu pliku źródłowego ...). Na przykład możesz mieć procedurę przechowywania sygnatury czasowej w czasie kompilacji do celów diagnostycznych.

Mój system to gcc (GCC) 5.2.0iLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Zarejestrowany użytkownik

2
Należy starać faktycznie tworząc dwa oddzielne pliki. Nie mogłem go również odtworzyć, modyfikując pojedynczy plik.
cyphar

Tak, winowajcą są nazwy plików. Mogę uzyskać te same sumy md5, jeśli skompiluję programy o tej samej nazwie.
Zarejestrowany użytkownik
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.