Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
35231 views
1
//=== DebugInfoLinker.cpp -------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "DebugInfoLinker.h"
10
#include "Error.h"
11
#include "llvm/ADT/StringSwitch.h"
12
#include "llvm/DWARFLinker/Classic/DWARFLinker.h"
13
#include "llvm/DWARFLinker/Classic/DWARFStreamer.h"
14
#include "llvm/DWARFLinker/Parallel/DWARFLinker.h"
15
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
16
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
17
#include "llvm/Object/ObjectFile.h"
18
#include <memory>
19
#include <vector>
20
21
namespace llvm {
22
using namespace dwarf_linker;
23
24
namespace dwarfutil {
25
26
// ObjFileAddressMap allows to check whether specified DIE referencing
27
// dead addresses. It uses tombstone values to determine dead addresses.
28
// The concrete values of tombstone constants were discussed in
29
// https://reviews.llvm.org/D81784 and https://reviews.llvm.org/D84825.
30
// So we use following values as indicators of dead addresses:
31
//
32
// bfd: (LowPC == 0) or (LowPC == 1 and HighPC == 1 and DWARF v4 (or less))
33
// or ([LowPC, HighPC] is not inside address ranges of .text sections).
34
//
35
// maxpc: (LowPC == -1) or (LowPC == -2 and DWARF v4 (or less))
36
// That value is assumed to be compatible with
37
// http://www.dwarfstd.org/ShowIssue.php?issue=200609.1
38
//
39
// exec: [LowPC, HighPC] is not inside address ranges of .text sections
40
//
41
// universal: maxpc and bfd
42
class ObjFileAddressMap : public AddressesMap {
43
public:
44
ObjFileAddressMap(DWARFContext &Context, const Options &Options,
45
object::ObjectFile &ObjFile)
46
: Opts(Options) {
47
// Remember addresses of existing text sections.
48
for (const object::SectionRef &Sect : ObjFile.sections()) {
49
if (!Sect.isText())
50
continue;
51
const uint64_t Size = Sect.getSize();
52
if (Size == 0)
53
continue;
54
const uint64_t StartAddr = Sect.getAddress();
55
TextAddressRanges.insert({StartAddr, StartAddr + Size});
56
}
57
58
// Check CU address ranges for tombstone value.
59
for (std::unique_ptr<DWARFUnit> &CU : Context.compile_units()) {
60
Expected<llvm::DWARFAddressRangesVector> ARanges =
61
CU->getUnitDIE().getAddressRanges();
62
if (!ARanges) {
63
llvm::consumeError(ARanges.takeError());
64
continue;
65
}
66
67
for (auto &Range : *ARanges) {
68
if (!isDeadAddressRange(Range.LowPC, Range.HighPC, CU->getVersion(),
69
Options.Tombstone, CU->getAddressByteSize())) {
70
HasValidAddressRanges = true;
71
break;
72
}
73
}
74
75
if (HasValidAddressRanges)
76
break;
77
}
78
}
79
80
// should be renamed into has valid address ranges
81
bool hasValidRelocs() override { return HasValidAddressRanges; }
82
83
std::optional<int64_t> getSubprogramRelocAdjustment(const DWARFDie &DIE,
84
bool Verbose) override {
85
assert((DIE.getTag() == dwarf::DW_TAG_subprogram ||
86
DIE.getTag() == dwarf::DW_TAG_label) &&
87
"Wrong type of input die");
88
89
if (std::optional<uint64_t> LowPC =
90
dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) {
91
if (!isDeadAddress(*LowPC, DIE.getDwarfUnit()->getVersion(),
92
Opts.Tombstone,
93
DIE.getDwarfUnit()->getAddressByteSize()))
94
// Relocation value for the linked binary is 0.
95
return 0;
96
}
97
98
return std::nullopt;
99
}
100
101
std::optional<int64_t>
102
getExprOpAddressRelocAdjustment(DWARFUnit &U,
103
const DWARFExpression::Operation &Op,
104
uint64_t, uint64_t, bool Verbose) override {
105
switch (Op.getCode()) {
106
default: {
107
assert(false && "Specified operation does not have address operand");
108
} break;
109
case dwarf::DW_OP_const2u:
110
case dwarf::DW_OP_const4u:
111
case dwarf::DW_OP_const8u:
112
case dwarf::DW_OP_const2s:
113
case dwarf::DW_OP_const4s:
114
case dwarf::DW_OP_const8s:
115
case dwarf::DW_OP_addr: {
116
if (!isDeadAddress(Op.getRawOperand(0), U.getVersion(), Opts.Tombstone,
117
U.getAddressByteSize()))
118
// Relocation value for the linked binary is 0.
119
return 0;
120
} break;
121
case dwarf::DW_OP_constx:
122
case dwarf::DW_OP_addrx: {
123
if (std::optional<object::SectionedAddress> Address =
124
U.getAddrOffsetSectionItem(Op.getRawOperand(0))) {
125
if (!isDeadAddress(Address->Address, U.getVersion(), Opts.Tombstone,
126
U.getAddressByteSize()))
127
// Relocation value for the linked binary is 0.
128
return 0;
129
}
130
} break;
131
}
132
133
return std::nullopt;
134
}
135
136
std::optional<StringRef> getLibraryInstallName() override {
137
return std::nullopt;
138
}
139
140
bool applyValidRelocs(MutableArrayRef<char>, uint64_t, bool) override {
141
// no need to apply relocations to the linked binary.
142
return false;
143
}
144
145
bool needToSaveValidRelocs() override { return false; }
146
147
void updateAndSaveValidRelocs(bool, uint64_t, int64_t, uint64_t,
148
uint64_t) override {}
149
150
void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
151
uint64_t OutputUnitOffset) override {}
152
153
void clear() override {}
154
155
protected:
156
// returns true if specified address range is inside address ranges
157
// of executable sections.
158
bool isInsideExecutableSectionsAddressRange(uint64_t LowPC,
159
std::optional<uint64_t> HighPC) {
160
std::optional<AddressRange> Range =
161
TextAddressRanges.getRangeThatContains(LowPC);
162
163
if (HighPC)
164
return Range.has_value() && Range->end() >= *HighPC;
165
166
return Range.has_value();
167
}
168
169
uint64_t isBFDDeadAddressRange(uint64_t LowPC, std::optional<uint64_t> HighPC,
170
uint16_t Version) {
171
if (LowPC == 0)
172
return true;
173
174
if ((Version <= 4) && HighPC && (LowPC == 1 && *HighPC == 1))
175
return true;
176
177
return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
178
}
179
180
uint64_t isMAXPCDeadAddressRange(uint64_t LowPC,
181
std::optional<uint64_t> HighPC,
182
uint16_t Version, uint8_t AddressByteSize) {
183
if (Version <= 4 && HighPC) {
184
if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1))
185
return true;
186
} else if (LowPC == dwarf::computeTombstoneAddress(AddressByteSize))
187
return true;
188
189
if (!isInsideExecutableSectionsAddressRange(LowPC, HighPC))
190
warning("Address referencing invalid text section is not marked with "
191
"tombstone value");
192
193
return false;
194
}
195
196
bool isDeadAddressRange(uint64_t LowPC, std::optional<uint64_t> HighPC,
197
uint16_t Version, TombstoneKind Tombstone,
198
uint8_t AddressByteSize) {
199
switch (Tombstone) {
200
case TombstoneKind::BFD:
201
return isBFDDeadAddressRange(LowPC, HighPC, Version);
202
case TombstoneKind::MaxPC:
203
return isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
204
case TombstoneKind::Universal:
205
return isBFDDeadAddressRange(LowPC, HighPC, Version) ||
206
isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
207
case TombstoneKind::Exec:
208
return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
209
}
210
211
llvm_unreachable("Unknown tombstone value");
212
}
213
214
bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone,
215
uint8_t AddressByteSize) {
216
return isDeadAddressRange(LowPC, std::nullopt, Version, Tombstone,
217
AddressByteSize);
218
}
219
220
private:
221
AddressRanges TextAddressRanges;
222
const Options &Opts;
223
bool HasValidAddressRanges = false;
224
};
225
226
static bool knownByDWARFUtil(StringRef SecName) {
227
return llvm::StringSwitch<bool>(SecName)
228
.Case(".debug_info", true)
229
.Case(".debug_types", true)
230
.Case(".debug_abbrev", true)
231
.Case(".debug_loc", true)
232
.Case(".debug_loclists", true)
233
.Case(".debug_frame", true)
234
.Case(".debug_aranges", true)
235
.Case(".debug_ranges", true)
236
.Case(".debug_rnglists", true)
237
.Case(".debug_line", true)
238
.Case(".debug_line_str", true)
239
.Case(".debug_addr", true)
240
.Case(".debug_macro", true)
241
.Case(".debug_macinfo", true)
242
.Case(".debug_str", true)
243
.Case(".debug_str_offsets", true)
244
.Case(".debug_pubnames", true)
245
.Case(".debug_pubtypes", true)
246
.Case(".debug_names", true)
247
.Default(false);
248
}
249
250
template <typename AccelTableKind>
251
static std::optional<AccelTableKind>
252
getAcceleratorTableKind(StringRef SecName) {
253
return llvm::StringSwitch<std::optional<AccelTableKind>>(SecName)
254
.Case(".debug_pubnames", AccelTableKind::Pub)
255
.Case(".debug_pubtypes", AccelTableKind::Pub)
256
.Case(".debug_names", AccelTableKind::DebugNames)
257
.Default(std::nullopt);
258
}
259
260
static std::string getMessageForReplacedAcceleratorTables(
261
SmallVector<StringRef> &AccelTableNamesToReplace,
262
DwarfUtilAccelKind TargetTable) {
263
std::string Message;
264
265
Message += "'";
266
for (StringRef Name : AccelTableNamesToReplace) {
267
if (Message.size() > 1)
268
Message += ", ";
269
Message += Name;
270
}
271
272
Message += "' will be replaced with requested ";
273
274
switch (TargetTable) {
275
case DwarfUtilAccelKind::DWARF:
276
Message += ".debug_names table";
277
break;
278
279
default:
280
assert(false);
281
}
282
283
return Message;
284
}
285
286
static std::string getMessageForDeletedAcceleratorTables(
287
SmallVector<StringRef> &AccelTableNamesToReplace) {
288
std::string Message;
289
290
Message += "'";
291
for (StringRef Name : AccelTableNamesToReplace) {
292
if (Message.size() > 1)
293
Message += ", ";
294
Message += Name;
295
}
296
297
Message += "' will be deleted as no accelerator tables are requested";
298
299
return Message;
300
}
301
302
template <typename Linker>
303
Error linkDebugInfoImpl(object::ObjectFile &File, const Options &Options,
304
raw_pwrite_stream &OutStream) {
305
std::mutex ErrorHandlerMutex;
306
307
auto ReportWarn = [&](const Twine &Message, StringRef Context,
308
const DWARFDie *Die) {
309
// FIXME: implement warning logging which does not block other threads.
310
if (!ErrorHandlerMutex.try_lock())
311
return;
312
313
warning(Message, Context);
314
if (Options.Verbose && Die) {
315
DIDumpOptions DumpOpts;
316
DumpOpts.ChildRecurseDepth = 0;
317
DumpOpts.Verbose = Options.Verbose;
318
319
WithColor::note() << " in DIE:\n";
320
Die->dump(errs(), /*Indent=*/6, DumpOpts);
321
}
322
ErrorHandlerMutex.unlock();
323
};
324
auto ReportErr = [&](const Twine &Message, StringRef Context,
325
const DWARFDie *) {
326
// FIXME: implement error logging which does not block other threads.
327
if (!ErrorHandlerMutex.try_lock())
328
return;
329
330
WithColor::error(errs(), Context) << Message << '\n';
331
ErrorHandlerMutex.unlock();
332
};
333
334
// Create DWARF linker.
335
std::unique_ptr<Linker> DebugInfoLinker =
336
Linker::createLinker(ReportErr, ReportWarn);
337
338
Triple TargetTriple = File.makeTriple();
339
std::unique_ptr<classic::DwarfStreamer> Streamer;
340
if (Expected<std::unique_ptr<classic::DwarfStreamer>> StreamerOrErr =
341
classic::DwarfStreamer::createStreamer(TargetTriple,
342
Linker::OutputFileType::Object,
343
OutStream, ReportWarn))
344
Streamer = std::move(*StreamerOrErr);
345
else
346
return StreamerOrErr.takeError();
347
348
if constexpr (std::is_same<Linker,
349
dwarf_linker::parallel::DWARFLinker>::value) {
350
DebugInfoLinker->setOutputDWARFHandler(
351
TargetTriple,
352
[&](std::shared_ptr<dwarf_linker::parallel::SectionDescriptorBase>
353
Section) {
354
Streamer->emitSectionContents(Section->getContents(),
355
Section->getKind());
356
});
357
} else
358
DebugInfoLinker->setOutputDWARFEmitter(Streamer.get());
359
360
DebugInfoLinker->setEstimatedObjfilesAmount(1);
361
DebugInfoLinker->setNumThreads(Options.NumThreads);
362
DebugInfoLinker->setNoODR(!Options.DoODRDeduplication);
363
DebugInfoLinker->setVerbosity(Options.Verbose);
364
DebugInfoLinker->setUpdateIndexTablesOnly(!Options.DoGarbageCollection);
365
366
std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking(1);
367
368
// Add object files to the DWARFLinker.
369
std::unique_ptr<DWARFContext> Context = DWARFContext::create(
370
File, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",
371
[&](Error Err) {
372
handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
373
ReportErr(Info.message(), "", nullptr);
374
});
375
},
376
[&](Error Warning) {
377
handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
378
ReportWarn(Info.message(), "", nullptr);
379
});
380
});
381
std::unique_ptr<ObjFileAddressMap> AddressesMap(
382
std::make_unique<ObjFileAddressMap>(*Context, Options, File));
383
384
ObjectsForLinking[0] = std::make_unique<DWARFFile>(
385
File.getFileName(), std::move(Context), std::move(AddressesMap));
386
387
uint16_t MaxDWARFVersion = 0;
388
std::function<void(const DWARFUnit &Unit)> OnCUDieLoaded =
389
[&MaxDWARFVersion](const DWARFUnit &Unit) {
390
MaxDWARFVersion = std::max(Unit.getVersion(), MaxDWARFVersion);
391
};
392
393
for (size_t I = 0; I < ObjectsForLinking.size(); I++)
394
DebugInfoLinker->addObjectFile(*ObjectsForLinking[I], nullptr,
395
OnCUDieLoaded);
396
397
// If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway.
398
if (MaxDWARFVersion == 0)
399
MaxDWARFVersion = 3;
400
401
if (Error Err = DebugInfoLinker->setTargetDWARFVersion(MaxDWARFVersion))
402
return Err;
403
404
SmallVector<typename Linker::AccelTableKind> AccelTables;
405
406
switch (Options.AccelTableKind) {
407
case DwarfUtilAccelKind::None:
408
// Nothing to do.
409
break;
410
case DwarfUtilAccelKind::DWARF:
411
// use .debug_names for all DWARF versions.
412
AccelTables.push_back(Linker::AccelTableKind::DebugNames);
413
break;
414
}
415
416
// Add accelerator tables to DWARFLinker.
417
for (typename Linker::AccelTableKind Table : AccelTables)
418
DebugInfoLinker->addAccelTableKind(Table);
419
420
for (std::unique_ptr<DWARFFile> &CurFile : ObjectsForLinking) {
421
SmallVector<StringRef> AccelTableNamesToReplace;
422
SmallVector<StringRef> AccelTableNamesToDelete;
423
424
// Unknown debug sections or non-requested accelerator sections would be
425
// removed. Display warning for such sections.
426
for (SectionName Sec : CurFile->Dwarf->getDWARFObj().getSectionNames()) {
427
if (isDebugSection(Sec.Name)) {
428
std::optional<typename Linker::AccelTableKind> SrcAccelTableKind =
429
getAcceleratorTableKind<typename Linker::AccelTableKind>(Sec.Name);
430
431
if (SrcAccelTableKind) {
432
assert(knownByDWARFUtil(Sec.Name));
433
434
if (Options.AccelTableKind == DwarfUtilAccelKind::None)
435
AccelTableNamesToDelete.push_back(Sec.Name);
436
else if (!llvm::is_contained(AccelTables, *SrcAccelTableKind))
437
AccelTableNamesToReplace.push_back(Sec.Name);
438
} else if (!knownByDWARFUtil(Sec.Name)) {
439
assert(!SrcAccelTableKind);
440
warning(
441
formatv(
442
"'{0}' is not currently supported: section will be skipped",
443
Sec.Name),
444
Options.InputFileName);
445
}
446
}
447
}
448
449
// Display message for the replaced accelerator tables.
450
if (!AccelTableNamesToReplace.empty())
451
warning(getMessageForReplacedAcceleratorTables(AccelTableNamesToReplace,
452
Options.AccelTableKind),
453
Options.InputFileName);
454
455
// Display message for the removed accelerator tables.
456
if (!AccelTableNamesToDelete.empty())
457
warning(getMessageForDeletedAcceleratorTables(AccelTableNamesToDelete),
458
Options.InputFileName);
459
}
460
461
// Link debug info.
462
if (Error Err = DebugInfoLinker->link())
463
return Err;
464
465
Streamer->finish();
466
return Error::success();
467
}
468
469
Error linkDebugInfo(object::ObjectFile &File, const Options &Options,
470
raw_pwrite_stream &OutStream) {
471
if (Options.UseDWARFLinkerParallel)
472
return linkDebugInfoImpl<parallel::DWARFLinker>(File, Options, OutStream);
473
else
474
return linkDebugInfoImpl<classic::DWARFLinker>(File, Options, OutStream);
475
}
476
477
} // end of namespace dwarfutil
478
} // end of namespace llvm
479
480