Mengapa dua binari program dengan hanya komentar diubah sama persis di gcc?

110

Saya membuat dua program C.

  1. Program 1

    int main()
    {
    }
    
  2. Program 2

    int main()
    {
    //Some Harmless comments
    }
    

AFAIK, saat mengkompilasi, kompilator (gcc) harus mengabaikan komentar dan whitepaces yang berlebihan, dan karenanya keluarannya harus serupa.

Tetapi ketika saya memeriksa md5sums dari binari keluaran, mereka tidak cocok. Saya juga mencoba kompilasi dengan optimasi -O3dan -Ofastnamun mereka masih tidak cocok.

Apa yang terjadi disini?

EDIT: perintah yang tepat dan ada md5sums (t1.c adalah program 1 dan t2.c adalah program 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

Dan ya, md5sums cocok di beberapa kompilasi dengan flag yang sama.

BTW sistem saya gcc (GCC) 5.2.0danLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux

Pengguna Terdaftar
sumber
17
Harap sertakan panji baris perintah persis Anda. Misalnya, apakah informasi debug sudah termasuk dalam biner? Jika demikian, perubahan nomor baris jelas akan mempengaruhinya ...
Jon Skeet
4
Apakah jumlah MD5 konsisten di beberapa build dengan kode yang sama?
tidak antusias
3
Saya tidak bisa mereproduksi ini. Saya akan menebak bahwa ini disebabkan oleh fakta bahwa GCC menyematkan sejumlah besar metadata ke dalam biner saat menyusunnya (termasuk cap waktu). Jika Anda bisa menambahkan tepat bendera baris perintah yang digunakan, yang akan berguna.
cyphar
2
Alih-alih hanya memeriksa MD5sums dan macet, hexdump dan diff untuk melihat dengan tepat byte mana yang berbeda
MM
12
Meskipun jawaban atas pertanyaan "apa yang berbeda antara dua keluaran kompilator?" Menarik, saya perhatikan bahwa pertanyaan tersebut memiliki asumsi yang tidak beralasan: bahwa kedua keluaran harus sama dan bahwa kami memerlukan penjelasan mengapa keduanya berbeda. Semua kompilator menjanjikan kepada Anda bahwa ketika Anda memberikan program C legal, hasilnya adalah eksekusi legal yang mengimplementasikan program itu. Bahwa dua eksekusi kompilator menghasilkan biner yang sama bukanlah jaminan standar C.
Eric Lippert

Jawaban:

159

Itu karena nama file berbeda (meskipun output stringnya sama). Jika Anda mencoba memodifikasi file itu sendiri (daripada memiliki dua file), Anda akan melihat bahwa binari keluaran tidak lagi berbeda. Seperti yang saya dan Jens katakan, itu karena GCC membuang seluruh muatan metadata ke dalam biner yang dibuatnya, termasuk nama file sumber yang tepat (dan AFAICS begitu pula dentang).

Coba ini:

$ 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

Ini menjelaskan mengapa md5sums Anda tidak berubah di antara build, tetapi berbeda di antara file yang berbeda. Jika Anda mau, Anda dapat melakukan apa yang disarankan Jens dan membandingkan output stringsuntuk setiap biner Anda akan melihat bahwa nama file disematkan dalam biner. Jika Anda ingin "memperbaiki" ini, Anda dapat stripbinari dan metadata akan dihapus:

$ 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
sumber
EDIT: Diperbarui untuk mengatakan bahwa Anda dapat menghapus binari untuk "memperbaiki" masalah.
cyphar
30
Dan inilah mengapa Anda harus membandingkan keluaran rakitan, bukan checksum MD5.
Balapan Ringan di Orbit
1
Saya telah mengajukan pertanyaan lanjutan di sini .
Federico Poloni
4
Bergantung pada format file objek, waktu kompilasi juga disimpan dalam file objek. Jadi menggunakan file COFF sebagai contoh file a dan a2 tidak akan sama.
Martin Rosenau
28

Alasan paling umum adalah nama file dan stempel waktu yang ditambahkan oleh kompiler (biasanya di bagian info debug di bagian ELF).

Coba lari

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

dan Anda mungkin melihat alasannya. Saya pernah menggunakan ini untuk menemukan mengapa sumber yang sama akan menyebabkan kode yang berbeda ketika dikompilasi di direktori yang berbeda. Temuannya adalah bahwa __FILE__makro diperluas ke nama file absolut , berbeda di kedua hierarki.

Jens
sumber
1
Menurut gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (usang, saya tahu) mereka tidak menyimpan stempel waktu dan itu mungkin masalah linker. Meskipun, saya ingat membaca sebuah cerita baru-baru ini tentang bagaimana sebuah perusahaan keamanan membuat profil kebiasaan kerja tim peretasan menggunakan informasi stempel waktu GCC di biner mereka.
cyphar
3
Dan belum lagi OP menyatakan bahwa "md5sums cocok di beberapa kompilasi dengan flag yang sama" yang menunjukkan mungkin bukan stempel waktu yang menyebabkan masalah. Ini mungkin disebabkan oleh fakta bahwa nama file mereka berbeda.
cyphar
1
@cyphar Nama file yang berbeda harus ditangkap dengan pendekatan string / diff juga.
Jens
15

Catatan : ingat bahwa nama file sumber dimasukkan ke dalam biner yang tidak dilipat, jadi dua program yang berasal dari file sumber dengan nama berbeda akan memiliki hash yang berbeda.

Dalam situasi serupa, jika hal di atas tidak berlaku , Anda dapat mencoba:

  • berjalan stripmelawan biner untuk menghilangkan lemak. Jika binari yang dilucuti sama maka itu adalah beberapa metadata yang tidak penting untuk operasi program.
  • menghasilkan output perakitan menengah untuk memverifikasi bahwa perbedaan itu tidak dalam petunjuk CPU yang sebenarnya (atau, namun, untuk pinpoint lebih baik di mana perbedaan sebenarnya adalah )
  • gunakan strings, atau buang kedua program ke hex dan jalankan diff pada dua hex dump. Setelah menemukan perbedaannya, Anda dapat mencoba dan melihat apakah ada rima atau alasan bagi mereka (PID, stempel waktu, stempel waktu file sumber ...). Misalnya Anda mungkin memiliki rutinitas menyimpan stempel waktu pada waktu kompilasi untuk tujuan diagnostik.
LSerni
sumber
Sistem saya gcc (GCC) 5.2.0danLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Pengguna Terdaftar
2
Anda harus mencoba benar-benar membuat dua file terpisah. Saya tidak bisa mereproduksinya dengan memodifikasi satu file juga.
cyphar
Ya, nama file adalah pelakunya. Saya bisa mendapatkan md5sums yang sama jika saya mengkompilasi program dengan nama yang sama.
Pengguna Terdaftar