[PATCH] D43978: Write a hash of the binary as the PE Debug Directory Timestamp

Zachary Turner via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 2 09:55:07 PST 2018


zturner updated this revision to Diff 136780.
zturner added a comment.

Attempt N+1 at making this right.

Now we hash the binary *after* setting the guid and age.  This should address the problem of debug info changes with no binary changes, because when that happens we increment the age.  By hashing *after* setting the guid and age, we ensure that even if only the debug info changes, the hash will also change.


https://reviews.llvm.org/D43978

Files:
  lld/COFF/Writer.cpp


Index: lld/COFF/Writer.cpp
===================================================================
--- lld/COFF/Writer.cpp
+++ lld/COFF/Writer.cpp
@@ -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>
@@ -71,11 +72,18 @@
       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;
 };
 
@@ -157,7 +165,7 @@
   RVATableChunk *GuardFidsTable = nullptr;
   RVATableChunk *SEHTable = nullptr;
 
-  Chunk *DebugDirectory = nullptr;
+  DebugDirectoryChunk *DebugDirectory = nullptr;
   std::vector<Chunk *> DebugRecords;
   CVDebugRecordChunk *BuildId = nullptr;
   Optional<codeview::DebugInfo> PreviousBuildId;
@@ -1032,16 +1040,46 @@
     return;
 
   assert(BuildId && "BuildId is not set!");
-
+  assert(DebugDirectory && "DebugDirectory is not set!");
+
+  // Set the time stamp to a hash of the executable file.  Make sure to
+  // do this *after* setting the GUID and age, this way if the debug
+  // info changes but the binary doesn't, the hash will also change.
+  // Otherwise you could have a situation where there is only one slot
+  // on a symbol server for a given binary, but two slots for the debug
+  // info, and you wouldn't be able to debug crash dumps for one of the
+  // versions.
+  //
+  // Note that there are several time stamps that need to be set.
+  // * The first is in the COFF file header which occurs immediately after
+  //   the DOS stub at the beginning of the file.
+  // * The second occur in the debug directories, one in each directory.  The
+  //   PE spec says this should be the time that the debug information was
+  //   created, but in practice I cannot find a link.exe generated binary where
+  //   the values aren't identical to the one in the COFF file header.
+  // It's not clear if they need to agree, but this is what the MS linker does
+  // so we match the behavior.
   if (PreviousBuildId.hasValue()) {
     *BuildId->BuildId = *PreviousBuildId;
     BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
-    return;
+  } else {
+    BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
+    BuildId->BuildId->PDB70.Age = 1;
+    llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
   }
 
-  BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
-  BuildId->BuildId->PDB70.Age = 1;
-  llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
+  // Now that the Guid and Age are set, hashing the binary will incorporate
+  // those values as part of the hash.
+  StringRef OutputFileData(
+      reinterpret_cast<const char *>(Buffer->getBufferStart()),
+      Buffer->getBufferSize());
+  uint32_t Hash = static_cast<uint32_t>(xxHash64(OutputFileData));
+  DebugDirectory->setTimeDateStamp(Hash);
+  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.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D43978.136780.patch
Type: text/x-patch
Size: 3535 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20180302/b6ff085a/attachment.bin>


More information about the llvm-commits mailing list