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