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

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

  • Программа 1

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

    int main()
    {
    //Some Harmless comments
    }
    

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, отметки времени, временная метка исходного файла...). Например, у вас может быть обычная временная метка времени компиляции для диагностики.