Почему в gcc не совпадают два бинарных файла программ с только комментариями?
Я создал две программы C
AFAIK, при компиляции, компилятор (gcc) должен игнорировать комментарии и избыточные белые области, и, следовательно, вывод должен быть схожим.
Но когда я проверил md5sums выходных двоичных файлов, они не совпадают. Я также попытался скомпилировать с оптимизацией -O3
и -Ofast
, но они все еще не совпадали.
Что здесь происходит?
EDIT:
точные команды и там md5sums (t1.c - это программа 1 и t2.c - это программа 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
И да, md5sums соответствуют нескольким компиляциям с одинаковыми флагами.
Кстати, моя система gcc (GCC) 5.2.0
и Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Ответы
Ответ 1
Это потому, что имена файлов разные (хотя вывод строк одинаковый). Если вы попытаетесь изменить сам файл (вместо двух файлов), вы заметите, что выходные двоичные файлы больше не отличаются друг от друга. Поскольку оба Jens и я сказали, это потому, что GCC сбрасывает всю загрузку метаданных в бинарные файлы, которые он строит, включая точное исходное имя файла (и AFAICS так делает clang).
Попробуйте следующее:
$ 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
Это объясняет, почему ваши md5sums не меняются между сборками, но они разные между разными файлами. Если вы хотите, вы можете сделать то, что предложил Йенс и сравнить результат strings
для каждого двоичного кода, вы заметите, что имена файлов встроены в двоичный файл. Если вы хотите "исправить" это, вы можете strip
двоичные файлы и метаданные будут удалены:
$ 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
Ответ 2
Наиболее распространенной причиной являются имена файлов и метки времени, добавленные компилятором (обычно в части информации об отладке в разделах ELF).
Попробуйте запустить
$ strings -a program > x
...recompile program...
$ strings -a program > y
$ diff x y
и вы можете увидеть причину. Я когда-то использовал это, чтобы выяснить, почему один и тот же источник может вызвать другой код при компиляции в разных каталогах. Вывод состоял в том, что макрос __FILE__
был расширен до абсолютного имени файла, различного для обоих деревьев.
Ответ 3
Примечание: помните, что имя исходного файла переходит в незастроенный двоичный файл, поэтому две программы, поступающие из файлов с именованными именами, будут иметь разные хэши.
В подобных ситуациях, если выше не применяется, вы можете попробовать:
- работает
strip
против двоичного файла, чтобы удалить некоторый жир. Если разделенные двоичные файлы одинаковы, то это были некоторые метаданные, которые не являются существенными для работы программы.
- создание промежуточного выхода сборки для проверки того, что разница не находится в действительных инструкциях ЦП (или, тем не менее, лучше определить, где именно находится разница)
- используйте
strings
или выгрузите обе программы в шестнадцатеричные и запустите diff на двух шестнадцатеричных дампах. После того, как вы разместите разницу (-ы), вы можете попробовать и посмотреть, есть ли для них какие-то рифмы или причины (PID, отметки времени, временная метка исходного файла...). Например, у вас может быть обычная временная метка времени компиляции для диагностики.