Почему два двоичных файла программ, в которых только комментарии были изменены, не совпадают в gcc?
Я создал две программы на C
Программа 1
int main() { }
Программа 2
int main() { //Some Harmless comments }
AFAIK, при компиляции компилятор (gcc) должен игнорировать комментарии и избыточные пробелы, и, следовательно, выходные данные должны быть похожими.
Но когда я проверил md5sums выходных двоичных файлов, они не совпадают. Я также пытался компилировать с оптимизацией -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
3 ответа
Это потому, что имена файлов различны (хотя вывод строк одинаков). Если вы попытаетесь изменить сам файл (а не иметь два файла), вы заметите, что выходные двоичные файлы больше не отличаются. Как мы с Дженсом говорили, это потому, что 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
Наиболее распространенной причиной являются имена файлов и метки времени, добавленные компилятором (обычно в части с информацией об отладке разделов ELF).
Попробуйте запустить
$ strings -a program > x
...recompile program...
$ strings -a program > y
$ diff x y
и вы можете увидеть причину. Однажды я использовал это, чтобы выяснить, почему один и тот же источник может вызывать разный код при компиляции в разных каталогах. Было установлено, что __FILE__
макрос расширен до абсолютного имени файла, различного в обоих деревьях.
Примечание: помните, что имя исходного файла помещается в двоичныйфайл без разметки, поэтому две программы из исходных файлов с разными именами будут иметь разные хэши.
В подобных ситуациях, если вышеприведенное не применимо, вы можете попробовать:
- Бег
strip
против двоичного файла, чтобы удалить немного жира. Если удаленные двоичные файлы совпадают, то это были некоторые метаданные, которые не важны для работы программы. - генерирование промежуточного вывода сборки для проверки того, что разница не в фактических инструкциях ЦП (или, однако, для более точного определения того, где разница на самом деле)
- использование
strings
или выведите обе программы в шестнадцатеричный формат и запустите diff для двух шестнадцатеричных дампов. Найдя разницу (ы), вы можете попытаться выяснить, есть ли в них какая-то рифма или причина (PID, метки времени, метка времени исходного файла...). Например, у вас может быть подпрограмма, хранящая метку времени во время компиляции для диагностических целей.