xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp (revision fcaf7f8644a9988098ac6be2165bce3ea4786e91)
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/DWARFLinker/DWARFLinker.h"
12 #include "llvm/DWARFLinker/DWARFStreamer.h"
13 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
14 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
15 #include "llvm/Object/ObjectFile.h"
16 #include <memory>
17 #include <vector>
18 
19 namespace llvm {
20 namespace dwarfutil {
21 
22 // ObjFileAddressMap allows to check whether specified DIE referencing
23 // dead addresses. It uses tombstone values to determine dead addresses.
24 // The concrete values of tombstone constants were discussed in
25 // https://reviews.llvm.org/D81784 and https://reviews.llvm.org/D84825.
26 // So we use following values as indicators of dead addresses:
27 //
28 // bfd: (LowPC == 0) or (LowPC == 1 and HighPC == 1 and  DWARF v4 (or less))
29 //      or ([LowPC, HighPC] is not inside address ranges of .text sections).
30 //
31 // maxpc: (LowPC == -1) or (LowPC == -2 and  DWARF v4 (or less))
32 //        That value is assumed to be compatible with
33 //        http://www.dwarfstd.org/ShowIssue.php?issue=200609.1
34 //
35 // exec: [LowPC, HighPC] is not inside address ranges of .text sections
36 //
37 // universal: maxpc and bfd
38 class ObjFileAddressMap : public AddressesMap {
39 public:
40   ObjFileAddressMap(DWARFContext &Context, const Options &Options,
41                     object::ObjectFile &ObjFile)
42       : Opts(Options) {
43     // Remember addresses of existing text sections.
44     for (const object::SectionRef &Sect : ObjFile.sections()) {
45       if (!Sect.isText())
46         continue;
47       const uint64_t Size = Sect.getSize();
48       if (Size == 0)
49         continue;
50       const uint64_t StartAddr = Sect.getAddress();
51       TextAddressRanges.insert({StartAddr, StartAddr + Size});
52     }
53 
54     // Check CU address ranges for tombstone value.
55     for (std::unique_ptr<DWARFUnit> &CU : Context.compile_units()) {
56       Expected<llvm::DWARFAddressRangesVector> ARanges =
57           CU->getUnitDIE().getAddressRanges();
58       if (ARanges) {
59         for (auto &Range : *ARanges) {
60           if (!isDeadAddressRange(Range.LowPC, Range.HighPC, CU->getVersion(),
61                                   Options.Tombstone, CU->getAddressByteSize()))
62             DWARFAddressRanges.insert({Range.LowPC, Range.HighPC}, 0);
63         }
64       }
65     }
66   }
67 
68   // should be renamed into has valid address ranges
69   bool hasValidRelocs() override { return !DWARFAddressRanges.empty(); }
70 
71   bool isLiveSubprogram(const DWARFDie &DIE,
72                         CompileUnit::DIEInfo &Info) override {
73     assert((DIE.getTag() == dwarf::DW_TAG_subprogram ||
74             DIE.getTag() == dwarf::DW_TAG_label) &&
75            "Wrong type of input die");
76 
77     if (Optional<uint64_t> LowPC =
78             dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) {
79       if (!isDeadAddress(*LowPC, DIE.getDwarfUnit()->getVersion(),
80                          Opts.Tombstone,
81                          DIE.getDwarfUnit()->getAddressByteSize())) {
82         Info.AddrAdjust = 0;
83         Info.InDebugMap = true;
84         return true;
85       }
86     }
87 
88     return false;
89   }
90 
91   bool isLiveVariable(const DWARFDie &DIE,
92                       CompileUnit::DIEInfo &Info) override {
93     assert((DIE.getTag() == dwarf::DW_TAG_variable ||
94             DIE.getTag() == dwarf::DW_TAG_constant) &&
95            "Wrong type of input die");
96 
97     if (Expected<DWARFLocationExpressionsVector> Loc =
98             DIE.getLocations(dwarf::DW_AT_location)) {
99       DWARFUnit *U = DIE.getDwarfUnit();
100       for (const auto &Entry : *Loc) {
101         DataExtractor Data(toStringRef(Entry.Expr),
102                            U->getContext().isLittleEndian(), 0);
103         DWARFExpression Expression(Data, U->getAddressByteSize(),
104                                    U->getFormParams().Format);
105         bool HasLiveAddresses =
106             any_of(Expression, [&](const DWARFExpression::Operation &Op) {
107               // TODO: add handling of dwarf::DW_OP_addrx
108               return !Op.isError() &&
109                      (Op.getCode() == dwarf::DW_OP_addr &&
110                       !isDeadAddress(Op.getRawOperand(0), U->getVersion(),
111                                      Opts.Tombstone,
112                                      DIE.getDwarfUnit()->getAddressByteSize()));
113             });
114 
115         if (HasLiveAddresses) {
116           Info.AddrAdjust = 0;
117           Info.InDebugMap = true;
118           return true;
119         }
120       }
121     } else {
122       // FIXME: missing DW_AT_location is OK here, but other errors should be
123       // reported to the user.
124       consumeError(Loc.takeError());
125     }
126 
127     return false;
128   }
129 
130   bool applyValidRelocs(MutableArrayRef<char>, uint64_t, bool) override {
131     // no need to apply relocations to the linked binary.
132     return false;
133   }
134 
135   RangesTy &getValidAddressRanges() override { return DWARFAddressRanges; };
136 
137   void clear() override { DWARFAddressRanges.clear(); }
138 
139   llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t, uint64_t) override {
140     // should not be called.
141     return object::createError("no relocations in linked binary");
142   }
143 
144 protected:
145   // returns true if specified address range is inside address ranges
146   // of executable sections.
147   bool isInsideExecutableSectionsAddressRange(uint64_t LowPC,
148                                               Optional<uint64_t> HighPC) {
149     Optional<AddressRange> Range =
150         TextAddressRanges.getRangeThatContains(LowPC);
151 
152     if (HighPC)
153       return Range.has_value() && Range->end() >= *HighPC;
154 
155     return Range.has_value();
156   }
157 
158   uint64_t isBFDDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
159                                  uint16_t Version) {
160     if (LowPC == 0)
161       return true;
162 
163     if ((Version <= 4) && HighPC && (LowPC == 1 && *HighPC == 1))
164       return true;
165 
166     return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
167   }
168 
169   uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
170                                    uint16_t Version, uint8_t AddressByteSize) {
171     if (Version <= 4 && HighPC) {
172       if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1))
173         return true;
174     } else if (LowPC == dwarf::computeTombstoneAddress(AddressByteSize))
175       return true;
176 
177     if (!isInsideExecutableSectionsAddressRange(LowPC, HighPC))
178       warning("Address referencing invalid text section is not marked with "
179               "tombstone value");
180 
181     return false;
182   }
183 
184   bool isDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
185                           uint16_t Version, TombstoneKind Tombstone,
186                           uint8_t AddressByteSize) {
187     switch (Tombstone) {
188     case TombstoneKind::BFD:
189       return isBFDDeadAddressRange(LowPC, HighPC, Version);
190     case TombstoneKind::MaxPC:
191       return isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
192     case TombstoneKind::Universal:
193       return isBFDDeadAddressRange(LowPC, HighPC, Version) ||
194              isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
195     case TombstoneKind::Exec:
196       return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
197     }
198 
199     llvm_unreachable("Unknown tombstone value");
200   }
201 
202   bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone,
203                      uint8_t AddressByteSize) {
204     return isDeadAddressRange(LowPC, None, Version, Tombstone, AddressByteSize);
205   }
206 
207 private:
208   RangesTy DWARFAddressRanges;
209   AddressRanges TextAddressRanges;
210   const Options &Opts;
211 };
212 
213 bool linkDebugInfo(object::ObjectFile &File, const Options &Options,
214                    raw_pwrite_stream &OutStream) {
215 
216   auto ReportWarn = [&](const Twine &Message, StringRef Context,
217                         const DWARFDie *Die) {
218     warning(Message, Context);
219 
220     if (!Options.Verbose || !Die)
221       return;
222 
223     DIDumpOptions DumpOpts;
224     DumpOpts.ChildRecurseDepth = 0;
225     DumpOpts.Verbose = Options.Verbose;
226 
227     WithColor::note() << "    in DIE:\n";
228     Die->dump(errs(), /*Indent=*/6, DumpOpts);
229   };
230   auto ReportErr = [&](const Twine &Message, StringRef Context,
231                        const DWARFDie *) {
232     WithColor::error(errs(), Context) << Message << '\n';
233   };
234 
235   // Create output streamer.
236   DwarfStreamer OutStreamer(OutputFileType::Object, OutStream, nullptr,
237                             ReportWarn, ReportWarn);
238   if (!OutStreamer.init(File.makeTriple(), ""))
239     return false;
240 
241   // Create DWARF linker.
242   DWARFLinker DebugInfoLinker(&OutStreamer, DwarfLinkerClient::LLD);
243 
244   DebugInfoLinker.setEstimatedObjfilesAmount(1);
245   DebugInfoLinker.setAccelTableKind(DwarfLinkerAccelTableKind::None);
246   DebugInfoLinker.setErrorHandler(ReportErr);
247   DebugInfoLinker.setWarningHandler(ReportWarn);
248   DebugInfoLinker.setNumThreads(Options.NumThreads);
249   DebugInfoLinker.setNoODR(!Options.DoODRDeduplication);
250   DebugInfoLinker.setVerbosity(Options.Verbose);
251   DebugInfoLinker.setUpdate(!Options.DoGarbageCollection);
252 
253   std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking(1);
254   std::vector<std::unique_ptr<AddressesMap>> AddresssMapForLinking(1);
255   std::vector<std::string> EmptyWarnings;
256 
257   std::unique_ptr<DWARFContext> Context = DWARFContext::create(File);
258 
259   // Add object files to the DWARFLinker.
260   AddresssMapForLinking[0] =
261       std::make_unique<ObjFileAddressMap>(*Context, Options, File);
262 
263   ObjectsForLinking[0] = std::make_unique<DWARFFile>(
264       File.getFileName(), &*Context, AddresssMapForLinking[0].get(),
265       EmptyWarnings);
266 
267   for (size_t I = 0; I < ObjectsForLinking.size(); I++)
268     DebugInfoLinker.addObjectFile(*ObjectsForLinking[I]);
269 
270   // Link debug info.
271   DebugInfoLinker.link();
272   OutStreamer.finish();
273   return true;
274 }
275 
276 } // end of namespace dwarfutil
277 } // end of namespace llvm
278