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(Range: {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(Err: ARanges.takeError()); |
64 | continue; |
65 | } |
66 | |
67 | for (auto &Range : *ARanges) { |
68 | if (!isDeadAddressRange(LowPC: Range.LowPC, HighPC: Range.HighPC, Version: CU->getVersion(), |
69 | Tombstone: Options.Tombstone, AddressByteSize: 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(V: DIE.find(Attr: dwarf::DW_AT_low_pc))) { |
91 | if (!isDeadAddress(LowPC: *LowPC, Version: DIE.getDwarfUnit()->getVersion(), |
92 | Tombstone: Opts.Tombstone, |
93 | AddressByteSize: 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(LowPC: Op.getRawOperand(Idx: 0), Version: U.getVersion(), Tombstone: Opts.Tombstone, |
117 | AddressByteSize: 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(Index: Op.getRawOperand(Idx: 0))) { |
125 | if (!isDeadAddress(LowPC: Address->Address, Version: U.getVersion(), Tombstone: Opts.Tombstone, |
126 | AddressByteSize: 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(Addr: 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(Message: "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, HighPC: 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(S: ".debug_info" , Value: true) |
229 | .Case(S: ".debug_types" , Value: true) |
230 | .Case(S: ".debug_abbrev" , Value: true) |
231 | .Case(S: ".debug_loc" , Value: true) |
232 | .Case(S: ".debug_loclists" , Value: true) |
233 | .Case(S: ".debug_frame" , Value: true) |
234 | .Case(S: ".debug_aranges" , Value: true) |
235 | .Case(S: ".debug_ranges" , Value: true) |
236 | .Case(S: ".debug_rnglists" , Value: true) |
237 | .Case(S: ".debug_line" , Value: true) |
238 | .Case(S: ".debug_line_str" , Value: true) |
239 | .Case(S: ".debug_addr" , Value: true) |
240 | .Case(S: ".debug_macro" , Value: true) |
241 | .Case(S: ".debug_macinfo" , Value: true) |
242 | .Case(S: ".debug_str" , Value: true) |
243 | .Case(S: ".debug_str_offsets" , Value: true) |
244 | .Case(S: ".debug_pubnames" , Value: true) |
245 | .Case(S: ".debug_pubtypes" , Value: true) |
246 | .Case(S: ".debug_names" , Value: true) |
247 | .Default(Value: 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, Prefix: 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(OS&: errs(), /*Indent=*/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(OS&: errs(), Prefix: 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(TheTriple: TargetTriple, |
342 | FileType: Linker::OutputFileType::Object, |
343 | OutFile&: OutStream, Warning: 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(SecData: Section->getContents(), |
355 | SecKind: 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>(args&: *Context, args: Options, args&: File)); |
383 | |
384 | ObjectsForLinking[0] = std::make_unique<DWARFFile>( |
385 | args: File.getFileName(), args: std::move(Context), args: 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(a: Unit.getVersion(), b: 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(SecName: 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(Elt: Sec.Name); |
436 | else if (!llvm::is_contained(AccelTables, *SrcAccelTableKind)) |
437 | AccelTableNamesToReplace.push_back(Elt: Sec.Name); |
438 | } else if (!knownByDWARFUtil(SecName: Sec.Name)) { |
439 | assert(!SrcAccelTableKind); |
440 | warning( |
441 | Message: formatv( |
442 | Fmt: "'{0}' is not currently supported: section will be skipped" , |
443 | Vals&: Sec.Name), |
444 | Prefix: Options.InputFileName); |
445 | } |
446 | } |
447 | } |
448 | |
449 | // Display message for the replaced accelerator tables. |
450 | if (!AccelTableNamesToReplace.empty()) |
451 | warning(Message: getMessageForReplacedAcceleratorTables(AccelTableNamesToReplace, |
452 | TargetTable: Options.AccelTableKind), |
453 | Prefix: Options.InputFileName); |
454 | |
455 | // Display message for the removed accelerator tables. |
456 | if (!AccelTableNamesToDelete.empty()) |
457 | warning(Message: getMessageForDeletedAcceleratorTables(AccelTableNamesToReplace&: AccelTableNamesToDelete), |
458 | Prefix: 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 | |