Resubmit "Write a hash of the executable into the PE timestamp fields."
authorZachary Turner <zturner@google.com>
Thu, 8 Mar 2018 19:33:47 +0000 (19:33 +0000)
committerZachary Turner <zturner@google.com>
Thu, 8 Mar 2018 19:33:47 +0000 (19:33 +0000)
This fixes the broken tests that were causing failures.  The tests
before were verifying that the time stamp was 0, but now that we
are actually writing a timestamp, I just removed the match against
the timestamp value.

llvm-svn: 327049

lld/COFF/Writer.cpp
lld/test/COFF/hello32.test
lld/test/COFF/largeaddressaware.test
lld/test/COFF/rsds.test

index 3b026e2..b3a33ce 100644 (file)
@@ -28,6 +28,7 @@
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Support/xxhash.h"
 #include <algorithm>
 #include <cstdio>
 #include <map>
@@ -103,11 +104,18 @@ public:
       uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA());
       D->PointerToRawData = Offs;
 
+      TimeDateStamps.push_back(&D->TimeDateStamp);
       ++D;
     }
   }
 
+  void setTimeDateStamp(uint32_t TimeDateStamp) {
+    for (support::ulittle32_t *TDS : TimeDateStamps)
+      *TDS = TimeDateStamp;
+  }
+
 private:
+  mutable std::vector<support::ulittle32_t *> TimeDateStamps;
   const std::vector<Chunk *> &Records;
 };
 
@@ -189,7 +197,7 @@ private:
   RVATableChunk *GuardFidsTable = nullptr;
   RVATableChunk *SEHTable = nullptr;
 
-  Chunk *DebugDirectory = nullptr;
+  DebugDirectoryChunk *DebugDirectory = nullptr;
   std::vector<Chunk *> DebugRecords;
   CVDebugRecordChunk *BuildId = nullptr;
   Optional<codeview::DebugInfo> PreviousBuildId;
@@ -1070,22 +1078,50 @@ void Writer::writeSections() {
 }
 
 void Writer::writeBuildId() {
-  // If we're not writing a build id (e.g. because /debug is not specified),
-  // then just return;
-  if (!Config->Debug)
-    return;
+  // There are two important parts to the build ID.
+  // 1) If building with debug info, the COFF debug directory contains a
+  //    timestamp as well as a Guid and Age of the PDB.
+  // 2) In all cases, the PE COFF file header also contains a timestamp.
+  // For reproducibility, instead of a timestamp we want to use a hash of the
+  // binary, however when building with debug info the hash needs to take into
+  // account the debug info, since it's possible to add blank lines to a file
+  // which causes the debug info to change but not the generated code.
+  //
+  // To handle this, we first set the Guid and Age in the debug directory (but
+  // only if we're doing a debug build).  Then, we hash the binary (thus causing
+  // the hash to change if only the debug info changes, since the Age will be
+  // different).  Finally, we write that hash into the debug directory (if
+  // present) as well as the COFF file header (always).
+  if (Config->Debug) {
+    assert(BuildId && "BuildId is not set!");
+    if (PreviousBuildId.hasValue()) {
+      *BuildId->BuildId = *PreviousBuildId;
+      BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
+    } else {
+      BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
+      BuildId->BuildId->PDB70.Age = 1;
+      llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
+    }
+  }
 
-  assert(BuildId && "BuildId is not set!");
+  // At this point the only fields in the COFF file which remain unset are the
+  // "timestamp" in the COFF file header, and the ones in the coff debug
+  // directory.  Now we can hash the file and write that hash to the various
+  // timestamp fields in the file.
+  StringRef OutputFileData(
+      reinterpret_cast<const char *>(Buffer->getBufferStart()),
+      Buffer->getBufferSize());
 
-  if (PreviousBuildId.hasValue()) {
-    *BuildId->BuildId = *PreviousBuildId;
-    BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
-    return;
-  }
+  uint32_t Hash = static_cast<uint32_t>(xxHash64(OutputFileData));
+
+  if (DebugDirectory)
+    DebugDirectory->setTimeDateStamp(Hash);
 
-  BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
-  BuildId->BuildId->PDB70.Age = 1;
-  llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
+  uint8_t *Buf = Buffer->getBufferStart();
+  Buf += DOSStubSize + sizeof(PEMagic);
+  object::coff_file_header *CoffHeader =
+      reinterpret_cast<coff_file_header *>(Buf);
+  CoffHeader->TimeDateStamp = Hash;
 }
 
 // Sort .pdata section contents according to PE/COFF spec 5.5.
index 3b489d7..9f3cadc 100644 (file)
@@ -11,7 +11,7 @@ HEADER-NEXT: AddressSize: 32bit
 HEADER-NEXT: ImageFileHeader {
 HEADER-NEXT:   Machine: IMAGE_FILE_MACHINE_I386 (0x14C)
 HEADER-NEXT:   SectionCount: 4
-HEADER-NEXT:   TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+HEADER-NEXT:   TimeDateStamp: 
 HEADER-NEXT:   PointerToSymbolTable: 0x0
 HEADER-NEXT:   SymbolCount: 0
 HEADER-NEXT:   OptionalHeaderSize: 224
index d035e7c..76aadee 100644 (file)
@@ -9,7 +9,7 @@ HEADER-NEXT: AddressSize: 32bit
 HEADER-NEXT: ImageFileHeader {
 HEADER-NEXT:   Machine: IMAGE_FILE_MACHINE_I386 (0x14C)
 HEADER-NEXT:   SectionCount: 4
-HEADER-NEXT:   TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+HEADER-NEXT:   TimeDateStamp: 
 HEADER-NEXT:   PointerToSymbolTable: 0x0
 HEADER-NEXT:   SymbolCount: 0
 HEADER-NEXT:   OptionalHeaderSize: 224
index 1765977..10c2c56 100644 (file)
@@ -18,7 +18,7 @@
 # CHECK: DebugDirectory [
 # CHECK:   DebugEntry {
 # CHECK:     Characteristics: 0x0
-# CHECK:     TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+# CHECK:     TimeDateStamp: 
 # CHECK:     MajorVersion: 0x0
 # CHECK:     MinorVersion: 0x0
 # CHECK:     Type: CodeView (0x2)
@@ -37,7 +37,7 @@
 # CHECK: DebugDirectory [
 # CHECK:   DebugEntry {
 # CHECK:     Characteristics: 0x0
-# CHECK:     TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+# CHECK:     TimeDateStamp: 
 # CHECK:     MajorVersion: 0x0
 # CHECK:     MinorVersion: 0x0
 # CHECK:     Type: CodeView (0x2)