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

Я создал две программы на 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


person Registered User    schedule 04.09.2015    source источник
comment
Пожалуйста, укажите ваши точные флаги командной строки. Например, включена ли вообще отладочная информация в двоичные файлы? Если так, то изменение номеров строк, очевидно, повлияет на это ...   -  person Jon Skeet    schedule 04.09.2015
comment
Согласована ли сумма MD5 в нескольких сборках одного и того же кода?   -  person unenthusiasticuser    schedule 04.09.2015
comment
Я не могу воспроизвести это. Я бы предположил, что это вызвано тем фактом, что GCC встраивает целый набор метаданных в двоичные файлы при их компиляции (включая временные метки). Если бы вы могли добавить использованные вами точные флаги командной строки, это будет полезно.   -  person cyphar    schedule 04.09.2015
comment
@JonSkeet Я отредактировал вопрос, чтобы добавить информацию, которую вы задали   -  person Registered User    schedule 04.09.2015
comment
@unenthusiasticuser да, они совпадают   -  person Registered User    schedule 04.09.2015
comment
Сделано редактирование @cyphar. пожалуйста, прочтите правку.   -  person Registered User    schedule 04.09.2015
comment
Сравните содержимое, а не MD5. Какая именно часть ELF изменилась?   -  person too honest for this site    schedule 04.09.2015
comment
Для ясности вы можете дополнительно изолировать это, разделив компиляцию и компоновку. т.е. скомпилируйте в файлы .o, сравните их, затем свяжите и сравните их. Fwiw, clang не показывает никакой разницы с тем же кодом, параметрами и т. Д. В моей инструментальной цепочке 3.6 (OSX 10.10.5).   -  person WhozCraig    schedule 04.09.2015
comment
Вместо того, чтобы просто проверять суммы MD5 и зависать, используйте hexdump и diff, чтобы точно увидеть, какие байты различаются   -  person M.M    schedule 05.09.2015
comment
Хотя ответ на вопрос, чем отличаются два вывода компилятора? Интересно, я заметил, что вопрос основан на необоснованном предположении: что два вывода должны быть одинаковыми и что нам требуется некоторое объяснение того, почему они разные. Все, что вам обещает компилятор, это то, что когда вы дадите ему легальную программу на C, на выходе будет легальный исполняемый файл, который реализует эту программу. То, что любые два выполнения компилятора производят один и тот же двоичный файл, не является гарантией стандарта C.   -  person Eric Lippert    schedule 05.09.2015
comment
Ознакомьтесь с этой статьей, чтобы узнать, почему выходные данные инструментальной цепочки GCC для одних и тех же входов могут быть разными, а также некоторые методы, помогающие получить детерминированный выход: blog.mindfab.net/2013/12/   -  person Michael Burr    schedule 06.09.2015
comment
@EricLippert: Знаешь, это возможно - и часто очень полезно - для компилятора дать более строгие гарантии, чем абсолютный минимум, требуемый стандартом C ;-)   -  person psmears    schedule 08.09.2015
comment
@psmears: Конечно, это возможно. Я хочу сказать, что вопрос может рассматриваться как имеющий форму, в которой мой компилятор не может реализовать функцию, которую не требуется реализовывать и не документируется как реализация; почему у него нет этой функции? На вопросы о том, почему не была реализована ненужная функция, может быть сложно ответить.   -  person Eric Lippert    schedule 08.09.2015
comment
@EricLippert: Я знаю, что вы имеете в виду в целом, но в данном случае я думаю, что вопрос действительно заслуживает ответа, как с наивной точки зрения (почему, черт возьми, не компилятор даст то же самое вывод, когда задан точно такой же ввод?), но также потому, что для этого есть веские реальные мотивы - как практические (трудноотслеживаемые ошибки, когда поведение зависит от точных байтов в исполняемом файле), так и процедурный (реализация ISO9000 или аналогичных стандартов может потребовать способности точно воспроизводить сборки). Компилятор, который может с этим помочь, - это хорошо :)   -  person psmears    schedule 09.09.2015


Ответы (3)


Это потому, что имена файлов разные (хотя вывод строк такой же). Если вы попытаетесь изменить сам файл (вместо того, чтобы иметь два файла), вы заметите, что выходные двоичные файлы больше не отличаются. Как и Йенс, и я сказали, это потому, что 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
person cyphar    schedule 04.09.2015
comment
РЕДАКТИРОВАТЬ: обновлено, чтобы сказать, что вы можете удалить двоичные файлы, чтобы решить проблему. - person cyphar; 04.09.2015
comment
Вот почему вы должны сравнивать вывод сборки, а не контрольные суммы MD5. - person Lightness Races in Orbit; 04.09.2015
comment
Я задал дополнительный вопрос здесь < / а>. - person Federico Poloni; 05.09.2015
comment
В зависимости от формата объектного файла время компиляции также сохраняется в объектных файлах. Таким образом, использование файлов COFF, например файлов a и a2, не будет идентичным. - person Martin Rosenau; 06.09.2015

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

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

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

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

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

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

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

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