Tại sao hai tệp nhị phân của chương trình chỉ có nhận xét được thay đổi không khớp chính xác trong gcc?


110

Tôi đã tạo hai chương trình C

  1. Chương trình 1

    int main()
    {
    }
  2. Chương trình 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK, khi biên dịch, trình biên dịch (gcc) nên bỏ qua các chú thích và khoảng trắng thừa, và do đó đầu ra phải tương tự.

Nhưng khi tôi kiểm tra md5sums của các tệp nhị phân đầu ra, chúng không khớp. Tôi cũng đã cố gắng biên soạn với tối ưu hóa -O3-Ofastnhưng họ vẫn không tìm thấy.

Chuyện gì đang xảy ra ở đây?

CHỈNH SỬA: các lệnh chính xác và có md5sums (t1.c là chương trình 1 và t2.c là chương trình 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

Và có, md5sums khớp với nhiều bộ sưu tập có cùng cờ.

BTW hệ thống của tôi là gcc (GCC) 5.2.0Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux


17
Vui lòng bao gồm cờ dòng lệnh chính xác của bạn. Ví dụ, thông tin gỡ lỗi có được bao gồm trong các tệp nhị phân không? Nếu vậy, số dòng thay đổi sẽ ảnh hưởng rõ ràng là nó ...
Jon Skeet

4
Tổng MD5 có nhất quán trên nhiều bản dựng của cùng một mã không?
không nhiệt tình

3
Tôi không thể tái tạo điều này. Tôi đoán rằng điều này là do GCC nhúng toàn bộ siêu dữ liệu vào các tệp nhị phân khi biên dịch chúng (bao gồm cả dấu thời gian). Nếu bạn có thể thêm các cờ dòng lệnh chính xác mà bạn đã sử dụng, điều đó sẽ hữu ích.
cyphar

2
Thay vì chỉ kiểm tra MD5sums và gặp khó khăn, hãy hexdump và khác nhau để xem chính xác các byte nào khác nhau
MM

12
Mặc dù câu trả lời cho câu hỏi "sự khác biệt giữa hai đầu ra của trình biên dịch là gì?" thật thú vị, tôi lưu ý rằng câu hỏi có một giả định không chính đáng: rằng hai đầu ra phải giống nhau và chúng tôi yêu cầu một số lời giải thích tại sao chúng khác nhau. Tất cả những gì mà trình biên dịch hứa hẹn với bạn là khi bạn cung cấp cho nó một chương trình C hợp pháp, đầu ra là một tệp thực thi hợp pháp thực hiện chương trình đó. Việc hai lần thực thi bất kỳ của trình biên dịch tạo ra cùng một tệp nhị phân không phải là đảm bảo cho tiêu chuẩn C.
Eric Lippert

Câu trả lời:


159

Đó là vì các tên tệp khác nhau (mặc dù đầu ra của các chuỗi giống nhau). Nếu bạn thử sửa đổi chính tệp (thay vì có hai tệp), bạn sẽ nhận thấy rằng các tệp nhị phân đầu ra không còn khác biệt nữa. Như cả Jens và tôi đã nói, đó là do GCC đổ toàn bộ siêu dữ liệu vào các tệp nhị phân mà nó xây dựng, bao gồm tên tệp nguồn chính xác (và AFAICS cũng vậy).

Thử cái này:

$ 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

Điều này giải thích tại sao md5sums của bạn không thay đổi giữa các bản dựng, nhưng chúng khác nhau giữa các tệp khác nhau. Nếu muốn, bạn có thể thực hiện những gì Jens đề xuất và so sánh kết quả đầu ra của stringsmỗi tệp nhị phân, bạn sẽ nhận thấy rằng các tên tệp được nhúng trong tệp nhị phân. Nếu bạn muốn "sửa" điều này, bạn có thể stripmã nhị phân và siêu dữ liệu sẽ bị xóa:

$ 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

CHỈNH SỬA: Đã cập nhật để nói rằng bạn có thể tách các tệp nhị phân để "khắc phục" sự cố.
cyphar

30
Và đây là lý do tại sao bạn nên so sánh đầu ra lắp ráp, không phải tổng kiểm tra MD5.
Các cuộc đua ánh sáng trong quỹ đạo

1
Tôi đã hỏi một câu hỏi tiếp theo ở đây .
Federico Poloni

4
Tùy thuộc vào định dạng tệp đối tượng mà thời gian biên dịch cũng được lưu trữ trong tệp đối tượng. Vì vậy, sử dụng tệp COFF ví dụ tệp a và a2 sẽ không giống nhau.
Martin Rosenau

28

Lý do phổ biến nhất là tên tệp và tem thời gian được trình biên dịch thêm vào (thường là trong phần thông tin gỡ lỗi của phần ELF).

Thử chạy

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

và bạn có thể thấy lý do. Tôi đã từng sử dụng điều này để tìm lý do tại sao cùng một nguồn sẽ tạo ra mã khác nhau khi được biên dịch trong các thư mục khác nhau. Phát hiện là __FILE__macro mở rộng thành tên tệp tuyệt đối , khác nhau ở cả hai cây.


1
Theo gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (lỗi thời, tôi biết), họ không lưu dấu thời gian và đó có thể là sự cố của trình liên kết. Mặc dù, tôi nhớ đã đọc một câu chuyện gần đây về cách một công ty bảo mật lập hồ sơ thói quen làm việc của một nhóm hack bằng cách sử dụng thông tin dấu thời gian GCC trong tệp nhị phân của họ.
cyphar

3
Và không đề cập đến việc OP nói rằng "md5sums khớp trên nhiều bộ tổng hợp có cùng cờ", điều này cho thấy nó có thể không phải là dấu thời gian gây ra sự cố. Có thể nguyên nhân là do chúng có tên tệp khác nhau.
cyphar

1
@cyphar Các tên tệp khác nhau cũng phải được truy cập theo chuỗi / khác biệt.
Jens

15

Lưu ý : hãy nhớ rằng tên tệp nguồn đi vào tệp nhị phân chưa được mã hóa, vì vậy hai chương trình đến từ các tệp nguồn được đặt tên khác nhau sẽ có các hàm băm khác nhau.

Trong các tình huống tương tự, nếu những điều trên không áp dụng , bạn có thể thử:

  • chạy stripngược lại nhị phân để loại bỏ một số chất béo. Nếu các tệp nhị phân đã loại bỏ là giống nhau thì đó là một số siêu dữ liệu không cần thiết cho hoạt động của chương trình.
  • tạo ra một đầu ra trung gian lắp ráp để xác minh rằng sự khác biệt không nằm trong hướng dẫn CPU thực tế (hoặc, tuy nhiên, để xác định chính xác hơn sự khác biệt thực sự là ở đâu )
  • sử dụng stringshoặc kết xuất cả hai chương trình thành hex và chạy một khác biệt trên hai kết xuất hex. Sau khi xác định được (các) sự khác biệt, bạn có thể thử và xem liệu có một số vần điệu hoặc lý do cho chúng (PID, dấu thời gian, dấu thời gian tệp nguồn ...). Ví dụ: bạn có thể có một thói quen lưu trữ dấu thời gian tại thời điểm biên dịch cho mục đích chẩn đoán.

Hệ thống của tôi là gcc (GCC) 5.2.0Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Người dùng đã đăng ký

2
Bạn nên cố gắng thực sự làm cho hai tập tin riêng biệt. Tôi cũng không thể tái tạo nó bằng cách sửa đổi một tệp duy nhất.
cyphar

Có, tên tệp là thủ phạm. Tôi có thể nhận được cùng md5sums nếu tôi biên dịch các chương trình có cùng tên.
Người dùng đã đăng ký
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.