Erzeugt das Neukompilieren eines Programms eine bitweise identische Binärdatei?


23

Wenn ich ein Programm in eine einzelne Binärdatei kompilieren würde, eine Prüfsumme erstellen und diese dann auf demselben Computer mit denselben Compiler- und Compilereinstellungen erneut kompilieren und das neu kompilierte Programm mit einer Prüfsumme versehen würde, würde die Prüfsumme fehlschlagen?

Wenn ja, warum ist das so? Wenn nicht, würde eine andere CPU zu einer nicht identischen Binärdatei führen?


7
Das hängt vom Compiler ab. Einige von ihnen binden Zeitstempel ein, daher lautet die Antwort "Nein" für diese.
ta.speot.is

Eigentlich kommt es auf die an ausführbares Format , nicht der Compiler. Einige ausführbare Formate wie das Windows-PE-Format enthalten einen Zeitstempel, der sich auf die Kompilierungszeit und das Kompilierungsdatum bezieht, während andere Formate wie das Linux-ELF-Format dies nicht tun. In jedem Fall hängt diese Frage von der Definition von „identischer Binärdatei“ ab. Das Image selbst wird / sollte bitweise identisch sein, wenn dieselbe Quelldatei mit demselben Compiler und denselben Bibliotheken und Schaltern und allem kompiliert wird, aber der Header und andere Metadaten können variieren.
Synetech

Antworten:


18
  1. Kompilieren Sie dasselbe Programm mit denselben Einstellungen auf demselben Computer:

    Obwohl die endgültige Antwort "es kommt darauf an" ist, ist zu erwarten, dass die meisten Compiler die meiste Zeit deterministisch sind und dass die erzeugten Binärdateien identisch sein sollten. In der Tat hängen einige Versionskontrollsysteme davon ab. Trotzdem gibt es immer Ausnahmen; es ist gut möglich, dass etwas Der Compiler wird irgendwo entscheiden, einen Zeitstempel oder einen solchen einzufügen (iirc, Delphi zum Beispiel). Oder der Build-Prozess selbst könnte dies tun. Ich habe Makefiles für C-Programme gesehen, die ein Präprozessor-Makro auf den aktuellen Zeitstempel setzen. (Ich denke, das würde jedoch als eine andere Compilereinstellung gelten.)

    Beachten Sie auch, dass Sie, wenn Sie die Binärdatei statisch verknüpfen, den Status aller relevanten Bibliotheken auf Ihrem Computer effektiv einbinden. Jede Änderung in einer dieser Bibliotheken wirkt sich auch auf Ihre Binärdatei aus. Es sind also nicht nur die Compilereinstellungen relevant.

  2. Kompilieren Sie dasselbe Programm auf einem anderen Rechner mit einer anderen CPU.

    Hier sind alle Wetten aus. Die meisten modernen Compiler sind in der Lage, zielspezifische Optimierungen vorzunehmen. Wenn diese Option aktiviert ist, unterscheiden sich die Binärdateien wahrscheinlich, sofern die CPUs nicht ähnlich sind (und selbst dann ist dies möglich). Beachten Sie auch den obigen Hinweis zur statischen Verknüpfung: Die Konfigurationsumgebung geht weit über die Compilereinstellungen hinaus. Wenn Sie keine sehr strenge Konfigurationskontrolle haben, ist es sehr wahrscheinlich, dass sich etwas zwischen den beiden Computern unterscheidet.


1
Angenommen, ich habe GCC verwendet und nicht die Option March (die Option, die die Binärdatei für eine bestimmte Familie von CPUs optimiert), und ich sollte eine Binärdatei mit einer CPU kompilieren, und dann mit einer anderen CPU würde es eine geben Unterschied?
David

1
@ David: Es kommt immer noch darauf an. Erstens können die Bibliotheken, mit denen Sie verknüpfen, architekturspezifische Builds haben. Also die Ausgabe von gcc -c kann durchaus identisch sein, aber die verknüpften Versionen unterscheiden sich. Auch ist es nicht nur -march; Es gibt auch -mtune/-mcpu und -mfpmatch (und möglicherweise andere). Einige davon haben möglicherweise unterschiedliche Standardeinstellungen für verschiedene Installationen. Daher müssen Sie möglicherweise explizit den ungünstigsten Fall für Ihre Computer erzwingen. Dies kann die Leistung erheblich beeinträchtigen, insbesondere wenn Sie ohne sse auf i386 zurückgreifen. Und natürlich, wenn einer Ihrer CPUs ein ARM und der andere ein i686 ist ...
rici

1
Ist GCC auch einer der fraglichen Compiler, die Binärdateien einen Zeitstempel hinzufügen?
David

@ David: afaik, nein.
rici

8

Was Sie fragen, ist "ist die Ausgabe deterministisch Msgstr "Wenn Sie das Programm einmal kompiliert hätten, würden Sie es wahrscheinlich sofort wieder kompilieren. Wenn sich jedoch etwas ändert - auch eine kleine Änderung -, insbesondere in einer Komponente, die das kompilierte Programm verwendet, dann die Ausgabe des Compilers könnte sich auch ändern.


2
Sehr guter Punkt. Dieser Beitrag hat einige sehr interessante Beobachtungen. Insbesondere kann die Zusammenstellung mit GCC erfolgen nicht Seien Sie deterministisch in Bezug auf Eingaben in bestimmten Fällen, zum Beispiel in Bezug auf die Funktionsweise in anonymen Namespaces, für die intern ein Zufallszahlengenerator verwendet wird. Um in diesem speziellen Fall einen Determinismus zu erhalten, geben Sie einen anfänglichen Zufallsstartwert an, indem Sie die Option angeben -frandom-seed=string.
ack

7

Erzeugt das Neukompilieren eines Programms eine bitweise identische Binärdatei?

Für alle Compiler? Nein, der C # -Compiler darf das zumindest nicht.

Eric Lippert hat Eine sehr gründliche Aufschlüsselung, warum die Ausgabe des Compilers nicht deterministisch ist .

[D] Der C # -Compiler erzeugt niemals zweimal dieselbe Binärdatei. Der C # -Compiler bettet jedes Mal, wenn Sie ihn ausführen, eine neu generierte GUID in jede Assembly ein, um sicherzustellen, dass keine zwei Assemblys bitweise identisch sind. Um aus der CLI-Spezifikation zu zitieren:

Die Spalte Mvid soll eine eindeutige [...] GUID indizieren, die diese Instanz des Moduls identifiziert. [...] Die Mvid sollte für jedes Modul neu generiert werden. [...] Während die [Laufzeit] selbst die Mvid nicht verwendet, verlassen sich andere Tools (wie [...] Debugger) auf die Tatsache, dass die Mvid unterscheidet sich fast immer von einem Modul zum anderen.

Obwohl es spezifisch für eine Version des C # -Compilers ist, können viele Punkte im Artikel angewendet werden irgendein Compiler.

Zunächst gehen wir davon aus, dass wir immer die gleiche Liste von Dateien in der gleichen Reihenfolge erhalten. Aber das liegt in einigen Fällen am Betriebssystem. Wenn Sie "csc * .cs" sagen, ist die Reihenfolge, in der das Betriebssystem die Liste der übereinstimmenden Dateien anzeigt, ein Implementierungsdetail des Betriebssystems. Der Compiler sortiert diese Liste nicht in eine kanonische Reihenfolge.


Es sollte nicht schwierig sein, den Build reproduzierbar zu machen (abgesehen von ein paar leicht zu verwerfenden Feldern wie der Kompilierungszeit und der Assembly-GUID). Das Sortieren von Eingabedateien in einer kanonischen Reihenfolge ist beispielsweise ein Einzeiler. Sogar diese GUID könnte ein Hash des Restes der Assembly sein, anstatt neu generiert zu werden.
CodesInChaos

Ich nehme an, Sie meinen den Microsoft C # -Compiler, oder ist dies eine Anforderung der Spezifikation?
David

@ David Die CLI-Spezifikation erfordert es. Monos C # -Compiler müsste dasselbe tun. Das Gleiche gilt für jeden VB .NET-Compiler.
ta.speot.is

3
Der ECMA-Standard muss keine Zeitstempel oder MVID-Unterschiede aufweisen. Ohne diese ist es in C # zumindest möglich, identische Binärdateien zu verwenden. Der Hauptgrund ist daher eine fragwürdige Entwurfsentscheidung und keine echte technische Einschränkung.
Shiv

5
  • -frandom-seed=123 steuert einige GCC interne Zufälligkeit. man gcc sagt:

    Diese Option liefert einen Startwert, den GCC anstelle von Zufallszahlen verwendet, um bestimmte Symbolnamen zu generieren, die in jeder kompilierten Datei unterschiedlich sein müssen. Es wird auch verwendet, um eindeutige Stempel in Deckungsdatendateien und die Objektdateien, die diese erzeugen, zu platzieren. Sie können die Option -frandom-seed verwenden, um reproduzierbar zu produzieren          identische Objektdateien.

  • __FILE__: Legen Sie die Quelle in einem festen Ordner ab (z. /tmp/build )

  • zum __DATE__, __TIME__, __TIMESTAMP__:
    • libfaketime: https://github.com/wolfcw/libfaketime
    • Überschreibe diese Makros mit -D
    • -Wdate-time oder -Werror=date-time: warnen oder scheitern, wenn entweder __TIME__, __DATE__ oder __TIMESTAMP__ verwendet wird. Der Linux-Kernel 4.4 verwendet es standardmäßig.
  • benutze die D Fahne mit aroder verwenden https://github.com/nh2/ar-timestamp-wiper/tree/master Briefmarken abwischen
  • -fno-guess-branch-probability: ältere manuelle Versionen sagen, es ist eine Quelle des Nichtdeterminismus, aber nicht länger . Ich bin nicht sicher, ob dies von abgedeckt wird -frandom-seed oder nicht.

Das Debian Reproduzierbares Bauprojekt Versuche, Debian-Pakete byteweise zu standardisieren, und bekamen kürzlich a Linux Foundation Zuschuss . Das beinhaltet mehr als nur das Zusammenstellen, aber es sollte von Interesse sein.

Baumwurzel hat ein BR2_REPRODUCIBLE Option, die einige Ideen auf Paketebene geben kann, aber an dieser Stelle noch lange nicht vollständig ist.

Verwandte Themen:


2

Ich würde NEIN sagen, es ist nicht 100% deterministisch. Ich habe zuvor mit einer Version von GCC gearbeitet, die Ziel-Binärdateien für den Hitachi H8-Prozessor generiert.

Es ist kein Problem mit dem Zeitstempel. Selbst wenn das Problem mit dem Zeitstempel ignoriert wird, kann es aufgrund der spezifischen Prozessorarchitektur möglich sein, dass derselbe Befehl auf zwei leicht unterschiedliche Arten codiert wird, wobei einige Bits 1 oder 0 sein können. Meine bisherigen Erfahrungen zeigen, dass die generierten Binärdateien die meiste Zeit gleich waren aber gelegentlich erzeugte der gcc Binärdateien mit identischer Größe, aber einigen Bytes, die sich nur um 1 Bit unterschieden, z 0XE0 wird 0XE1.


1

Im Allgemeinen nicht. Die meisten einigermaßen ausgefeilten Compiler berücksichtigen die Kompilierzeit im Objektmodul. Selbst wenn Sie die Uhr zurücksetzen würden, müssten Sie hinsichtlich des Zeitpunkts, zu dem Sie die Kompilierung gestartet haben, sehr genau sein (und dann hoffen, dass die Datenträgerzugriffe usw. die gleiche Geschwindigkeit wie zuvor hatten).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.