Việc biên dịch lại một chương trình có tạo ra một nhị phân giống hệt bit-bit không?


24

Nếu tôi biên dịch một chương trình thành một nhị phân đơn, tạo một tổng kiểm tra, sau đó biên dịch lại nó trên cùng một máy với cùng cài đặt trình biên dịch và trình biên dịch và kiểm tra lại chương trình được biên dịch lại, liệu tổng kiểm tra có thất bại không?

Nếu vậy, tại sao lại thế này? Nếu không, việc có một CPU khác dẫn đến nhị phân không giống nhau?


8
Nó phụ thuộc vào trình biên dịch. Một số trong số chúng nhúng tem thời gian, vì vậy câu trả lời là "không" cho những cái đó.
ta.speot.is

Trên thực tế, nó phụ thuộc vào định dạng thực thi , không phải trình biên dịch. Một số định dạng thực thi như định dạng PE của Windows bao gồm dấu thời gian được chạm vào ngày và ngày biên dịch, trong khi các định dạng khác như định dạng ELF của Linux thì không. Dù bằng cách nào, câu hỏi này xoay quanh định nghĩa về nhị phân giống hệt nhau. Bản thân hình ảnh sẽ / nên giống hệt nhau theo bit nếu cùng một tệp nguồn được biên dịch với cùng trình biên dịch và thư viện và các công tắc và mọi thứ, nhưng tiêu đề và siêu dữ liệu khác có thể khác nhau.
Synetech 27/12/13

Câu trả lời:


19
  1. Biên dịch cùng một chương trình với cùng các cài đặt trên cùng một máy:

    Mặc dù câu trả lời dứt khoát là "nó phụ thuộc", thật hợp lý khi hy vọng rằng hầu hết các trình biên dịch sẽ có tính xác định trong hầu hết thời gian và các nhị phân được tạo ra phải giống hệt nhau. Thật vậy, một số hệ thống kiểm soát phiên bản phụ thuộc vào điều này. Tuy nhiên, luôn có những trường hợp ngoại lệ; hoàn toàn có khả năng một số trình biên dịch ở đâu đó sẽ quyết định chèn dấu thời gian hoặc một số thứ tương tự (ví dụ: iirc, Delphi chẳng hạn). Hoặc chính quá trình xây dựng có thể làm điều đó; Tôi đã thấy các tệp tạo tệp cho các chương trình C đặt macro tiền xử lý thành dấu thời gian hiện tại. (Tuy nhiên, tôi đoán đó sẽ được coi là một cài đặt trình biên dịch khác.)

    Ngoài ra, hãy lưu ý rằng nếu bạn liên kết tĩnh nhị phân, thì bạn đang kết hợp hiệu quả trạng thái của tất cả các thư viện có liên quan trên máy của mình và mọi thay đổi trong bất kỳ một trong số đó cũng sẽ ảnh hưởng đến nhị phân của bạn. Vì vậy, nó không chỉ là cài đặt trình biên dịch có liên quan.

  2. Biên dịch cùng một chương trình trên một máy khác với CPU khác.

    Ở đây, tất cả các cược đã tắt. Hầu hết các trình biên dịch hiện đại có khả năng thực hiện tối ưu hóa mục tiêu cụ thể; nếu tùy chọn này được bật, thì các nhị phân có thể sẽ khác đi trừ khi các CPU tương tự nhau (và thậm chí sau đó, điều đó là có thể). Ngoài ra, hãy xem lưu ý ở trên về liên kết tĩnh: môi trường cấu hình vượt xa các cài đặt trình biên dịch. Trừ khi bạn có kiểm soát cấu hình rất nghiêm ngặt, rất có thể sẽ có thứ gì đó khác nhau giữa hai máy.


1
Giả sử tôi đang sử dụng GCC và tôi đã không sử dụng tùy chọn diễu hành (tùy chọn tối ưu hóa nhị phân cho một họ CPU cụ thể) và tôi đã biên dịch nhị phân với một CPU, và sau đó với CPU khác sẽ có Sự khác biệt?
David

1
@David: Nó vẫn còn phụ thuộc. Đầu tiên, các thư viện bạn liên kết đến có thể có các bản dựng dành riêng cho kiến ​​trúc. Vì vậy, đầu ra của gcc -ccó thể giống hệt nhau, nhưng các phiên bản được liên kết khác nhau. Ngoài ra, nó không chỉ -march; cũng có -mtune/-mcpu-mfpmatch(và có thể những người khác). Một số trong số này có thể có các giá trị mặc định khác nhau trên các cài đặt khác nhau, vì vậy bạn có thể cần phải buộc trường hợp xấu nhất có thể xảy ra đối với các máy của mình một cách rõ ràng; làm như vậy có thể làm giảm đáng kể hiệu suất, đặc biệt nếu bạn trở lại i386 mà không có sse. Và, tất nhiên, nếu một trong những
cpus

1
Ngoài ra, GCC có phải là một trong những trình biên dịch được đề cập có thêm dấu thời gian vào tệp nhị phân không?
David

@david: afaik, không.
rici

8

Những gì bạn đang hỏi là "là đầu ra xác định ." Nếu bạn biên dịch chương trình một lần, ngay lập tức biên dịch lại, có thể bạn sẽ kết thúc với cùng một tệp đầu ra. Tuy nhiên, nếu có bất cứ điều gì thay đổi - ngay cả một thay đổi nhỏ - đặc biệt là trong một thành phần mà chương trình đã biên dịch sử dụng, thì đầu ra của trình biên dịch cũng có thể thay đổi.


2
Điểm rất tốt thực sự. Bài viết này có một số quan sát rất thú vị. Cụ thể, việc biên dịch với GCC có thể không mang tính quyết định đối với các yếu tố đầu vào trong một số trường hợp nhất định, ví dụ như cách nó mang hàm hoạt động trong các không gian tên ẩn danh, trong đó nó sử dụng một trình tạo số ngẫu nhiên trong nội bộ. Để có được tính xác định trong trường hợp cụ thể này, hãy cung cấp một hạt giống ngẫu nhiên ban đầu bằng cách chỉ định tùy chọn -frandom-seed=string.
ack

7

Việc biên dịch lại một chương trình có tạo ra một nhị phân giống hệt bit-bit không?

Đối với tất cả các trình biên dịch? Không. Trình biên dịch C #, ít nhất, không được phép.

Eric Lippert đã phân tích rất kỹ về lý do tại sao đầu ra của trình biên dịch không mang tính quyết định .

[T] anh trình biên dịch C # theo thiết kế không bao giờ tạo ra cùng một nhị phân hai lần. Trình biên dịch C # nhúng GUID mới được tạo trong mỗi hội đồng, mỗi khi bạn chạy nó, do đó đảm bảo rằng không có hai hội đồng nào giống nhau từng bit một. Để trích dẫn từ đặc tả CLI:

Cột Mvid sẽ lập chỉ mục GUID duy nhất [...] xác định phiên bản này của mô-đun. [...] Mvid nên được tạo mới cho mọi mô-đun [...] Mặc dù [thời gian chạy] không sử dụng Mvid, các công cụ khác (như trình gỡ lỗi [...]) dựa vào thực tế là Mvid hầu như luôn luôn khác nhau từ mô-đun này sang mô-đun khác.

Mặc dù nó dành riêng cho một phiên bản của trình biên dịch C #, nhiều điểm trong bài viết có thể được áp dụng cho bất kỳ trình biên dịch nào .

Trước hết, chúng tôi giả định rằng chúng tôi luôn nhận được cùng một danh sách các tệp mỗi lần, theo cùng một thứ tự. Nhưng đó là trong một số trường hợp cho đến hệ điều hành. Khi bạn nói "csc * .cs", thứ tự mà hệ điều hành cung cấp danh sách các tệp phù hợp là một chi tiết triển khai của hệ điều hành; trình biên dịch không sắp xếp danh sách đó thành một thứ tự chính tắc.


Không khó để tạo bản sao được xây dựng (ngoài một số trường dễ bị loại bỏ như thời gian biên dịch và GUID lắp ráp). Ví dụ: sắp xếp các tệp đầu vào thành một thứ tự chính tắc là một lớp lót. Thậm chí GUID đó có thể là một hàm băm của phần còn lại của hội đồng thay vì mới được tạo.
CodeInChaos

Tôi giả sử bạn có nghĩa là trình biên dịch Microsoft C #, hoặc nó là một yêu cầu của đặc tả?
David

@David Thông số CLI yêu cầu nó. Trình biên dịch C # của Mono sẽ phải làm như vậy. Ditto cho bất kỳ trình biên dịch VB .NET.
ta.speot.is

4
Tiêu chuẩn ECMA không phải có dấu thời gian hoặc sự khác biệt MVID. Không có những cái đó, ít nhất có thể có các nhị phân giống hệt nhau trong C #. Do đó, lý do chính là một quyết định thiết kế đáng ngờ và không phải là một hạn chế kỹ thuật thực sự.
Shiv

7
  • -frandom-seed=123kiểm soát một số ngẫu nhiên nội bộ GCC. man gccnói:

    Tùy chọn này cung cấp một hạt giống mà GCC sử dụng thay cho các số ngẫu nhiên trong việc tạo các tên ký hiệu nhất định phải khác nhau trong mỗi tệp được biên dịch. Nó cũng được sử dụng để đặt tem độc đáo trong các tệp dữ liệu bảo hiểm và các tệp đối tượng tạo ra chúng. Bạn có thể sử dụng tùy chọn -frandom-seed để tạo các tệp đối tượng giống hệt nhau.

  • __FILE__: đặt nguồn vào một thư mục cố định (ví dụ /tmp/build)

  • cho __DATE__, __TIME__, __TIMESTAMP__:
    • libfaketime: https://github.com/wolfcw/libfaketime
    • ghi đè các macro đó bằng -D
    • -Wdate-timehoặc -Werror=date-time: cảnh báo hay thất bại nếu một trong hai __TIME__, __DATE__hoặc __TIMESTAMP__là được sử dụng. Linux kernel 4.4 sử dụng nó theo mặc định.
  • sử dụng Dcờ với arhoặc sử dụng https://github.com/nh2/ar-timestamp-wiper/tree/master để xóa tem
  • -fno-guess-branch-probability: các phiên bản thủ công cũ hơn nói rằng đó là một nguồn không xác định, nhưng không còn nữa . Không chắc chắn nếu điều này được bao phủ bởi -frandom-seedhay không.

Dự án xây dựng lại bản sao Debian cố gắng chuẩn hóa các gói Debian theo từng byte và gần đây đã nhận được một khoản tài trợ Linux Foundation . Điều đó bao gồm nhiều hơn là chỉ biên dịch, nhưng nó nên được quan tâm.

Buildroot có một BR2_REPRODUCIBLEtùy chọn có thể đưa ra một số ý tưởng ở cấp độ gói, nhưng nó vẫn chưa hoàn thành vào thời điểm này.

Chủ đề liên quan:


2

Tôi nói KHÔNG, nó không mang tính quyết định 100%. Trước đây tôi đã làm việc với một phiên bản GCC để tạo các nhị phân đích cho bộ xử lý Hitachi H8.

Nó không phải là một vấn đề với dấu thời gian. Ngay cả khi vấn đề về dấu thời gian bị bỏ qua, kiến ​​trúc bộ xử lý cụ thể có thể cho phép cùng một lệnh được mã hóa theo 2 cách hơi khác nhau trong đó một số bit có thể là 1 hoặc 0. Kinh nghiệm trước đây của tôi cho thấy các nhị phân được tạo ra là cùng MOST của thời gian nhưng đôi khi gcc sẽ tạo ra các nhị phân có kích thước giống hệt nhau nhưng một số byte chỉ khác nhau 1 bit, ví dụ 0XE0 trở thành 0XE1.


Và điều đó có dẫn đến hành vi khác nhau hoặc "vấn đề nghiêm trọng" không?
Florian Straub

2

Dự án https://reproducible-builds.org/ là tất cả về điều này, và đang cố gắng hết sức để đưa ra câu trả lời cho câu hỏi của bạn "không, chúng sẽ không khác nhau" ở càng nhiều nơi càng tốt. NixOS và Debian hiện có khả năng tái tạo hơn 90% cho các gói của họ.

Nếu bạn biên dịch nhị phân và tôi biên dịch nhị phân, và chúng giống hệt nhau từng bit, thì tôi có thể yên tâm rằng mã nguồn và các công cụ là yếu tố quyết định đầu ra và bạn không lén lút mã trojan trên đường đi.

Nếu chúng ta kết hợp khả năng tái tạo với khả năng khởi động từ nguồn có thể đọc được của con người, vì http://bootstrappable.org/ đang thực hiện, chúng ta sẽ có một hệ thống được xác định từ nguồn gốc có thể đọc được từ con người, và chỉ khi đó chúng ta mới ở điểm chúng ta có thể tin tưởng rằng chúng ta biết hệ thống đang làm gì.


1
Liên kết mát mẻ. Tôi là một fanboy của Buildroot, nhưng nếu ai đó cho tôi một thiết lập vòm chéo Nix ARM khởi động trên QEMU, tôi sẽ rất vui :-)
Ciro Santilli 改造 心 心 996ICU

Tôi đã không đề cập đến Guix vì tôi không biết tìm số của họ ở đâu, nhưng họ đã ở trước NixOS trên chuyến tàu tái tạo với công cụ xác minh và vì vậy, tôi chắc chắn rằng họ đang ở vị trí ngang bằng hoặc tốt hơn.
clacke

1

Nói chung, không. Các trình biên dịch hợp lý nhất sẽ bao gồm thời gian biên dịch trong mô đun đối tượng. Ngay cả khi bạn đã thiết lập lại đồng hồ, bạn phải rất chính xác về thời điểm bạn khởi động trình biên dịch (và sau đó hy vọng rằng các truy cập đĩa, v.v., có cùng tốc độ như trước).

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.