Index: lld/test/COFF/repro-debug-clean.test
===================================================================
--- /dev/null
+++ lld/test/COFF/repro-debug-clean.test
@@ -0,0 +1,30 @@
+RUN: rm -rf %t
+RUN: mkdir %t
+RUN: yaml2obj %p/Inputs/pdb1.yaml > %t/a.obj
+RUN: yaml2obj %p/Inputs/pdb2.yaml > %t/b.obj
+
+RUN: lld-link /DEBUG %t/a.obj %t/b.obj /nodefaultlib /entry:main /OUT:%t/out.exe /PDB:%t/out.pdb
+RUN: mv %t/out.exe %t/out.1.exe
+RUN: mv %t/out.pdb %t/out.1.pdb
+
+RUN: lld-link /DEBUG %t/a.obj %t/b.obj /nodefaultlib /entry:main /OUT:%t/out.exe /PDB:%t/out.pdb
+RUN: mv %t/out.exe %t/out.2.exe
+RUN: mv %t/out.pdb %t/out.2.pdb
+
+; Files should be different since the debug directories will contain different
+; GUIDs.
+RUN: not diff %t/out.1.exe %t/out.2.exe
+
+RUN: llvm-pdbutil stamp -age=12 -guid={FF141A38-63A8-4972-A23B-090EC3C16BD2} %t/out.1.exe
+RUN: llvm-pdbutil stamp -age=12 -guid={FF141A38-63A8-4972-A23B-090EC3C16BD2} %t/out.2.exe
+
+; Files should be the same now that they've been stamped with the same GUID / Age
+; e.g. the only difference was the GUID and age.
+
+RUN: diff %t/out.1.exe %t/out.2.exe
+
+RUN: llvm-readobj -coff-debug-directory -file-headers %t/out.1.exe | FileCheck %s
+
+CHECK-NOT:  TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+CHECK:      PDBGUID: (FF 14 1A 38 63 A8 49 72 A2 3B 09 0E C3 C1 6B D2)
+CHECK-NEXT: PDBAge: 12
Index: lld/test/COFF/repro-nodebug.test
===================================================================
--- /dev/null
+++ lld/test/COFF/repro-nodebug.test
@@ -0,0 +1,7 @@
+RUN: rm -rf %t
+RUN: mkdir %t
+RUN: yaml2obj %p/Inputs/pdb1.yaml > %t/a.obj
+RUN: yaml2obj %p/Inputs/pdb2.yaml > %t/b.obj
+RUN: lld-link %t/a.obj %t/b.obj /nodefaultlib /entry:main /OUT:%t/out.nodebug.1.exe
+RUN: lld-link %t/a.obj %t/b.obj /nodefaultlib /entry:main /OUT:%t/out.nodebug.2.exe
+RUN: diff %t/out.nodebug.1.exe %t/out.nodebug.2.exe
Index: llvm/include/llvm/ADT/StringExtras.h
===================================================================
--- llvm/include/llvm/ADT/StringExtras.h
+++ llvm/include/llvm/ADT/StringExtras.h
@@ -48,6 +48,31 @@
 }
 
 /// Construct a string ref from an array ref of unsigned chars.
+inline StringRef toStringRef(MutableArrayRef<uint8_t> Input) {
+  return StringRef(reinterpret_cast<const char *>(Input.begin()), Input.size());
+}
+
+/// Construct a string ref from an array ref of unsigned chars.
+inline StringRef toStringRef(ArrayRef<char> Input) {
+  return StringRef(Input.begin(), Input.size());
+}
+
+/// Construct a string ref from an array ref of unsigned chars.
+inline StringRef toStringRef(MutableArrayRef<char> Input) {
+  return StringRef(Input.begin(), Input.size());
+}
+
+/// Construct a string ref from an array ref of unsigned chars.
+inline StringRef toStringRef(const std::vector<char> &Input) {
+  return StringRef(Input.data(), Input.size());
+}
+
+/// Construct a string ref from an array ref of unsigned chars.
+inline StringRef toStringRef(const std::vector<uint8_t> &Input) {
+  return StringRef(reinterpret_cast<const char *>(Input.data()), Input.size());
+}
+
+/// Construct a string ref from an array ref of unsigned chars.
 inline ArrayRef<uint8_t> arrayRefFromStringRef(StringRef Input) {
   return {Input.bytes_begin(), Input.bytes_end()};
 }
Index: llvm/include/llvm/DebugInfo/PDB/PDB.h
===================================================================
--- llvm/include/llvm/DebugInfo/PDB/PDB.h
+++ llvm/include/llvm/DebugInfo/PDB/PDB.h
@@ -13,6 +13,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/DebugInfo/PDB/PDBTypes.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
 #include <memory>
 
 namespace llvm {
Index: llvm/include/llvm/Object/COFF.h
===================================================================
--- llvm/include/llvm/Object/COFF.h
+++ llvm/include/llvm/Object/COFF.h
@@ -958,6 +958,7 @@
       return nullptr;
     return reinterpret_cast<const dos_header *>(base());
   }
+  std::error_code getCOFFFileHeader(const coff_file_header *&Res) const;
   std::error_code getPE32Header(const pe32_header *&Res) const;
   std::error_code getPE32PlusHeader(const pe32plus_header *&Res) const;
   std::error_code getDataDirectory(uint32_t index,
Index: llvm/lib/Object/COFFObjectFile.cpp
===================================================================
--- llvm/lib/Object/COFFObjectFile.cpp
+++ llvm/lib/Object/COFFObjectFile.cpp
@@ -930,6 +930,11 @@
   return make_range(base_reloc_begin(), base_reloc_end());
 }
 
+std::error_code COFFObjectFile::getCOFFFileHeader(const coff_file_header *&Res) const {
+  Res = COFFHeader;
+  return std::error_code();
+}
+
 std::error_code COFFObjectFile::getPE32Header(const pe32_header *&Res) const {
   Res = PE32Header;
   return std::error_code();
Index: llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
===================================================================
--- llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -32,6 +32,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/BinaryFormat/COFF.h"
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Config/config.h"
 #include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
@@ -49,6 +50,7 @@
 #include "llvm/DebugInfo/PDB/IPDBSession.h"
 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
 #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
 #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
 #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
@@ -64,6 +66,7 @@
 #include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
+#include "llvm/Object/COFF.h"
 #include "llvm/Support/BinaryByteStream.h"
 #include "llvm/Support/COM.h"
 #include "llvm/Support/CommandLine.h"
@@ -81,7 +84,7 @@
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
-
+#include "llvm/Support/xxhash.h"
 
 using namespace llvm;
 using namespace llvm::codeview;
@@ -113,6 +116,8 @@
 cl::SubCommand MergeSubcommand("merge",
                                "Merge multiple PDBs into a single PDB");
 
+cl::SubCommand StampSubcommand("stamp", "Stamp a PDB and/or PE file with a build id");
+
 cl::OptionCategory TypeCategory("Symbol Type Options");
 cl::OptionCategory FilterCategory("Filtering and Sorting Options");
 cl::OptionCategory OtherOptions("Other Options");
@@ -629,6 +634,14 @@
     PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
                   cl::sub(MergeSubcommand));
 }
+
+namespace stamp {
+  cl::opt<std::string> Guid("guid", cl::desc("GUID for the PDB or executable"), cl::Optional, cl::sub(StampSubcommand));
+  cl::opt<uint32_t> Age("age", cl::desc("Age for the PDB or executable"), cl::Optional, cl::sub(StampSubcommand));
+  cl::opt<std::string> InputFilename(cl::Positional, cl::Required,
+    cl::desc("<input PE or PDB file>"),
+    cl::sub(StampSubcommand));
+}
 }
 
 static ExitOnError ExitOnErr;
@@ -1072,6 +1085,179 @@
   Chunks.push_back(opts::ModuleSubsection::All);
 }
 
+static void findPdbGuidAndAge(PDBFile &File, Optional<uint64_t> &Guid, Optional<uint64_t> &PDBAge, Optional<uint64_t> &DBIAge) {
+  const MSFLayout &L = File.getMsfLayout();
+  if (StreamPDB >= L.StreamMap.size())
+    return;
+
+  ArrayRef<support::ulittle32_t> StreamBlocks = L.StreamMap[StreamPDB];
+  if (StreamBlocks.empty())
+    return;
+
+  uint64_t IS0 = blockToOffset(StreamBlocks[0], L.SB->BlockSize);
+  PDBAge = IS0 + offsetof(InfoStreamHeader, Age);
+  Guid = IS0 + offsetof(InfoStreamHeader, Guid);
+
+  if (StreamDBI >= L.StreamMap.size())
+    return;
+  StreamBlocks = L.StreamMap[StreamDBI];
+  if (StreamBlocks.empty())
+    return;
+
+  uint64_t DBI0 = blockToOffset(StreamBlocks[0], L.SB->BlockSize);
+  DBIAge = DBI0 + offsetof(DbiStreamHeader, Age);
+}
+
+static Expected<codeview::GUID> guidFromText(StringRef GuidStr) {
+  // We recognize the following two forms of Guid specification.
+  //   {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
+  //    XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
+  // where X is a hex digit (in upper or lowercase).
+  if (GuidStr.size() != 36 && GuidStr.size() != 38)
+    return make_error<StringError>("Invalid Guid string!",
+      inconvertibleErrorCode());
+
+  if (GuidStr.size() == 38) {
+    // Strip { and } from the beginning and end.
+    if (GuidStr.front() != '{' || GuidStr.back() != '}')
+      return make_error<StringError>("Invalid Guid string!",
+        inconvertibleErrorCode());
+    GuidStr = GuidStr.drop_front().drop_back();
+  }
+
+  assert(GuidStr.size() == 36);
+  SmallVector<StringRef, 4> Groups;
+  GuidStr.split(Groups, '-');
+  if (Groups.size() != 5 || Groups[0].size() != 8 || Groups[1].size() != 4 ||
+    Groups[2].size() != 4 || Groups[3].size() != 4 || Groups[4].size() != 12)
+    return make_error<StringError>("Invalid Guid string!",
+      inconvertibleErrorCode());
+  GUID Result;
+  uint8_t *WritePtr = Result.Guid;
+  for (StringRef G : Groups) {
+    while (!G.empty()) {
+      if (!isHexDigit(G[0]) || !isHexDigit(G[1]))
+        return make_error<StringError>("Invalid Guid string!",
+          inconvertibleErrorCode());
+
+      *WritePtr = hexFromNibbles(G[0], G[1]);
+      G = G.drop_front(2);
+      ++WritePtr;
+    }
+  }
+  return Result;
+}
+
+static void stampPdb(std::unique_ptr<WriteThroughMemoryBuffer> Buffer) {
+  bool Age = opts::stamp::Age.getNumOccurrences() > 0;
+  bool Guid = opts::stamp::Guid.getNumOccurrences() > 0;
+
+  std::unique_ptr<IPDBSession> PdbSession;
+  NativeSession *PdbNativeSession = nullptr;
+
+  WriteThroughMemoryBuffer *PdbBufferPtr = Buffer.get();
+  if (NativeSession::createFromPdb(std::move(Buffer), PdbSession)) {
+    errs() << "Unable to parse PDB file.\n";
+    exit(1);
+  }
+  PdbNativeSession = static_cast<NativeSession *>(PdbSession.get());
+  Optional<uint64_t> AOff1;
+  Optional<uint64_t> AOff2;
+  Optional<uint64_t> GOff;
+  findPdbGuidAndAge(PdbNativeSession->getPDBFile(), GOff, AOff1, AOff2);
+
+  if (Age) {
+    for (auto Off : { AOff1, AOff2 }) {
+      if (!Off)
+        continue;
+
+      support::ulittle32_t *Ptr = reinterpret_cast<support::ulittle32_t*>(PdbBufferPtr->getBufferStart() + *Off);
+      *Ptr = opts::stamp::Age;
+    }
+  }
+
+  if (Guid) {
+    codeview::GUID GuidValue = ExitOnErr(guidFromText(opts::stamp::Guid));
+
+    if (GOff.hasValue()) {
+      codeview::GUID *Ptr = reinterpret_cast<codeview::GUID*>(PdbBufferPtr->getBufferStart() + *GOff);
+      ::memcpy(Ptr, &GuidValue, sizeof(codeview::GUID));
+    } else {
+      outs() << "The PDB file does not contain a GUID.\n";
+    }
+  }
+}
+
+static void stampExecutable(std::unique_ptr<WriteThroughMemoryBuffer> Buffer) {
+  bool Age = opts::stamp::Age.getNumOccurrences() > 0;
+  bool Guid = opts::stamp::Guid.getNumOccurrences() > 0;
+
+  WriteThroughMemoryBuffer *ExeBufferPtr = Buffer.get();
+
+  std::error_code EC;
+  object::COFFObjectFile CoffBinary(*Buffer, EC);
+  if (EC) {
+    errs() << "Could not parse executable file.\n";
+    exit(1);
+  }
+
+  SmallVector<object::debug_directory *, 1> CodeViewDebugDirs;
+
+  // We need to update two places in the PE file.  Both of these places store
+  // a "time stamp" (which we treat as a hash of the executable).  This hash
+  // *includes* the GUID and age, but *does not* include the value of the hash
+  // fields themselves.  So we need to zero out the time stamp fields that we
+  // encounter, so that after re-writing the GUID and age the new hash is
+  // computed correctly.
+
+  // First we update the CodeView debug directories.
+  for (const object::debug_directory &Dir : CoffBinary.debug_directories()) {
+    // We know these were created from a read-write MemoryBuffer, so it's safe to
+    // const_cast them back to writable memory.
+    object::debug_directory *MutableDir = const_cast<object::debug_directory*>(&Dir);
+    MutableDir->TimeDateStamp = 0;
+    CodeViewDebugDirs.push_back(MutableDir);
+
+    if (Dir.Type != COFF::IMAGE_DEBUG_TYPE_CODEVIEW)
+      continue;
+    const codeview::DebugInfo *PdbInfo = nullptr;
+    StringRef PdbFileName;
+    if (CoffBinary.getDebugPDBInfo(PdbInfo, PdbFileName)) {
+      errs() << "Unable to read CV debug directory from PE file.\n";
+      exit(1);
+    }
+
+    codeview::DebugInfo *MutableBuildId = const_cast<codeview::DebugInfo*>(PdbInfo);
+    if (Age)
+      MutableBuildId->PDB70.Age = opts::stamp::Age;
+    if (Guid) {
+      codeview::GUID GuidValue = ExitOnErr(guidFromText(opts::stamp::Guid));
+      ::memcpy(&MutableBuildId->PDB70.Signature, &GuidValue, sizeof(codeview::GUID));
+    }
+  }
+
+  if (CodeViewDebugDirs.empty()) {
+    errs() << "Executable file does not contain a CV debug directory.  Cannot stamp file, re-link with /DEBUG.\n";
+    exit(1);
+  }
+
+  // Second is the COFF file header.
+  const object::coff_file_header *CFH;
+  CoffBinary.getCOFFFileHeader(CFH);
+  object::coff_file_header* MutableCFH = const_cast<object::coff_file_header*>(CFH);
+  MutableCFH->TimeDateStamp = 0;
+
+  // After all fields have been zeroed out, hash the executable and then write
+  // the hash to the appropriate places (coff file header + CV debug
+  // directories).
+  
+  StringRef ExeData = toStringRef(ExeBufferPtr->getBuffer());
+  uint32_t Hash = static_cast<uint32_t>(xxHash64(ExeData));
+  MutableCFH->TimeDateStamp = Hash;
+  for (object::debug_directory *DD : CodeViewDebugDirs)
+    DD->TimeDateStamp = Hash;
+}
+
 int main(int argc_, const char *argv_[]) {
   // Print a stack trace if we signal out.
   sys::PrintStackTraceOnErrorSignal(argv_[0]);
@@ -1234,6 +1420,24 @@
       exit(1);
     }
     mergePdbs();
+  } else if (opts::StampSubcommand) {
+    auto BufferOrErr = WriteThroughMemoryBuffer::getFile(opts::stamp::InputFilename);
+    if (!BufferOrErr) {
+      errs() << "Unable to open input file for read-write.\n";
+      exit(1);
+    }
+
+    std::unique_ptr<WriteThroughMemoryBuffer> Buffer = std::move(*BufferOrErr);
+    file_magic Magic = identify_magic(toStringRef(Buffer->getBuffer()));
+
+    if (Magic == file_magic::pecoff_executable)
+      stampExecutable(std::move(Buffer));
+    else if (Magic == file_magic::pdb)
+      stampPdb(std::move(Buffer));
+    else {
+      errs() << "File must be a PDB file or PE/COFF executable or DLL.\n";
+      exit(1);
+    }
   }
 
   outs().flush();