Почему два двоичных файла программ только с комментариями не изменяются в точности совпадают в gcc?

110

Я создал две программы на C

  1. Программа 1

    int main()
    {
    }
  2. Программа 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK, при компиляции компилятор (gcc) должен игнорировать комментарии и избыточные пробелы, и, следовательно, вывод должен быть аналогичным.

Но когда я проверил md5-суммы выходных двоичных файлов, они не совпадают. Я также попытался компиляции с оптимизацией -O3и , -Ofastно они по- прежнему не совпадают.

Что здесь происходит?

РЕДАКТИРОВАТЬ: точные команды и 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

зарегистрированный пользователь
источник
17
Укажите точные флаги командной строки. Например, включена ли вообще отладочная информация в двоичные файлы? Если так, то изменение номеров строк, очевидно, повлияет на это ...
Джон Скит
4
Согласована ли сумма MD5 для нескольких сборок одного и того же кода?
unenthusiasticuser
3
Я не могу воспроизвести это. Я бы предположил, что это вызвано тем фактом, что GCC встраивает целый набор метаданных в двоичные файлы при их компиляции (включая временные метки). Если бы вы могли добавить точные флаги командной строки, которые вы использовали, это будет полезно.
cyphar
2
Вместо того, чтобы просто проверять суммы MD5 и застревать, используйте hexdump и diff, чтобы точно увидеть, какие байты различаются
ММ
12
Хотя ответ на вопрос "чем отличаются два вывода компилятора?" Интересно, я отмечаю, что вопрос основан на необоснованном предположении: что два вывода должны быть одинаковыми, и что нам нужно некоторое объяснение того, почему они разные. Все, что вам обещает компилятор, это то, что когда вы дадите ему легальную программу на C, на выходе будет легальный исполняемый файл, который реализует эту программу. То, что любые два выполнения компилятора производят один и тот же двоичный файл, не является гарантией стандарта C.
Эрик Липперт

Ответы:

159

Это потому, что имена файлов разные (хотя вывод строк одинаковый). Если вы попытаетесь изменить сам файл (вместо того, чтобы иметь два файла), вы заметите, что выходные двоичные файлы больше не отличаются. Как и Йенс, и я сказали, это потому, что GCC выгружает целую загрузку метаданных в бинарные файлы, которые он строит, включая точное имя исходного файла (и AFAICS так же звенит).

Попробуй это:

$ 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
кифар
источник
РЕДАКТИРОВАТЬ: обновлено, чтобы сказать, что вы можете удалить двоичные файлы, чтобы «исправить» проблему.
cyphar
30
Вот почему вы должны сравнивать вывод сборки, а не контрольные суммы MD5.
Гонки легкости на орбите
1
Я задал дополнительный вопрос здесь .
Федерико Полони
4
В зависимости от формата объектного файла время компиляции также сохраняется в объектных файлах. Таким образом, использование файлов COFF, например файлов a и a2, не будет идентичным.
Мартин Розенау
28

Наиболее частая причина - это имена файлов и отметки времени, добавленные компилятором (обычно в части отладочной информации в разделах ELF).

Попробуйте бежать

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

и вы можете увидеть причину. Однажды я использовал это, чтобы понять, почему один и тот же источник вызывает разный код при компиляции в разных каталогах. Обнаружено, что __FILE__макрос расширяется до абсолютного имени файла, различающегося в обоих деревьях.

Йенс
источник
1
Согласно gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (устаревший, я знаю), они не сохраняют метки времени, и это может быть проблема компоновщика. Хотя я помню, как недавно прочитал историю о том, как охранная фирма профилировала рабочие привычки хакерской команды, используя информацию о временных метках GCC в своих двоичных файлах.
cyphar
3
И не говоря уже о том, что OP заявляет, что «md5sums соответствуют нескольким компиляциям с одинаковыми флагами», что указывает на то, что, вероятно, проблема не в временных метках. Вероятно, это вызвано тем, что это разные имена файлов.
cyphar
1
@cyphar Различные имена файлов также должны быть пойманы подходом strings / diff.
Йенс
15

Примечание : помните, что имя исходного файла входит в распакованный двоичный файл, поэтому две программы, поступающие из исходных файлов с разными именами, будут иметь разные хэши.

В аналогичных ситуациях, если вышеперечисленное не применимо , вы можете попробовать:

  • работает stripс двоичным файлом, чтобы удалить немного жира. Если вырезанные двоичные файлы одинаковы, значит, это были некоторые метаданные, которые не важны для работы программы.
  • генерации сборки промежуточного выходного сигнала , чтобы проверить , что разница не в реальных командах процессора (или, тем не менее, лучше определить , где разница на самом деле является )
  • используйте stringsили выгрузите обе программы в шестнадцатеричный формат и выполните сравнение двух шестнадцатеричных дампа. Обнаружив разницу (я), вы можете попытаться увидеть, есть ли для них какая-то рифма или причина (PID, временные метки, временная метка исходного файла ...). Например, у вас может быть процедура хранения метки времени во время компиляции для диагностических целей.
LSerni
источник
Моя система gcc (GCC) 5.2.0 иLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Зарегистрированный пользователь
2
Вы должны попробовать на самом деле сделать два отдельных файлов. Я тоже не мог воспроизвести это с изменением одного файла.
cyphar
Да, виноваты имена файлов. Я могу получить такие же md5sums, если скомпилирую программы с таким же именем.
Зарегистрированный пользователь