Skip to content

Commit aaf3a8d

Browse files
authored
[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.
1 parent af03e29 commit aaf3a8d

File tree

9 files changed

+105
-32
lines changed

9 files changed

+105
-32
lines changed

lld/COFF/Config.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ enum class ICFLevel {
102102
// behavior.
103103
};
104104

105+
enum class BuildIDHash {
106+
None,
107+
PDB,
108+
Binary,
109+
};
110+
105111
// Global configuration.
106112
struct Configuration {
107113
enum ManifestKind { Default, SideBySide, Embed, No };
@@ -318,6 +324,7 @@ struct Configuration {
318324
bool writeCheckSum = false;
319325
EmitKind emit = EmitKind::Obj;
320326
bool allowDuplicateWeak = false;
327+
BuildIDHash buildIDHash = BuildIDHash::None;
321328
};
322329

323330
} // namespace lld::coff

lld/COFF/Driver.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2314,6 +2314,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23142314
config->lldmapFile.clear();
23152315
}
23162316

2317+
// If should create PDB, use the hash of PDB content for build id. Otherwise,
2318+
// generate using the hash of executable content.
2319+
if (args.hasFlag(OPT_build_id, OPT_build_id_no, false))
2320+
config->buildIDHash = BuildIDHash::Binary;
2321+
23172322
if (shouldCreatePDB) {
23182323
// Put the PDB next to the image if no /pdb flag was passed.
23192324
if (config->pdbPath.empty()) {
@@ -2335,6 +2340,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23352340
// Don't do this earlier, so that ctx.OutputFile is ready.
23362341
parsePDBAltPath();
23372342
}
2343+
config->buildIDHash = BuildIDHash::PDB;
23382344
}
23392345

23402346
// Set default image base if /base is not given.

lld/COFF/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ def : Flag<["--"], "time-trace">, Alias<time_trace_eq>,
302302
def time_trace_granularity_eq: Joined<["--"], "time-trace-granularity=">,
303303
HelpText<"Minimum time granularity (in microseconds) traced by time profiler">;
304304

305+
defm build_id: B<
306+
"build-id",
307+
"Generate build ID (always on when generating PDB)",
308+
"Do not Generate build ID">;
309+
305310
// Flags for debugging
306311
def lldmap : F<"lldmap">;
307312
def lldmap_file : P_priv<"lldmap">;

lld/COFF/Writer.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ class Writer {
315315
OutputSection *relocSec;
316316
OutputSection *ctorsSec;
317317
OutputSection *dtorsSec;
318+
// Either .rdata section or .buildid section.
319+
OutputSection *debugInfoSec;
318320

319321
// The range of .pdata sections in the output file.
320322
//
@@ -1103,15 +1105,16 @@ void Writer::createMiscChunks() {
11031105
}
11041106

11051107
// Create Debug Information Chunks
1106-
OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
1107-
if (config->debug || config->repro || config->cetCompat) {
1108+
debugInfoSec = config->mingw ? buildidSec : rdataSec;
1109+
if (config->buildIDHash != BuildIDHash::None || config->debug ||
1110+
config->repro || config->cetCompat) {
11081111
debugDirectory =
11091112
make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
11101113
debugDirectory->setAlignment(4);
11111114
debugInfoSec->addChunk(debugDirectory);
11121115
}
11131116

1114-
if (config->debug) {
1117+
if (config->debug || config->buildIDHash != BuildIDHash::None) {
11151118
// Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We
11161119
// output a PDB no matter what, and this chunk provides the only means of
11171120
// allowing a debugger to match a PDB and an executable. So we need it even
@@ -2170,8 +2173,8 @@ void Writer::writeBuildId() {
21702173
// For reproducibility, instead of a timestamp we want to use a hash of the
21712174
// PE contents.
21722175
Configuration *config = &ctx.config;
2173-
2174-
if (config->debug) {
2176+
bool generateSyntheticBuildId = config->buildIDHash == BuildIDHash::Binary;
2177+
if (generateSyntheticBuildId) {
21752178
assert(buildId && "BuildId is not set!");
21762179
// BuildId->BuildId was filled in when the PDB was written.
21772180
}
@@ -2186,8 +2189,6 @@ void Writer::writeBuildId() {
21862189

21872190
uint32_t timestamp = config->timestamp;
21882191
uint64_t hash = 0;
2189-
bool generateSyntheticBuildId =
2190-
config->mingw && config->debug && config->pdbPath.empty();
21912192

21922193
if (config->repro || generateSyntheticBuildId)
21932194
hash = xxh3_64bits(outputFileData);
@@ -2196,8 +2197,6 @@ void Writer::writeBuildId() {
21962197
timestamp = static_cast<uint32_t>(hash);
21972198

21982199
if (generateSyntheticBuildId) {
2199-
// For MinGW builds without a PDB file, we still generate a build id
2200-
// to allow associating a crash dump to the executable.
22012200
buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70;
22022201
buildId->buildId->PDB70.Age = 1;
22032202
memcpy(buildId->buildId->PDB70.Signature, &hash, 8);

lld/MinGW/Driver.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,21 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
302302
} else if (!args.hasArg(OPT_strip_all)) {
303303
add("-debug:dwarf");
304304
}
305+
if (auto *a = args.getLastArg(OPT_build_id)) {
306+
StringRef v = a->getValue();
307+
if (v == "none")
308+
add("-build-id:no");
309+
else {
310+
if (!v.empty())
311+
warn("unsupported build id hashing: " + v + ", using default hashing.");
312+
add("-build-id");
313+
}
314+
} else {
315+
if (args.hasArg(OPT_strip_debug) || args.hasArg(OPT_strip_all))
316+
add("-build-id:no");
317+
else
318+
add("-build-id");
319+
}
305320

306321
if (args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false))
307322
add("-WX");

lld/MinGW/Options.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ defm guard_longjmp : B<"guard-longjmp",
196196
"Do not enable Control Flow Guard long jump hardening">;
197197
defm error_limit:
198198
EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
199+
def build_id: J<"build-id=">, HelpText<"Generate build ID note (pass none to disable)">,
200+
MetaVarName<"<arg>">;
201+
def : F<"build-id">, Alias<build_id>, HelpText<"Alias for --build-id=">;
199202

200203
// Alias
201204
def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
@@ -213,7 +216,6 @@ def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
213216
// Ignored options
214217
def: Joined<["-"], "O">;
215218
def: F<"as-needed">;
216-
def: F<"build-id">;
217219
def: F<"disable-auto-image-base">;
218220
def: F<"enable-auto-image-base">;
219221
def: F<"end-group">;

lld/test/COFF/debug-reloc.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# RUN: llvm-mc -triple=x86_64-windows-gnu %s -filetype=obj -o %t.obj
44

5-
# RUN: lld-link -lldmingw -debug:dwarf -out:%t.exe -entry:mainfunc -subsystem:console %t.obj
5+
# RUN: lld-link -lldmingw -debug:dwarf -build-id -out:%t.exe -entry:mainfunc -subsystem:console %t.obj
66
# RUN: llvm-readobj --sections %t.exe | FileCheck %s -check-prefix SECTIONS
77
# RUN: llvm-readobj --coff-basereloc %t.exe | FileCheck %s -check-prefix RELOCS
88
# RUN: llvm-readobj --file-headers %t.exe | FileCheck %s -check-prefix HEADERS

lld/test/COFF/rsds.test

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,30 @@
2222
# RUN: lld-link /Brepro /debug /dll /out:%t.dll /entry:DllMain %t.obj
2323
# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix REPRODEBUG %s
2424

25+
# Generate .buildid section using binary hash under /lldmingw and /build-id
2526
# RUN: rm -f %t.dll %t.pdb
26-
# RUN: lld-link /lldmingw /debug:dwarf /dll /out:%t.dll /entry:DllMain %t.obj
27-
# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix MINGW %s
27+
# RUN: lld-link /lldmingw /build-id /dll /out:%t.dll /entry:DllMain %t.obj
28+
# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
29+
30+
# Generate debug directory with use binary hash when /build-id is given and not
31+
# generating PDB.
32+
# RUN: rm -f %t.dll %t.pdb
33+
# RUN: lld-link /build-id /dll /out:%t.dll /entry:DllMain %t.obj
34+
# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
35+
36+
# If generate PDB, PDB hash is used and /build-id is ignored.
37+
# RUN: rm -f %t.dll %t.pdb
38+
# RUN: lld-link /build-id /debug /pdbaltpath:test.pdb /dll /out:%t.dll /entry:DllMain %t.obj
39+
# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix BUILDID %s
40+
41+
# Do not generate .buildid section under /build-id:no
42+
# RUN: rm -f %t.dll %t.pdb
43+
# RUN: lld-link /build-id:no /dll /out:%t.dll /entry:DllMain %t.obj
44+
# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix NO_BUILDID %s
45+
46+
# RUN: rm -f %t.dll %t.pdb
47+
# RUN: lld-link /dll /out:%t.dll /entry:DllMain %t.obj
48+
# RUN: llvm-readobj --coff-debug-directory %t.dll | FileCheck --check-prefix NO_BUILDID %s
2849

2950
# CHECK: File: [[FILE:.*]].dll
3051
# CHECK: DebugDirectory [
@@ -148,25 +169,30 @@
148169
# REPRODEBUG: }
149170
# REPRODEBUG: ]
150171

151-
# MINGW: File: {{.*}}.dll
152-
# MINGW: DebugDirectory [
153-
# MINGW: DebugEntry {
154-
# MINGW: Characteristics: 0x0
155-
# MINGW: TimeDateStamp:
156-
# MINGW: MajorVersion: 0x0
157-
# MINGW: MinorVersion: 0x0
158-
# MINGW: Type: CodeView (0x2)
159-
# MINGW: SizeOfData: 0x{{[^0]}}
160-
# MINGW: AddressOfRawData: 0x{{[^0]}}
161-
# MINGW: PointerToRawData: 0x{{[^0]}}
162-
# MINGW: PDBInfo {
163-
# MINGW: PDBSignature: 0x53445352
164-
# MINGW: PDBGUID: [[GUID:\(([A-Za-z0-9]{2} ?){16}\)]]
165-
# MINGW: PDBAge: 1
166-
# MINGW: PDBFileName:
167-
# MINGW: }
168-
# MINGW: }
169-
# MINGW: ]
172+
# BUILDID: File: {{.*}}.dll
173+
# BUILDID: DebugDirectory [
174+
# BUILDID: DebugEntry {
175+
# BUILDID: Characteristics: 0x0
176+
# BUILDID: TimeDateStamp:
177+
# BUILDID: MajorVersion: 0x0
178+
# BUILDID: MinorVersion: 0x0
179+
# BUILDID: Type: CodeView (0x2)
180+
# BUILDID: SizeOfData: 0x{{[^0]}}
181+
# BUILDID: AddressOfRawData: 0x{{[^0]}}
182+
# BUILDID: PointerToRawData: 0x{{[^0]}}
183+
# BUILDID: PDBInfo {
184+
# BUILDID: PDBSignature: 0x53445352
185+
# BUILDID: PDBGUID: [[GUID:\(([A-Za-z0-9]{2} ?){16}\)]]
186+
# BUILDID: PDBAge: 1
187+
# BUILDID: PDBFileName:
188+
# BUILDID: }
189+
# BUILDID: }
190+
# BUILDID: ]
191+
192+
# NO_BUILDID: DebugDirectory [
193+
# NO_BUILDID-NEXT: ]
194+
195+
# BUILDID_SEC: Name: .buildid
170196
--- !COFF
171197
header:
172198
Machine: IMAGE_FILE_MACHINE_I386

lld/test/MinGW/driver.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,16 @@ Test GCC specific LTO options that GCC passes unconditionally, that we ignore.
389389

390390
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
391391
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
392+
393+
RUN: ld.lld -### foo.o -m i386pep 2>&1 | FileCheck -check-prefix=BUILD_ID %s
394+
RUN: ld.lld -### foo.o -m i386pep --build-id 2>&1 | FileCheck -check-prefix=BUILD_ID %s
395+
BUILD_ID: -build-id{{ }}
396+
397+
RUN: ld.lld -### foo.o -m i386pep --build-id=fast 2>&1 | FileCheck -check-prefix=BUILD_ID_WARN %s
398+
BUILD_ID_WARN: unsupported build id hashing: fast, using default hashing.
399+
BUILD_ID_WARN: -build-id{{ }}
400+
401+
RUN: ld.lld -### foo.o -m i386pep --build-id=none 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
402+
RUN: ld.lld -### foo.o -m i386pep -s 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
403+
RUN: ld.lld -### foo.o -m i386pep -S 2>&1 | FileCheck -check-prefix=NO_BUILD_ID %s
404+
NO_BUILD_ID: -build-id:no

0 commit comments

Comments
 (0)