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

via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 5 11:57:49 PST 2023


Author: Zequan Wu
Date: 2023-12-05T14:57:45-05:00
New Revision: aaf3a8ded47121c8ec8136f97a7a2c39112b3e59

URL: https://github.com/llvm/llvm-project/commit/aaf3a8ded47121c8ec8136f97a7a2c39112b3e59
DIFF: https://github.com/llvm/llvm-project/commit/aaf3a8ded47121c8ec8136f97a7a2c39112b3e59.diff

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

[RFC](https://discourse.llvm.org/t/rfc-add-build-id-flag-to-lld-link/74661)

Before, lld-link only generate the debug directory containing guid when
generating PDB with the hash of PDB content.

With this change, lld-link can generate the debug directory when only
`/build-id` is given:
1. If generating PDB, `/build-id` is ignored. Same behaviour as before.
2. Not generating PDB, using hash of the binary.
   - Not under MinGW, the debug directory is still in `.rdata` section.
   - Under MinGW, place the debug directory into new `.buildid` section.

Added: 
    

Modified: 
    lld/COFF/Config.h
    lld/COFF/Driver.cpp
    lld/COFF/Options.td
    lld/COFF/Writer.cpp
    lld/MinGW/Driver.cpp
    lld/MinGW/Options.td
    lld/test/COFF/debug-reloc.s
    lld/test/COFF/rsds.test
    lld/test/MinGW/driver.test

Removed: 
    


################################################################################
diff  --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index bee6dc3ec3cae..24126f635a06f 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 };
@@ -318,6 +324,7 @@ struct Configuration {
   bool writeCheckSum = false;
   EmitKind emit = EmitKind::Obj;
   bool allowDuplicateWeak = false;
+  BuildIDHash buildIDHash = BuildIDHash::None;
 };
 
 } // namespace lld::coff

diff  --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 5181520c0917e..99c1a60735adc 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2314,6 +2314,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     config->lldmapFile.clear();
   }
 
+  // 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()) {
@@ -2335,6 +2340,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       // Don't do this earlier, so that ctx.OutputFile is ready.
       parsePDBAltPath();
     }
+    config->buildIDHash = BuildIDHash::PDB;
   }
 
   // Set default image base if /base is not given.

diff  --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index abee660272689..4dab4a2071739 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -302,6 +302,11 @@ 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 (always on when generating PDB)",
+     "Do not 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 d6b1dbc8cc225..7b1ff8071e2e3 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -315,6 +315,8 @@ class Writer {
   OutputSection *relocSec;
   OutputSection *ctorsSec;
   OutputSection *dtorsSec;
+  // Either .rdata section or .buildid section.
+  OutputSection *debugInfoSec;
 
   // The range of .pdata sections in the output file.
   //
@@ -1103,15 +1105,16 @@ void Writer::createMiscChunks() {
   }
 
   // Create Debug Information Chunks
-  OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
-  if (config->debug || config->repro || config->cetCompat) {
+  debugInfoSec = config->mingw ? buildidSec : rdataSec;
+  if (config->buildIDHash != BuildIDHash::None || 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->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
@@ -2170,8 +2173,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) {
+  bool generateSyntheticBuildId = config->buildIDHash == BuildIDHash::Binary;
+  if (generateSyntheticBuildId) {
     assert(buildId && "BuildId is not set!");
     // BuildId->BuildId was filled in when the PDB was written.
   }
@@ -2186,8 +2189,6 @@ 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)
     hash = xxh3_64bits(outputFileData);
@@ -2196,8 +2197,6 @@ void Writer::writeBuildId() {
     timestamp = static_cast<uint32_t>(hash);
 
   if (generateSyntheticBuildId) {
-    // 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;
     buildId->buildId->PDB70.Age = 1;
     memcpy(buildId->buildId->PDB70.Signature, &hash, 8);

diff  --git a/lld/MinGW/Driver.cpp b/lld/MinGW/Driver.cpp
index 19bf2d1617057..d22b617cf2f01 100644
--- a/lld/MinGW/Driver.cpp
+++ b/lld/MinGW/Driver.cpp
@@ -302,6 +302,21 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
   } else if (!args.hasArg(OPT_strip_all)) {
     add("-debug:dwarf");
   }
+  if (auto *a = args.getLastArg(OPT_build_id)) {
+    StringRef v = a->getValue();
+    if (v == "none")
+      add("-build-id:no");
+    else {
+      if (!v.empty())
+        warn("unsupported build id hashing: " + v + ", using default hashing.");
+      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/MinGW/Options.td b/lld/MinGW/Options.td
index fa4c4ecc75d65..d4a49cdbd5359 100644
--- a/lld/MinGW/Options.td
+++ b/lld/MinGW/Options.td
@@ -196,6 +196,9 @@ 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: 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>;
@@ -213,7 +216,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/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

diff  --git a/lld/test/COFF/rsds.test b/lld/test/COFF/rsds.test
index 475249ca40566..3b611c091e2ef 100644
--- a/lld/test/COFF/rsds.test
+++ b/lld/test/COFF/rsds.test
@@ -22,9 +22,30 @@
 # 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 using binary hash under /lldmingw and /build-id
 # 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: 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
+
+# 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 /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
+
+# 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
+# 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
+# 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 [
@@ -148,25 +169,30 @@
 # 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: ]
+
+# NO_BUILDID:      DebugDirectory [
+# NO_BUILDID-NEXT: ]
+
+# BUILDID_SEC: Name: .buildid
 --- !COFF
 header:
   Machine:         IMAGE_FILE_MACHINE_I386

diff  --git a/lld/test/MinGW/driver.test b/lld/test/MinGW/driver.test
index a07c95edb580d..d08c64258be89 100644
--- a/lld/test/MinGW/driver.test
+++ b/lld/test/MinGW/driver.test
@@ -389,3 +389,16 @@ 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
+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
+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


        


More information about the llvm-commits mailing list