[lld] [LLD][COFF] Add -build-id flag to generate .buildid section. (PR #71433)

Zequan Wu via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 5 11:55:50 PST 2023


https://github.com/ZequanWu updated https://github.com/llvm/llvm-project/pull/71433

>From 3dcfc47ec996693896f18c6aa893c91eb50202aa Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Mon, 6 Nov 2023 14:27:50 -0500
Subject: [PATCH 01/13] [LLD][COFF] Add -build-id flag to genrate .buildid
 section.

---
 lld/COFF/Config.h       |  1 +
 lld/COFF/Driver.cpp     |  5 ++++
 lld/COFF/Options.td     |  2 ++
 lld/COFF/Writer.cpp     | 22 +++++++++++------
 lld/MinGW/Options.td    |  2 +-
 lld/test/COFF/rsds.test | 54 ++++++++++++++++++++++++++---------------
 6 files changed, 58 insertions(+), 28 deletions(-)

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 1c338cc63fa87..4e4811357d26a 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -317,6 +317,7 @@ struct Configuration {
   bool writeCheckSum = false;
   EmitKind emit = EmitKind::Obj;
   bool allowDuplicateWeak = false;
+  bool buildID = false;
 };
 
 } // namespace lld::coff
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 5613c2e6993a5..0fa6fd6d34cf6 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2332,6 +2332,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     }
   }
 
+  // Generate build id hash in .buildid section when:
+  // 1. /build-id
+  // 2. /lldmingw + /debug and not generating pdb file.
+  config->buildID = args.hasArg(OPT_build_id) ||
+                    (config->mingw && config->debug && !shouldCreatePDB);
   // Set default image base if /base is not given.
   if (config->imageBase == uint64_t(-1))
     config->imageBase = getDefaultImageBase();
diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index 977657a433dc5..cc7ce4fdbeea0 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -299,6 +299,8 @@ def : Flag<["--"], "time-trace">, Alias<time_trace_eq>,
 def time_trace_granularity_eq: Joined<["--"], "time-trace-granularity=">,
     HelpText<"Minimum time granularity (in microseconds) traced by time profiler">;
 
+def build_id: F<"build-id">, HelpText<"Generate build ID">;
+
 // Flags for debugging
 def lldmap : F<"lldmap">;
 def lldmap_file : P_priv<"lldmap">;
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 43d8e7c1d5308..dca148a62e9d1 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1041,14 +1041,14 @@ void Writer::createMiscChunks() {
 
   // Create Debug Information Chunks
   OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
-  if (config->debug || config->repro || config->cetCompat) {
+  if (config->buildID || config->debug || config->repro || config->cetCompat) {
     debugDirectory =
         make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
     debugDirectory->setAlignment(4);
     debugInfoSec->addChunk(debugDirectory);
   }
 
-  if (config->debug) {
+  if (config->debug || config->buildID) {
     // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified.  We
     // output a PDB no matter what, and this chunk provides the only means of
     // allowing a debugger to match a PDB and an executable.  So we need it even
@@ -1069,6 +1069,16 @@ void Writer::createMiscChunks() {
     debugInfoSec->addChunk(r.second);
   }
 
+  // If .buildid section was not chosen and /build-id is given, create .buildid
+  // section.
+  if (config->buildID && debugInfoSec != buildidSec) {
+    buildidSec->addChunk(debugDirectory);
+    for (std::pair<COFF::DebugType, Chunk *> r : debugRecords) {
+      r.second->setAlignment(4);
+      buildidSec->addChunk(r.second);
+    }
+  }
+
   // Create SEH table. x86-only.
   if (config->safeSEH)
     createSEHTable();
@@ -2028,7 +2038,7 @@ void Writer::writeBuildId() {
   // PE contents.
   Configuration *config = &ctx.config;
 
-  if (config->debug) {
+  if (config->debug || config->buildID) {
     assert(buildId && "BuildId is not set!");
     // BuildId->BuildId was filled in when the PDB was written.
   }
@@ -2043,16 +2053,14 @@ void Writer::writeBuildId() {
 
   uint32_t timestamp = config->timestamp;
   uint64_t hash = 0;
-  bool generateSyntheticBuildId =
-      config->mingw && config->debug && config->pdbPath.empty();
 
-  if (config->repro || generateSyntheticBuildId)
+  if (config->repro || config->buildID)
     hash = xxh3_64bits(outputFileData);
 
   if (config->repro)
     timestamp = static_cast<uint32_t>(hash);
 
-  if (generateSyntheticBuildId) {
+  if (config->buildID) {
     // For MinGW builds without a PDB file, we still generate a build id
     // to allow associating a crash dump to the executable.
     buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70;
diff --git a/lld/MinGW/Options.td b/lld/MinGW/Options.td
index fa4c4ecc75d65..87b5c4fff68ce 100644
--- a/lld/MinGW/Options.td
+++ b/lld/MinGW/Options.td
@@ -196,6 +196,7 @@ defm guard_longjmp : B<"guard-longjmp",
   "Do not enable Control Flow Guard long jump hardening">;
 defm error_limit:
   EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
+def build_id: F<"build-id">, HelpText<"Generate build ID">;
 
 // Alias
 def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
@@ -213,7 +214,6 @@ def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
 // Ignored options
 def: Joined<["-"], "O">;
 def: F<"as-needed">;
-def: F<"build-id">;
 def: F<"disable-auto-image-base">;
 def: F<"enable-auto-image-base">;
 def: F<"end-group">;
diff --git a/lld/test/COFF/rsds.test b/lld/test/COFF/rsds.test
index 475249ca40566..783ac08255c03 100644
--- a/lld/test/COFF/rsds.test
+++ b/lld/test/COFF/rsds.test
@@ -22,9 +22,23 @@
 # RUN: lld-link /Brepro /debug /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix REPRODEBUG %s
 
+# Generate .buildid section when /lldmingw + /debug:dwarf are given and not generating pdb file.
 # RUN: rm -f %t.dll %t.pdb
 # RUN: lld-link /lldmingw /debug:dwarf /dll /out:%t.dll /entry:DllMain %t.obj
-# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix MINGW %s
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
+
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /lldmingw /debug:dwarf /build-id /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
+
+# Generate .buildid section when /build-id is given.
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /build-id /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
+
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /build-id /debug /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
 
 # CHECK: File: [[FILE:.*]].dll
 # CHECK: DebugDirectory [
@@ -148,25 +162,25 @@
 # REPRODEBUG:   }
 # REPRODEBUG: ]
 
-# MINGW: File: {{.*}}.dll
-# MINGW: DebugDirectory [
-# MINGW:   DebugEntry {
-# MINGW:     Characteristics: 0x0
-# MINGW:     TimeDateStamp:
-# MINGW:     MajorVersion: 0x0
-# MINGW:     MinorVersion: 0x0
-# MINGW:     Type: CodeView (0x2)
-# MINGW:     SizeOfData: 0x{{[^0]}}
-# MINGW:     AddressOfRawData: 0x{{[^0]}}
-# MINGW:     PointerToRawData: 0x{{[^0]}}
-# MINGW:     PDBInfo {
-# MINGW:       PDBSignature: 0x53445352
-# MINGW:       PDBGUID: [[GUID:\(([A-Za-z0-9]{2} ?){16}\)]]
-# MINGW:       PDBAge: 1
-# MINGW:       PDBFileName:
-# MINGW:     }
-# MINGW:   }
-# MINGW: ]
+# BUILDID: File: {{.*}}.dll
+# BUILDID: DebugDirectory [
+# BUILDID:   DebugEntry {
+# BUILDID:     Characteristics: 0x0
+# BUILDID:     TimeDateStamp:
+# BUILDID:     MajorVersion: 0x0
+# BUILDID:     MinorVersion: 0x0
+# BUILDID:     Type: CodeView (0x2)
+# BUILDID:     SizeOfData: 0x{{[^0]}}
+# BUILDID:     AddressOfRawData: 0x{{[^0]}}
+# BUILDID:     PointerToRawData: 0x{{[^0]}}
+# BUILDID:     PDBInfo {
+# BUILDID:       PDBSignature: 0x53445352
+# BUILDID:       PDBGUID: [[GUID:\(([A-Za-z0-9]{2} ?){16}\)]]
+# BUILDID:       PDBAge: 1
+# BUILDID:       PDBFileName:
+# BUILDID:     }
+# BUILDID:   }
+# BUILDID: ]
 --- !COFF
 header:
   Machine:         IMAGE_FILE_MACHINE_I386

>From 0460323822e26c1d4f83642c2e48e38799358e22 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Tue, 7 Nov 2023 15:39:50 -0500
Subject: [PATCH 02/13] Address comments.

---
 lld/COFF/Config.h       |  9 +++++-
 lld/COFF/Driver.cpp     | 20 ++++++++-----
 lld/COFF/Options.td     |  2 +-
 lld/COFF/Writer.cpp     | 65 ++++++++++++++++++++++++++---------------
 lld/MinGW/Options.td    |  1 -
 lld/test/COFF/rsds.test | 21 +++++++++++++
 6 files changed, 84 insertions(+), 34 deletions(-)

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 4e4811357d26a..4186cc934b24f 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -102,6 +102,12 @@ enum class ICFLevel {
         // behavior.
 };
 
+enum class BuildIDHash {
+  None,
+  PDB,
+  Binary,
+};
+
 // Global configuration.
 struct Configuration {
   enum ManifestKind { Default, SideBySide, Embed, No };
@@ -317,7 +323,8 @@ struct Configuration {
   bool writeCheckSum = false;
   EmitKind emit = EmitKind::Obj;
   bool allowDuplicateWeak = false;
-  bool buildID = false;
+  bool shouldCreatePDB = false;
+  BuildIDHash buildIDHash = BuildIDHash::None;
 };
 
 } // namespace lld::coff
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 0fa6fd6d34cf6..b0991c023f20a 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1672,10 +1672,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       config->driverUponly || config->driverWdm || args.hasArg(OPT_driver);
 
   // Handle /pdb
-  bool shouldCreatePDB =
+  config->shouldCreatePDB =
       (debug == DebugKind::Full || debug == DebugKind::GHash ||
        debug == DebugKind::NoGHash);
-  if (shouldCreatePDB) {
+  if (config->shouldCreatePDB) {
     if (auto *arg = args.getLastArg(OPT_pdb))
       config->pdbPath = arg->getValue();
     if (auto *arg = args.getLastArg(OPT_pdbaltpath))
@@ -2309,7 +2309,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     config->lldmapFile.clear();
   }
 
-  if (shouldCreatePDB) {
+  if (config->shouldCreatePDB) {
     // Put the PDB next to the image if no /pdb flag was passed.
     if (config->pdbPath.empty()) {
       config->pdbPath = config->outputFile;
@@ -2332,11 +2332,15 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     }
   }
 
-  // Generate build id hash in .buildid section when:
-  // 1. /build-id
-  // 2. /lldmingw + /debug and not generating pdb file.
-  config->buildID = args.hasArg(OPT_build_id) ||
-                    (config->mingw && config->debug && !shouldCreatePDB);
+  // Generate build id hash in .buildid section if /build-id is given or
+  // /lldmingw + /debug are given:
+  // 1. If PDB is generated, the build id hash will be the hash of PDB content.
+  // 2. Otherwise, the build id hash will be the hash of executable content.
+  if (args.hasFlag(OPT_build_id, OPT_build_id_no,
+                   config->mingw && config->debug))
+    config->buildIDHash =
+        config->shouldCreatePDB ? BuildIDHash::PDB : BuildIDHash::Binary;
+
   // Set default image base if /base is not given.
   if (config->imageBase == uint64_t(-1))
     config->imageBase = getDefaultImageBase();
diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index cc7ce4fdbeea0..436b65b22d838 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -299,7 +299,7 @@ def : Flag<["--"], "time-trace">, Alias<time_trace_eq>,
 def time_trace_granularity_eq: Joined<["--"], "time-trace-granularity=">,
     HelpText<"Minimum time granularity (in microseconds) traced by time profiler">;
 
-def build_id: F<"build-id">, HelpText<"Generate build ID">;
+defm build_id: B<"build-id", "Generate build ID", "Do not Generate build ID">;
 
 // Flags for debugging
 def lldmap : F<"lldmap">;
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index dca148a62e9d1..490c265a9dd0c 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -86,10 +86,8 @@ namespace {
 
 class DebugDirectoryChunk : public NonSectionChunk {
 public:
-  DebugDirectoryChunk(const COFFLinkerContext &c,
-                      const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
-                      bool writeRepro)
-      : records(r), writeRepro(writeRepro), ctx(c) {}
+  DebugDirectoryChunk(const COFFLinkerContext &c, bool writeRepro)
+      : writeRepro(writeRepro), ctx(c) {}
 
   size_t getSize() const override {
     return (records.size() + int(writeRepro)) * sizeof(debug_directory);
@@ -121,6 +119,18 @@ class DebugDirectoryChunk : public NonSectionChunk {
       *tds = timeDateStamp;
   }
 
+  void addRecord(COFF::DebugType type, Chunk *c) {
+    records.emplace_back(type, c);
+  }
+
+  Chunk* getBuildIdChunk() {
+    for (const std::pair<COFF::DebugType, Chunk *> &record : records) {
+      if (record.first == COFF::IMAGE_DEBUG_TYPE_CODEVIEW)
+        return record.second;
+    }
+    return nullptr;
+  }
+
 private:
   void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size,
                  uint64_t rva, uint64_t offs) const {
@@ -137,7 +147,7 @@ class DebugDirectoryChunk : public NonSectionChunk {
   }
 
   mutable std::vector<support::ulittle32_t *> timeDateStamps;
-  const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
+  std::vector<std::pair<COFF::DebugType, Chunk *>> records;
   bool writeRepro;
   const COFFLinkerContext &ctx;
 };
@@ -284,7 +294,6 @@ class Writer {
   uint32_t tlsAlignment = 0;
 
   DebugDirectoryChunk *debugDirectory = nullptr;
-  std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
   CVDebugRecordChunk *buildId = nullptr;
   ArrayRef<uint8_t> sectionTable;
 
@@ -1040,15 +1049,16 @@ void Writer::createMiscChunks() {
   }
 
   // Create Debug Information Chunks
-  OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
-  if (config->buildID || config->debug || config->repro || config->cetCompat) {
-    debugDirectory =
-        make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
+  std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
+  OutputSection *debugInfoSec = config->shouldCreatePDB ? rdataSec : buildidSec;
+  if (config->buildIDHash != BuildIDHash::None || config->debug ||
+      config->repro || config->cetCompat) {
+    debugDirectory = make<DebugDirectoryChunk>(ctx, config->repro);
     debugDirectory->setAlignment(4);
     debugInfoSec->addChunk(debugDirectory);
   }
 
-  if (config->debug || config->buildID) {
+  if (config->debug || config->buildIDHash != BuildIDHash::None) {
     // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified.  We
     // output a PDB no matter what, and this chunk provides the only means of
     // allowing a debugger to match a PDB and an executable.  So we need it even
@@ -1067,16 +1077,19 @@ void Writer::createMiscChunks() {
   for (std::pair<COFF::DebugType, Chunk *> r : debugRecords) {
     r.second->setAlignment(4);
     debugInfoSec->addChunk(r.second);
+    debugDirectory->addRecord(r.first, r.second);
   }
 
-  // If .buildid section was not chosen and /build-id is given, create .buildid
-  // section.
-  if (config->buildID && debugInfoSec != buildidSec) {
+  // Create extra .buildid section if build id was stored in .rdata.
+  if (config->buildIDHash == BuildIDHash::PDB) {
+    DebugDirectoryChunk *debugDirectory =
+        make<DebugDirectoryChunk>(ctx, config->repro);
+    debugDirectory->setAlignment(4);
+    CVDebugRecordChunk *buildId = make<CVDebugRecordChunk>(ctx);
+    buildId->setAlignment(4);
+    debugDirectory->addRecord(COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId);
     buildidSec->addChunk(debugDirectory);
-    for (std::pair<COFF::DebugType, Chunk *> r : debugRecords) {
-      r.second->setAlignment(4);
-      buildidSec->addChunk(r.second);
-    }
+    buildidSec->addChunk(buildId);
   }
 
   // Create SEH table. x86-only.
@@ -2038,7 +2051,7 @@ void Writer::writeBuildId() {
   // PE contents.
   Configuration *config = &ctx.config;
 
-  if (config->debug || config->buildID) {
+  if (config->debug || config->buildIDHash != BuildIDHash::None) {
     assert(buildId && "BuildId is not set!");
     // BuildId->BuildId was filled in when the PDB was written.
   }
@@ -2054,15 +2067,13 @@ void Writer::writeBuildId() {
   uint32_t timestamp = config->timestamp;
   uint64_t hash = 0;
 
-  if (config->repro || config->buildID)
+  if (config->repro || config->buildIDHash != BuildIDHash::None)
     hash = xxh3_64bits(outputFileData);
 
   if (config->repro)
     timestamp = static_cast<uint32_t>(hash);
 
-  if (config->buildID) {
-    // For MinGW builds without a PDB file, we still generate a build id
-    // to allow associating a crash dump to the executable.
+  if (config->buildIDHash == BuildIDHash::Binary) {
     buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70;
     buildId->buildId->PDB70.Age = 1;
     memcpy(buildId->buildId->PDB70.Signature, &hash, 8);
@@ -2070,6 +2081,14 @@ void Writer::writeBuildId() {
     memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8);
   }
 
+  // If using PDB hash, build id in .buildid section is not set yet.
+  if (config->buildIDHash == BuildIDHash::PDB) {
+    auto *buildIdChunk = buildidSec->chunks.back();
+    codeview::DebugInfo *buildIdInfo =
+        cast<CVDebugRecordChunk>(buildIdChunk)->buildId;
+    memcpy(buildIdInfo, buildId->buildId, sizeof(codeview::DebugInfo));
+  }
+
   if (debugDirectory)
     debugDirectory->setTimeDateStamp(timestamp);
 
diff --git a/lld/MinGW/Options.td b/lld/MinGW/Options.td
index 87b5c4fff68ce..3f3de22f2bd10 100644
--- a/lld/MinGW/Options.td
+++ b/lld/MinGW/Options.td
@@ -196,7 +196,6 @@ defm guard_longjmp : B<"guard-longjmp",
   "Do not enable Control Flow Guard long jump hardening">;
 defm error_limit:
   EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
-def build_id: F<"build-id">, HelpText<"Generate build ID">;
 
 // Alias
 def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
diff --git a/lld/test/COFF/rsds.test b/lld/test/COFF/rsds.test
index 783ac08255c03..0ee015574cbea 100644
--- a/lld/test/COFF/rsds.test
+++ b/lld/test/COFF/rsds.test
@@ -35,10 +35,26 @@
 # RUN: rm -f %t.dll %t.pdb
 # RUN: lld-link /build-id /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
+# RUN: llvm-readobj --coff-debug-directory %t.dll | grep "PDBGUID: " > %t.binary.guid
 
+# When generating PDB, generate extra .buildid section.
 # RUN: rm -f %t.dll %t.pdb
 # RUN: lld-link /build-id /debug /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
+# RUN: llvm-readobj -S %t.dll | FileCheck --check-prefix BUILDID_SEC %s
+
+# RUN: llvm-readobj --coff-debug-directory %t.dll | grep "PDBGUID: " > %t.pdb.guid
+# PDB hash should be different from binary hash.
+# RUN: not diff %t.binary.guid %t.pdb.guid
+
+# Do not generate .buildid section under /lldmingw + /debug:dwarf + /build-id:no
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /lldmingw /debug:dwarf /build-id:no /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix NO_BUILDID %s
+
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix NO_BUILDID %s
 
 # CHECK: File: [[FILE:.*]].dll
 # CHECK: DebugDirectory [
@@ -181,6 +197,11 @@
 # BUILDID:     }
 # BUILDID:   }
 # BUILDID: ]
+
+# NO_BUILDID: DebugDirectory [
+# NO_BUILDID: ]
+
+# BUILDID_SEC: Name: .buildid
 --- !COFF
 header:
   Machine:         IMAGE_FILE_MACHINE_I386

>From c50f440417289071194074ea38873f0965be1bf0 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Tue, 7 Nov 2023 16:42:53 -0500
Subject: [PATCH 03/13] Always add -build-id in MinGW driver unless
 -no-build-id is given

---
 lld/COFF/Driver.cpp        |  6 ++----
 lld/COFF/Writer.cpp        |  2 +-
 lld/MinGW/Driver.cpp       |  1 +
 lld/MinGW/Options.td       |  1 +
 lld/test/COFF/rsds.test    | 13 ++-----------
 lld/test/MinGW/driver.test |  6 ++++++
 6 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index b0991c023f20a..116c4a1f86172 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2332,12 +2332,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     }
   }
 
-  // Generate build id hash in .buildid section if /build-id is given or
-  // /lldmingw + /debug are given:
+  // Generate build id hash in .buildid section if /build-id is given:
   // 1. If PDB is generated, the build id hash will be the hash of PDB content.
   // 2. Otherwise, the build id hash will be the hash of executable content.
-  if (args.hasFlag(OPT_build_id, OPT_build_id_no,
-                   config->mingw && config->debug))
+  if (args.hasFlag(OPT_build_id, OPT_build_id_no, false))
     config->buildIDHash =
         config->shouldCreatePDB ? BuildIDHash::PDB : BuildIDHash::Binary;
 
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 490c265a9dd0c..eff292bd9449f 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -123,7 +123,7 @@ class DebugDirectoryChunk : public NonSectionChunk {
     records.emplace_back(type, c);
   }
 
-  Chunk* getBuildIdChunk() {
+  Chunk *getBuildIdChunk() {
     for (const std::pair<COFF::DebugType, Chunk *> &record : records) {
       if (record.first == COFF::IMAGE_DEBUG_TYPE_CODEVIEW)
         return record.second;
diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index 19bf2d1617057..48e5f434093c0 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -302,6 +302,7 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
   } else if (!args.hasArg(OPT_strip_all)) {
     add("-debug:dwarf");
   }
+  add(args.hasArg(OPT_no_build_id) ? "-build-id:no" : "-build-id");
 
   if (args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false))
     add("-WX");
diff --git a/lld/MinGW/Options.td b/lld/MinGW/Options.td
index 3f3de22f2bd10..0cdc9fea7de3a 100644
--- a/lld/MinGW/Options.td
+++ b/lld/MinGW/Options.td
@@ -196,6 +196,7 @@ defm guard_longjmp : B<"guard-longjmp",
   "Do not enable Control Flow Guard long jump hardening">;
 defm error_limit:
   EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
+defm build_id: B<"build-id", "Generate build ID", "Do not Generate build ID">;
 
 // Alias
 def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
diff --git a/lld/test/COFF/rsds.test b/lld/test/COFF/rsds.test
index 0ee015574cbea..9b82b6f700919 100644
--- a/lld/test/COFF/rsds.test
+++ b/lld/test/COFF/rsds.test
@@ -22,15 +22,6 @@
 # RUN: lld-link /Brepro /debug /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix REPRODEBUG %s
 
-# Generate .buildid section when /lldmingw + /debug:dwarf are given and not generating pdb file.
-# RUN: rm -f %t.dll %t.pdb
-# RUN: lld-link /lldmingw /debug:dwarf /dll /out:%t.dll /entry:DllMain %t.obj
-# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
-
-# RUN: rm -f %t.dll %t.pdb
-# RUN: lld-link /lldmingw /debug:dwarf /build-id /dll /out:%t.dll /entry:DllMain %t.obj
-# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
-
 # Generate .buildid section when /build-id is given.
 # RUN: rm -f %t.dll %t.pdb
 # RUN: lld-link /build-id /dll /out:%t.dll /entry:DllMain %t.obj
@@ -47,9 +38,9 @@
 # PDB hash should be different from binary hash.
 # RUN: not diff %t.binary.guid %t.pdb.guid
 
-# Do not generate .buildid section under /lldmingw + /debug:dwarf + /build-id:no
+# Do not generate .buildid section under /build-id:no
 # RUN: rm -f %t.dll %t.pdb
-# RUN: lld-link /lldmingw /debug:dwarf /build-id:no /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: lld-link /build-id:no /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix NO_BUILDID %s
 
 # RUN: rm -f %t.dll %t.pdb
diff --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index a07c95edb580d..bab2fdca9b834 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -389,3 +389,9 @@ Test GCC specific LTO options that GCC passes unconditionally, that we ignore.
 
 RUN: ld.lld -### foo.o -m i386pep -plugin /usr/lib/gcc/x86_64-w64-mingw32/10-posix/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-w64-mingw32/10-posix/lto-wrapper -plugin-opt=-fresolution=/tmp/ccM9d4fP.res -plugin-opt=-pass-through=-lmingw32 2> /dev/null
 RUN: ld.lld -### foo.o -m i386pep -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/liblto_plugin.dll -plugin-opt=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/lto-wrapper.exe -plugin-opt=-fresolution=C:/msys64/tmp/cckbC7wB.res -plugin-opt=-pass-through=-lmingw32 2> /dev/null
+
+RUN: ld.lld -### foo.o -m i386pep 2>&1 | FileCheck -check-prefix=BUILD_ID %s
+BUILD_ID:-build-id
+
+RUN: ld.lld -### foo.o -m i386pep -no-build-id 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
+NO_BUILD_ID:-build-id:no

>From e2dfe8af35a587e034923e706e39c1fc4168073c Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Tue, 7 Nov 2023 17:59:52 -0500
Subject: [PATCH 04/13] Use -build-id=none format

---
 lld/MinGW/Driver.cpp       | 11 ++++++++++-
 lld/MinGW/Options.td       |  3 ++-
 lld/test/MinGW/driver.test |  2 +-
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index 48e5f434093c0..922ea2a035ba6 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -302,7 +302,16 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
   } else if (!args.hasArg(OPT_strip_all)) {
     add("-debug:dwarf");
   }
-  add(args.hasArg(OPT_no_build_id) ? "-build-id:no" : "-build-id");
+  if (auto *a = args.getLastArg(OPT_build_id)) {
+    StringRef v = a->getValue();
+    if (v == "none")
+      add("-build-id:no");
+    else if (v.empty())
+      add("-build-id");
+    else
+      warn("unsupported build id hashing: " + a->getSpelling());
+  } else
+    add("-build-id");
 
   if (args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false))
     add("-WX");
diff --git a/lld/MinGW/Options.td b/lld/MinGW/Options.td
index 0cdc9fea7de3a..29f3da25d8be1 100644
--- a/lld/MinGW/Options.td
+++ b/lld/MinGW/Options.td
@@ -196,7 +196,8 @@ defm guard_longjmp : B<"guard-longjmp",
   "Do not enable Control Flow Guard long jump hardening">;
 defm error_limit:
   EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
-defm build_id: B<"build-id", "Generate build ID", "Do not Generate build ID">;
+def build_id: J<"build-id=">, HelpText<"Generate build ID note (pass none to disable)">, 
+  MetaVarName<"<arg>">;
 
 // Alias
 def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
diff --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index bab2fdca9b834..ade964dc655f1 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -393,5 +393,5 @@ RUN: ld.lld -### foo.o -m i386pep -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_6
 RUN: ld.lld -### foo.o -m i386pep 2>&1 | FileCheck -check-prefix=BUILD_ID %s
 BUILD_ID:-build-id
 
-RUN: ld.lld -### foo.o -m i386pep -no-build-id 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
+RUN: ld.lld -### foo.o -m i386pep -build-id=none 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
 NO_BUILD_ID:-build-id:no

>From 44cd8983b87747a114e5783ade14a07c3d0bb40f Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Wed, 8 Nov 2023 12:33:50 -0500
Subject: [PATCH 05/13] address comments

---
 lld/MinGW/Driver.cpp       | 8 +++++---
 lld/MinGW/Options.td       | 1 +
 lld/test/MinGW/driver.test | 6 +++++-
 3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index 922ea2a035ba6..91a8ff72145d5 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -306,10 +306,12 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
     StringRef v = a->getValue();
     if (v == "none")
       add("-build-id:no");
-    else if (v.empty())
+    else {
+      if (!v.empty())
+        warn("unsupported build id hashing: " + v +
+             ", using default hashing.");
       add("-build-id");
-    else
-      warn("unsupported build id hashing: " + a->getSpelling());
+    }
   } else
     add("-build-id");
 
diff --git a/lld/MinGW/Options.td b/lld/MinGW/Options.td
index 29f3da25d8be1..d4a49cdbd5359 100644
--- a/lld/MinGW/Options.td
+++ b/lld/MinGW/Options.td
@@ -198,6 +198,7 @@ defm error_limit:
   EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
 def build_id: J<"build-id=">, HelpText<"Generate build ID note (pass none to disable)">, 
   MetaVarName<"<arg>">;
+def : F<"build-id">, Alias<build_id>, HelpText<"Alias for --build-id=">;
 
 // Alias
 def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
diff --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index ade964dc655f1..3ab9bf5a8b954 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -391,7 +391,11 @@ RUN: ld.lld -### foo.o -m i386pep -plugin /usr/lib/gcc/x86_64-w64-mingw32/10-pos
 RUN: ld.lld -### foo.o -m i386pep -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/liblto_plugin.dll -plugin-opt=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/lto-wrapper.exe -plugin-opt=-fresolution=C:/msys64/tmp/cckbC7wB.res -plugin-opt=-pass-through=-lmingw32 2> /dev/null
 
 RUN: ld.lld -### foo.o -m i386pep 2>&1 | FileCheck -check-prefix=BUILD_ID %s
+RUN: ld.lld -### foo.o -m i386pep --build-id 2>&1 | FileCheck -check-prefix=BUILD_ID %s
 BUILD_ID:-build-id
 
-RUN: ld.lld -### foo.o -m i386pep -build-id=none 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
+RUN: ld.lld -### foo.o -m i386pep --build-id=fast 2>&1 | FileCheck -check-prefix=BUILD_ID_WARN %s
+BUILD_ID_WARN: unsupported build id hashing: fast, using default hashing.
+
+RUN: ld.lld -### foo.o -m i386pep --build-id=none 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
 NO_BUILD_ID:-build-id:no

>From 6255326f7e4021e9d2998f55500e09b48719952b Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Wed, 8 Nov 2023 14:00:44 -0500
Subject: [PATCH 06/13] fix format.

---
 lld/MinGW/Driver.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index 91a8ff72145d5..ea35848c16ab6 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -308,8 +308,7 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
       add("-build-id:no");
     else {
       if (!v.empty())
-        warn("unsupported build id hashing: " + v +
-             ", using default hashing.");
+        warn("unsupported build id hashing: " + v + ", using default hashing.");
       add("-build-id");
     }
   } else

>From ebcb3d0f6a8ea21f3dbc924387df0d69558bac31 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Wed, 8 Nov 2023 16:00:23 -0500
Subject: [PATCH 07/13] address comment and fix the case when .buildid
 shouldn't be generated.

---
 lld/COFF/Writer.cpp        | 24 +++++++++++++-----------
 lld/test/MinGW/driver.test |  7 +++++--
 2 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index eff292bd9449f..8415e693ca7d8 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -123,14 +123,6 @@ class DebugDirectoryChunk : public NonSectionChunk {
     records.emplace_back(type, c);
   }
 
-  Chunk *getBuildIdChunk() {
-    for (const std::pair<COFF::DebugType, Chunk *> &record : records) {
-      if (record.first == COFF::IMAGE_DEBUG_TYPE_CODEVIEW)
-        return record.second;
-    }
-    return nullptr;
-  }
-
 private:
   void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size,
                  uint64_t rva, uint64_t offs) const {
@@ -314,6 +306,8 @@ class Writer {
   OutputSection *relocSec;
   OutputSection *ctorsSec;
   OutputSection *dtorsSec;
+  // Either .rdata section or .buildid section.
+  OutputSection *debugInfoSec;
 
   // The first and last .pdata sections in the output file.
   //
@@ -1050,7 +1044,13 @@ void Writer::createMiscChunks() {
 
   // Create Debug Information Chunks
   std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
-  OutputSection *debugInfoSec = config->shouldCreatePDB ? rdataSec : buildidSec;
+  // The default debug info section is .rdata. It set to .buildid section only
+  // when not generating PDB and /build-id flag is given. If .rdata is chosen
+  // and /build-id is given, an extra .buildid section will be generated.
+  debugInfoSec =
+      config->shouldCreatePDB || config->buildIDHash != BuildIDHash::Binary
+          ? rdataSec
+          : buildidSec;
   if (config->buildIDHash != BuildIDHash::None || config->debug ||
       config->repro || config->cetCompat) {
     debugDirectory = make<DebugDirectoryChunk>(ctx, config->repro);
@@ -1081,7 +1081,8 @@ void Writer::createMiscChunks() {
   }
 
   // Create extra .buildid section if build id was stored in .rdata.
-  if (config->buildIDHash == BuildIDHash::PDB) {
+  if (debugInfoSec != buildidSec &&
+      config->buildIDHash == BuildIDHash::Binary) {
     DebugDirectoryChunk *debugDirectory =
         make<DebugDirectoryChunk>(ctx, config->repro);
     debugDirectory->setAlignment(4);
@@ -2082,7 +2083,8 @@ void Writer::writeBuildId() {
   }
 
   // If using PDB hash, build id in .buildid section is not set yet.
-  if (config->buildIDHash == BuildIDHash::PDB) {
+  if (debugInfoSec != buildidSec &&
+      config->buildIDHash == BuildIDHash::Binary) {
     auto *buildIdChunk = buildidSec->chunks.back();
     codeview::DebugInfo *buildIdInfo =
         cast<CVDebugRecordChunk>(buildIdChunk)->buildId;
diff --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index 3ab9bf5a8b954..d618a8c6e51db 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -392,10 +392,13 @@ RUN: ld.lld -### foo.o -m i386pep -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_6
 
 RUN: ld.lld -### foo.o -m i386pep 2>&1 | FileCheck -check-prefix=BUILD_ID %s
 RUN: ld.lld -### foo.o -m i386pep --build-id 2>&1 | FileCheck -check-prefix=BUILD_ID %s
-BUILD_ID:-build-id
+BUILD_ID: -build-id
+BUILD_ID-NOT: -build-id:no
 
 RUN: ld.lld -### foo.o -m i386pep --build-id=fast 2>&1 | FileCheck -check-prefix=BUILD_ID_WARN %s
 BUILD_ID_WARN: unsupported build id hashing: fast, using default hashing.
+BUILD_ID_WARN: -build-id
+BUILD_ID_WARN-NOT: -build-id:no
 
 RUN: ld.lld -### foo.o -m i386pep --build-id=none 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
-NO_BUILD_ID:-build-id:no
+NO_BUILD_ID: -build-id:no

>From 97a191ed33cbbd769503fd546fe70f8402d3fb9f Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Wed, 8 Nov 2023 18:11:16 -0500
Subject: [PATCH 08/13] fixup! address comment and fix the case when .buildid
 shouldn't be generated.

---
 lld/COFF/Writer.cpp         | 10 ++++------
 lld/test/COFF/debug-reloc.s |  2 +-
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 8415e693ca7d8..e7fe7a9224c92 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1079,10 +1079,9 @@ void Writer::createMiscChunks() {
     debugInfoSec->addChunk(r.second);
     debugDirectory->addRecord(r.first, r.second);
   }
-
-  // Create extra .buildid section if build id was stored in .rdata.
-  if (debugInfoSec != buildidSec &&
-      config->buildIDHash == BuildIDHash::Binary) {
+  // Create extra .buildid section if build id was stored in .rdata and
+  // /build-id is given.
+  if (debugInfoSec != buildidSec && config->buildIDHash != BuildIDHash::None) {
     DebugDirectoryChunk *debugDirectory =
         make<DebugDirectoryChunk>(ctx, config->repro);
     debugDirectory->setAlignment(4);
@@ -2083,8 +2082,7 @@ void Writer::writeBuildId() {
   }
 
   // If using PDB hash, build id in .buildid section is not set yet.
-  if (debugInfoSec != buildidSec &&
-      config->buildIDHash == BuildIDHash::Binary) {
+  if (debugInfoSec != buildidSec && config->buildIDHash != BuildIDHash::None) {
     auto *buildIdChunk = buildidSec->chunks.back();
     codeview::DebugInfo *buildIdInfo =
         cast<CVDebugRecordChunk>(buildIdChunk)->buildId;
diff --git a/lld/test/COFF/debug-reloc.s b/lld/test/COFF/debug-reloc.s
index bdf2563156540..68992414bd97c 100644
--- a/lld/test/COFF/debug-reloc.s
+++ b/lld/test/COFF/debug-reloc.s
@@ -2,7 +2,7 @@
 
 # RUN: llvm-mc -triple=x86_64-windows-gnu %s -filetype=obj -o %t.obj
 
-# RUN: lld-link -lldmingw -debug:dwarf -out:%t.exe -entry:mainfunc -subsystem:console %t.obj
+# RUN: lld-link -lldmingw -debug:dwarf -build-id -out:%t.exe -entry:mainfunc -subsystem:console %t.obj
 # RUN: llvm-readobj --sections %t.exe | FileCheck %s -check-prefix SECTIONS
 # RUN: llvm-readobj --coff-basereloc %t.exe | FileCheck %s -check-prefix RELOCS
 # RUN: llvm-readobj --file-headers %t.exe | FileCheck %s -check-prefix HEADERS

>From c336d53dc83049d2415e2c1e8ea325aa1ba763a4 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Thu, 9 Nov 2023 11:46:48 -0500
Subject: [PATCH 09/13] Fix test case and typo.

---
 lld/COFF/Writer.cpp        | 6 +++---
 lld/test/MinGW/driver.test | 8 +++-----
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index e7fe7a9224c92..09365336be3b1 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1044,9 +1044,9 @@ void Writer::createMiscChunks() {
 
   // Create Debug Information Chunks
   std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
-  // The default debug info section is .rdata. It set to .buildid section only
-  // when not generating PDB and /build-id flag is given. If .rdata is chosen
-  // and /build-id is given, an extra .buildid section will be generated.
+  // The default debug info section is .rdata. It is set to .buildid section
+  // only when not generating PDB and /build-id flag is given. If .rdata is
+  // chosen and /build-id is given, an extra .buildid section will be generated.
   debugInfoSec =
       config->shouldCreatePDB || config->buildIDHash != BuildIDHash::Binary
           ? rdataSec
diff --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index d618a8c6e51db..ebedc71215a71 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -391,14 +391,12 @@ RUN: ld.lld -### foo.o -m i386pep -plugin /usr/lib/gcc/x86_64-w64-mingw32/10-pos
 RUN: ld.lld -### foo.o -m i386pep -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/liblto_plugin.dll -plugin-opt=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/lto-wrapper.exe -plugin-opt=-fresolution=C:/msys64/tmp/cckbC7wB.res -plugin-opt=-pass-through=-lmingw32 2> /dev/null
 
 RUN: ld.lld -### foo.o -m i386pep 2>&1 | FileCheck -check-prefix=BUILD_ID %s
-RUN: ld.lld -### foo.o -m i386pep --build-id 2>&1 | FileCheck -check-prefix=BUILD_ID %s
-BUILD_ID: -build-id
-BUILD_ID-NOT: -build-id:no
+RUN: ld.lld -### foo.o -m i386pep --build-id=none 2>&1 | FileCheck -check-prefix=BUILD_ID %s
+BUILD_ID: -build-id{{ }}
 
 RUN: ld.lld -### foo.o -m i386pep --build-id=fast 2>&1 | FileCheck -check-prefix=BUILD_ID_WARN %s
 BUILD_ID_WARN: unsupported build id hashing: fast, using default hashing.
-BUILD_ID_WARN: -build-id
-BUILD_ID_WARN-NOT: -build-id:no
+BUILD_ID_WARN: -build-id{{ }}
 
 RUN: ld.lld -### foo.o -m i386pep --build-id=none 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
 NO_BUILD_ID: -build-id:no

>From 9ce0eee58bc18930787b99ccdc22a0b12d927e48 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Fri, 10 Nov 2023 13:28:44 -0500
Subject: [PATCH 10/13] Fix typo in test.

---
 lld/test/MinGW/driver.test | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index ebedc71215a71..11dcf3ebc06d8 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -391,7 +391,7 @@ RUN: ld.lld -### foo.o -m i386pep -plugin /usr/lib/gcc/x86_64-w64-mingw32/10-pos
 RUN: ld.lld -### foo.o -m i386pep -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/liblto_plugin.dll -plugin-opt=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/lto-wrapper.exe -plugin-opt=-fresolution=C:/msys64/tmp/cckbC7wB.res -plugin-opt=-pass-through=-lmingw32 2> /dev/null
 
 RUN: ld.lld -### foo.o -m i386pep 2>&1 | FileCheck -check-prefix=BUILD_ID %s
-RUN: ld.lld -### foo.o -m i386pep --build-id=none 2>&1 | FileCheck -check-prefix=BUILD_ID %s
+RUN: ld.lld -### foo.o -m i386pep --build-id 2>&1 | FileCheck -check-prefix=BUILD_ID %s
 BUILD_ID: -build-id{{ }}
 
 RUN: ld.lld -### foo.o -m i386pep --build-id=fast 2>&1 | FileCheck -check-prefix=BUILD_ID_WARN %s

>From 66ddabdb5e3e3340e66f1297bfce4b19ec74e5d7 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Mon, 20 Nov 2023 12:53:58 -0500
Subject: [PATCH 11/13] Only put debug direcotry into .buildid section when
 under MinGW.

---
 lld/COFF/Config.h       |  1 -
 lld/COFF/Driver.cpp     | 19 +++++++++----------
 lld/COFF/Writer.cpp     | 36 +++++-------------------------------
 lld/test/COFF/rsds.test | 18 +++++++++---------
 4 files changed, 23 insertions(+), 51 deletions(-)

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 4186cc934b24f..3a26f94f17e74 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -323,7 +323,6 @@ struct Configuration {
   bool writeCheckSum = false;
   EmitKind emit = EmitKind::Obj;
   bool allowDuplicateWeak = false;
-  bool shouldCreatePDB = false;
   BuildIDHash buildIDHash = BuildIDHash::None;
 };
 
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 116c4a1f86172..286a98519824f 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1672,10 +1672,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       config->driverUponly || config->driverWdm || args.hasArg(OPT_driver);
 
   // Handle /pdb
-  config->shouldCreatePDB =
+  bool shouldCreatePDB =
       (debug == DebugKind::Full || debug == DebugKind::GHash ||
        debug == DebugKind::NoGHash);
-  if (config->shouldCreatePDB) {
+  if (shouldCreatePDB) {
     if (auto *arg = args.getLastArg(OPT_pdb))
       config->pdbPath = arg->getValue();
     if (auto *arg = args.getLastArg(OPT_pdbaltpath))
@@ -2309,7 +2309,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     config->lldmapFile.clear();
   }
 
-  if (config->shouldCreatePDB) {
+  // If should create PDB, use the hash of PDB content for build id. Otherwise,
+  // generate using the hash of executable content.
+  if (args.hasFlag(OPT_build_id, OPT_build_id_no, false))
+    config->buildIDHash = BuildIDHash::Binary;
+
+  if (shouldCreatePDB) {
     // Put the PDB next to the image if no /pdb flag was passed.
     if (config->pdbPath.empty()) {
       config->pdbPath = config->outputFile;
@@ -2330,15 +2335,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       // Don't do this earlier, so that ctx.OutputFile is ready.
       parsePDBAltPath();
     }
+    config->buildIDHash = BuildIDHash::PDB;
   }
 
-  // Generate build id hash in .buildid section if /build-id is given:
-  // 1. If PDB is generated, the build id hash will be the hash of PDB content.
-  // 2. Otherwise, the build id hash will be the hash of executable content.
-  if (args.hasFlag(OPT_build_id, OPT_build_id_no, false))
-    config->buildIDHash =
-        config->shouldCreatePDB ? BuildIDHash::PDB : BuildIDHash::Binary;
-
   // Set default image base if /base is not given.
   if (config->imageBase == uint64_t(-1))
     config->imageBase = getDefaultImageBase();
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 09365336be3b1..29bc2898bfde9 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1044,13 +1044,7 @@ void Writer::createMiscChunks() {
 
   // Create Debug Information Chunks
   std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
-  // The default debug info section is .rdata. It is set to .buildid section
-  // only when not generating PDB and /build-id flag is given. If .rdata is
-  // chosen and /build-id is given, an extra .buildid section will be generated.
-  debugInfoSec =
-      config->shouldCreatePDB || config->buildIDHash != BuildIDHash::Binary
-          ? rdataSec
-          : buildidSec;
+  debugInfoSec = config->mingw ? buildidSec : rdataSec;
   if (config->buildIDHash != BuildIDHash::None || config->debug ||
       config->repro || config->cetCompat) {
     debugDirectory = make<DebugDirectoryChunk>(ctx, config->repro);
@@ -1079,18 +1073,6 @@ void Writer::createMiscChunks() {
     debugInfoSec->addChunk(r.second);
     debugDirectory->addRecord(r.first, r.second);
   }
-  // Create extra .buildid section if build id was stored in .rdata and
-  // /build-id is given.
-  if (debugInfoSec != buildidSec && config->buildIDHash != BuildIDHash::None) {
-    DebugDirectoryChunk *debugDirectory =
-        make<DebugDirectoryChunk>(ctx, config->repro);
-    debugDirectory->setAlignment(4);
-    CVDebugRecordChunk *buildId = make<CVDebugRecordChunk>(ctx);
-    buildId->setAlignment(4);
-    debugDirectory->addRecord(COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId);
-    buildidSec->addChunk(debugDirectory);
-    buildidSec->addChunk(buildId);
-  }
 
   // Create SEH table. x86-only.
   if (config->safeSEH)
@@ -2050,8 +2032,8 @@ void Writer::writeBuildId() {
   // For reproducibility, instead of a timestamp we want to use a hash of the
   // PE contents.
   Configuration *config = &ctx.config;
-
-  if (config->debug || config->buildIDHash != BuildIDHash::None) {
+  bool generateSyntheticBuildId = config->buildIDHash == BuildIDHash::Binary;
+  if (generateSyntheticBuildId) {
     assert(buildId && "BuildId is not set!");
     // BuildId->BuildId was filled in when the PDB was written.
   }
@@ -2067,13 +2049,13 @@ void Writer::writeBuildId() {
   uint32_t timestamp = config->timestamp;
   uint64_t hash = 0;
 
-  if (config->repro || config->buildIDHash != BuildIDHash::None)
+  if (config->repro || generateSyntheticBuildId)
     hash = xxh3_64bits(outputFileData);
 
   if (config->repro)
     timestamp = static_cast<uint32_t>(hash);
 
-  if (config->buildIDHash == BuildIDHash::Binary) {
+  if (generateSyntheticBuildId) {
     buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70;
     buildId->buildId->PDB70.Age = 1;
     memcpy(buildId->buildId->PDB70.Signature, &hash, 8);
@@ -2081,14 +2063,6 @@ void Writer::writeBuildId() {
     memcpy(&buildId->buildId->PDB70.Signature[8], "LLD PDB.", 8);
   }
 
-  // If using PDB hash, build id in .buildid section is not set yet.
-  if (debugInfoSec != buildidSec && config->buildIDHash != BuildIDHash::None) {
-    auto *buildIdChunk = buildidSec->chunks.back();
-    codeview::DebugInfo *buildIdInfo =
-        cast<CVDebugRecordChunk>(buildIdChunk)->buildId;
-    memcpy(buildIdInfo, buildId->buildId, sizeof(codeview::DebugInfo));
-  }
-
   if (debugDirectory)
     debugDirectory->setTimeDateStamp(timestamp);
 
diff --git a/lld/test/COFF/rsds.test b/lld/test/COFF/rsds.test
index 9b82b6f700919..8704779fc1e3c 100644
--- a/lld/test/COFF/rsds.test
+++ b/lld/test/COFF/rsds.test
@@ -22,21 +22,21 @@
 # RUN: lld-link /Brepro /debug /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix REPRODEBUG %s
 
-# Generate .buildid section when /build-id is given.
+# Generate .buildid section using binary hash under /lldmingw and /build-id
 # RUN: rm -f %t.dll %t.pdb
-# RUN: lld-link /build-id /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: lld-link /lldmingw /build-id /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
-# RUN: llvm-readobj --coff-debug-directory %t.dll | grep "PDBGUID: " > %t.binary.guid
 
-# When generating PDB, generate extra .buildid section.
+# Generate debug directory with use binary hash when /build-id is given and not 
+# generating PDB.
 # RUN: rm -f %t.dll %t.pdb
-# RUN: lld-link /build-id /debug /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: lld-link /build-id /dll /out:%t.dll /entry:DllMain %t.obj
 # RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
-# RUN: llvm-readobj -S %t.dll | FileCheck --check-prefix BUILDID_SEC %s
 
-# RUN: llvm-readobj --coff-debug-directory %t.dll | grep "PDBGUID: " > %t.pdb.guid
-# PDB hash should be different from binary hash.
-# RUN: not diff %t.binary.guid %t.pdb.guid
+# If generate PDB, PDB hash is used and /build-id is ignored.
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /build-id /debug /pdbaltpath:test.pdb /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
 
 # Do not generate .buildid section under /build-id:no
 # RUN: rm -f %t.dll %t.pdb

>From 456b2803560d2a8d82d26dc9d5efbdd8184a5917 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Wed, 29 Nov 2023 13:48:06 -0500
Subject: [PATCH 12/13] address comments

---
 lld/COFF/Writer.cpp        | 18 ++++++++----------
 lld/MinGW/Driver.cpp       |  8 ++++++--
 lld/test/MinGW/driver.test |  2 ++
 3 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 29bc2898bfde9..22c73e634f26c 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -86,8 +86,10 @@ namespace {
 
 class DebugDirectoryChunk : public NonSectionChunk {
 public:
-  DebugDirectoryChunk(const COFFLinkerContext &c, bool writeRepro)
-      : writeRepro(writeRepro), ctx(c) {}
+  DebugDirectoryChunk(const COFFLinkerContext &c,
+                      const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
+                      bool writeRepro)
+      : records(r), writeRepro(writeRepro), ctx(c) {}
 
   size_t getSize() const override {
     return (records.size() + int(writeRepro)) * sizeof(debug_directory);
@@ -119,10 +121,6 @@ class DebugDirectoryChunk : public NonSectionChunk {
       *tds = timeDateStamp;
   }
 
-  void addRecord(COFF::DebugType type, Chunk *c) {
-    records.emplace_back(type, c);
-  }
-
 private:
   void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size,
                  uint64_t rva, uint64_t offs) const {
@@ -139,7 +137,7 @@ class DebugDirectoryChunk : public NonSectionChunk {
   }
 
   mutable std::vector<support::ulittle32_t *> timeDateStamps;
-  std::vector<std::pair<COFF::DebugType, Chunk *>> records;
+  const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
   bool writeRepro;
   const COFFLinkerContext &ctx;
 };
@@ -286,6 +284,7 @@ class Writer {
   uint32_t tlsAlignment = 0;
 
   DebugDirectoryChunk *debugDirectory = nullptr;
+  std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
   CVDebugRecordChunk *buildId = nullptr;
   ArrayRef<uint8_t> sectionTable;
 
@@ -1043,11 +1042,11 @@ void Writer::createMiscChunks() {
   }
 
   // Create Debug Information Chunks
-  std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
   debugInfoSec = config->mingw ? buildidSec : rdataSec;
   if (config->buildIDHash != BuildIDHash::None || config->debug ||
       config->repro || config->cetCompat) {
-    debugDirectory = make<DebugDirectoryChunk>(ctx, config->repro);
+    debugDirectory =
+        make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
     debugDirectory->setAlignment(4);
     debugInfoSec->addChunk(debugDirectory);
   }
@@ -1071,7 +1070,6 @@ void Writer::createMiscChunks() {
   for (std::pair<COFF::DebugType, Chunk *> r : debugRecords) {
     r.second->setAlignment(4);
     debugInfoSec->addChunk(r.second);
-    debugDirectory->addRecord(r.first, r.second);
   }
 
   // Create SEH table. x86-only.
diff --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index ea35848c16ab6..d22b617cf2f01 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -311,8 +311,12 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
         warn("unsupported build id hashing: " + v + ", using default hashing.");
       add("-build-id");
     }
-  } else
-    add("-build-id");
+  } else {
+    if (args.hasArg(OPT_strip_debug) || args.hasArg(OPT_strip_all))
+      add("-build-id:no");
+    else
+      add("-build-id");
+  }
 
   if (args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false))
     add("-WX");
diff --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index 11dcf3ebc06d8..d08c64258be89 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -399,4 +399,6 @@ BUILD_ID_WARN: unsupported build id hashing: fast, using default hashing.
 BUILD_ID_WARN: -build-id{{ }}
 
 RUN: ld.lld -### foo.o -m i386pep --build-id=none 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
+RUN: ld.lld -### foo.o -m i386pep -s 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
+RUN: ld.lld -### foo.o -m i386pep -S 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
 NO_BUILD_ID: -build-id:no

>From e36d2403478564ea91dbca73f9d80122b77d4007 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Tue, 5 Dec 2023 14:51:31 -0500
Subject: [PATCH 13/13] address comments.

---
 lld/COFF/Options.td     | 5 ++++-
 lld/test/COFF/rsds.test | 4 ++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index 436b65b22d838..46763207042fd 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -299,7 +299,10 @@ def : Flag<["--"], "time-trace">, Alias<time_trace_eq>,
 def time_trace_granularity_eq: Joined<["--"], "time-trace-granularity=">,
     HelpText<"Minimum time granularity (in microseconds) traced by time profiler">;
 
-defm build_id: B<"build-id", "Generate build ID", "Do not Generate build ID">;
+defm build_id: B<
+     "build-id", 
+     "Generate build ID (always on when generating PDB)",
+     "Do not Generate build ID">;
 
 // Flags for debugging
 def lldmap : F<"lldmap">;
diff --git a/lld/test/COFF/rsds.test b/lld/test/COFF/rsds.test
index 8704779fc1e3c..3b611c091e2ef 100644
--- a/lld/test/COFF/rsds.test
+++ b/lld/test/COFF/rsds.test
@@ -189,8 +189,8 @@
 # BUILDID:   }
 # BUILDID: ]
 
-# NO_BUILDID: DebugDirectory [
-# NO_BUILDID: ]
+# NO_BUILDID:      DebugDirectory [
+# NO_BUILDID-NEXT: ]
 
 # BUILDID_SEC: Name: .buildid
 --- !COFF



More information about the llvm-commits mailing list