Чому два бінарні програми з лише коментарями не змінюють точно збіг у gcc?


110

Я створив дві програми на С

  1. Програма 1

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

    int main()
    {
    //Some Harmless comments
    }

AFAIK при компілюванні компілятор (gcc) повинен ігнорувати коментарі та надлишкові пробіли, а значить, вихід повинен бути подібним.

Але коли я перевірив md5суми вихідних бінарних файлів, вони не відповідають. Я також спробував компіляції з оптимізацією -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


17
Будь ласка, включіть ваші точні прапорці командного рядка. Наприклад, чи взагалі включена інформація про налагодження у двійкові файли? Якщо так, зміна номерів рядків, очевидно, вплине на це ...
Джон Скіт

4
Чи відповідає сума MD5 у кількох збірках одного і того ж коду?
ентузіастичнийкористувач

3
Я не можу це відтворити. Я б здогадався, що це спричинено тим фактом, що GCC вкладає цілу купу метаданих у двійкові файли під час їх компілювання (включаючи часові позначки). Якщо ви можете додати точні прапорці командного рядка, які ви використовували, це буде корисно.
cyphar

2
Замість того, щоб просто перевірити MD5sums та застрягнути, hexdump та diff, щоб точно побачити, якими байтами відрізняються
MM

12
Хоча відповідь на питання "що відрізняється між двома результатами компілятора?" Цікаво, зауважу, що питання має необґрунтоване припущення: два виходи повинні бути однаковими і нам потрібне пояснення, чому вони різні. Усі компілятори обіцяють вам, що коли ви надаєте йому легальну програму C, вихідний результат є законним виконуваним файлом, який реалізує цю програму. Те, що будь-яке два виконання компілятора виробляє один і той же двійковий файл, не є гарантією стандарту C.
Ерік Ліпперт

Відповіді:


159

Це тому, що назви файлів різні (хоча вихідні рядки однакові). Якщо ви спробуєте змінити сам файл (а не мати два файли), ви помітите, що вихідні бінарні файли вже не відрізняються. Як і Jens, і я сказали, це тому, що 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

EDIT: Оновлено, щоб сказати, що ви можете зняти двійкові файли, щоб "виправити" проблему.
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
І не кажучи вже про те, що ОП заявляє, що "md5sums збігаються в декількох компіляціях з однаковими прапорами", що вказує, що це, ймовірно, не часові позначки, які викликають проблему. Це, мабуть, викликано тим, що вони мають різні назви файлів.
cyphar

1
@cyphar Різні назви файлів також повинні вловлюватися підходами string / diff.
Єнс

15

Примітка : пам’ятайте, що ім'я вихідного файлу переходить у нерозкритий бінарний файл, тому дві програми, що надходять із різних названих вихідних файлів, матимуть різні хеші.

У подібних ситуаціях, якщо вищезазначене не застосовується , ви можете спробувати:

  • бігаючи stripпроти бінарного, щоб видалити трохи жиру. Якщо обрізані бінарні файли однакові, то це були деякі метадані, які не є істотними для роботи програми.
  • генерування проміжного виводу збірки для перевірки того, що різниця не в реальних інструкціях CPU (або, однак, для кращого визначення місця, де різниця є насправді )
  • використовувати stringsабо скидати обидві програми на шістнадцятковий і запускати діф на двох шестигранних скидах. Розмістивши різницю, ви можете спробувати дізнатися, чи є в них якась рима або причина (PID, часові позначки, часова мітка вихідного файлу ...). Наприклад, у вас може бути звичайне зберігання часової позначки під час компіляції для діагностичних цілей.

Моя система є gcc (GCC) 5.2.0іLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Зареєстрований користувач

2
Ви повинні спробувати на насправді зробити два окремих файлів. Я також не міг відтворити його, змінивши один файл.
cyphar

Так, імена файлів є винуватцями. Я можу отримати ті ж md5sums, якщо компілюю програми з такою ж назвою.
Зареєстрований користувач
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.