1//===- tools/dsymutil/DwarfLinkerForBinary.h --------------------*- C++ -*-===//
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#ifndef LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
10#define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
11
12#include "BinaryHolder.h"
13#include "DebugMap.h"
14#include "LinkUtils.h"
15#include "MachOUtils.h"
16#include "RelocationMap.h"
17#include "llvm/DebugInfo/DWARF/DWARFContext.h"
18#include "llvm/Remarks/RemarkFormat.h"
19#include "llvm/Remarks/RemarkLinker.h"
20#include <mutex>
21#include <optional>
22
23namespace llvm {
24class ThreadPoolInterface;
25using namespace dwarf_linker;
26
27namespace dsymutil {
28
29/// DwarfLinkerForBinaryRelocationMap contains the logic to handle the
30/// relocations and to store them inside an associated RelocationMap.
31class DwarfLinkerForBinaryRelocationMap {
32public:
33 void init(DWARFContext &Context);
34
35 bool isInitialized() {
36 return StoredValidDebugInfoRelocsMap.getMemorySize() != 0;
37 }
38
39 void addValidRelocs(RelocationMap &RM);
40
41 void updateAndSaveValidRelocs(bool IsDWARF5,
42 std::vector<ValidReloc> &InRelocs,
43 uint64_t UnitOffset, int64_t LinkedOffset);
44
45 void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
46 uint64_t OutputUnitOffset);
47
48 /// Map compilation unit offset to the valid relocations to store
49 /// @{
50 DenseMap<uint64_t, std::vector<ValidReloc>> StoredValidDebugInfoRelocsMap;
51 DenseMap<uint64_t, std::vector<ValidReloc>> StoredValidDebugAddrRelocsMap;
52 /// @}
53
54 DwarfLinkerForBinaryRelocationMap() = default;
55};
56
57struct ObjectWithRelocMap {
58 ObjectWithRelocMap(
59 std::unique_ptr<DWARFFile> Object,
60 std::shared_ptr<DwarfLinkerForBinaryRelocationMap> OutRelocs)
61 : Object(std::move(Object)), OutRelocs(OutRelocs) {}
62 std::unique_ptr<DWARFFile> Object;
63 std::shared_ptr<DwarfLinkerForBinaryRelocationMap> OutRelocs;
64};
65
66/// The core of the Dsymutil Dwarf linking logic.
67///
68/// The link of the dwarf information from the object files will be
69/// driven by DWARFLinker. DwarfLinkerForBinary reads DebugMap objects
70/// and pass information to the DWARFLinker. DWARFLinker
71/// optimizes DWARF taking into account valid relocations.
72/// Finally, optimized DWARF is passed to DwarfLinkerForBinary through
73/// DWARFEmitter interface.
74class DwarfLinkerForBinary {
75public:
76 DwarfLinkerForBinary(raw_fd_ostream &OutFile, BinaryHolder &BinHolder,
77 LinkOptions Options, std::mutex &ErrorHandlerMutex,
78 ThreadPoolInterface *ThreadPool = nullptr)
79 : OutFile(OutFile), BinHolder(BinHolder), Options(std::move(Options)),
80 ErrorHandlerMutex(ErrorHandlerMutex), ThreadPool(ThreadPool) {}
81
82 /// Link the contents of the DebugMap.
83 bool link(const DebugMap &);
84
85 void reportWarning(Twine Warning, Twine Context = {},
86 const DWARFDie *DIE = nullptr) const;
87 void reportError(Twine Error, Twine Context = {},
88 const DWARFDie *DIE = nullptr) const;
89
90 /// Returns true if input verification is enabled and verification errors were
91 /// found.
92 bool InputVerificationFailed() const { return HasVerificationErrors; }
93
94 /// Flags passed to DwarfLinker::lookForDIEsToKeep
95 enum TraversalFlags {
96 TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept.
97 TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope.
98 TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE.
99 TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE.
100 TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents.
101 TF_SkipPC = 1 << 5, ///< Skip all location attributes.
102 };
103
104private:
105
106 /// Keeps track of relocations.
107 class AddressManager : public dwarf_linker::AddressesMap {
108
109 const DwarfLinkerForBinary &Linker;
110
111 /// The valid relocations for the current DebugMapObject.
112 /// These vectors are sorted by relocation offset.
113 /// {
114 std::vector<ValidReloc> ValidDebugInfoRelocs;
115 std::vector<ValidReloc> ValidDebugAddrRelocs;
116 /// }
117
118 StringRef SrcFileName;
119
120 uint8_t DebugMapObjectType;
121
122 std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DwarfLinkerRelocMap;
123
124 std::optional<std::string> LibInstallName;
125
126 /// Address ranges for symbols with sizes (used for assembly file support).
127 RangesTy AddressRanges;
128
129 /// Returns list of valid relocations from \p Relocs,
130 /// between \p StartOffset and \p NextOffset.
131 ///
132 /// \returns true if any relocation is found.
133 std::vector<ValidReloc>
134 getRelocations(const std::vector<ValidReloc> &Relocs, uint64_t StartPos,
135 uint64_t EndPos);
136
137 /// Resolve specified relocation \p Reloc.
138 ///
139 /// \returns resolved value.
140 uint64_t relocate(const ValidReloc &Reloc) const;
141
142 /// \returns value for the specified \p Reloc.
143 int64_t getRelocValue(const ValidReloc &Reloc);
144
145 /// Print contents of debug map entry for the specified \p Reloc.
146 void printReloc(const ValidReloc &Reloc);
147
148 public:
149 AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj,
150 const DebugMapObject &DMO,
151 std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM)
152 : Linker(Linker), SrcFileName(DMO.getObjectFilename()),
153 DebugMapObjectType(MachO::N_OSO), DwarfLinkerRelocMap(DLBRM) {
154 if (DMO.getRelocationMap().has_value()) {
155 DebugMapObjectType = MachO::N_LIB;
156 LibInstallName.emplace(args: DMO.getInstallName().value());
157 const RelocationMap &RM = DMO.getRelocationMap().value();
158 for (const auto &Reloc : RM.relocations()) {
159 const auto *DebugMapEntry = DMO.lookupSymbol(SymbolName: Reloc.SymbolName);
160 if (!DebugMapEntry)
161 continue;
162 std::optional<uint64_t> ObjAddress;
163 ObjAddress.emplace(args: DebugMapEntry->getValue().ObjectAddress.value());
164 ValidDebugInfoRelocs.emplace_back(
165 args: Reloc.Offset, args: Reloc.Size, args: Reloc.Addend, args: Reloc.SymbolName,
166 args: SymbolMapping(ObjAddress, DebugMapEntry->getValue().BinaryAddress,
167 DebugMapEntry->getValue().Size));
168 // FIXME: Support relocations debug_addr.
169 }
170 } else {
171 findValidRelocsInDebugSections(Obj, DMO);
172 }
173 // Populate address ranges from debug map symbols that have sizes.
174 // This is used for assembly files where labels may not have high_pc.
175 for (const auto &Entry : DMO.symbols()) {
176 const auto &Mapping = Entry.getValue();
177 if (Mapping.Size && Mapping.ObjectAddress)
178 AddressRanges.insert(
179 Range: {*Mapping.ObjectAddress, *Mapping.ObjectAddress + Mapping.Size},
180 Value: int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress);
181 }
182 }
183 ~AddressManager() override { clear(); }
184
185 bool hasValidRelocs() override {
186 return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty();
187 }
188
189 /// \defgroup FindValidRelocations Translate debug map into a list
190 /// of relevant relocations
191 ///
192 /// @{
193 bool findValidRelocsInDebugSections(const object::ObjectFile &Obj,
194 const DebugMapObject &DMO);
195
196 bool findValidRelocs(const object::SectionRef &Section,
197 const object::ObjectFile &Obj,
198 const DebugMapObject &DMO,
199 std::vector<ValidReloc> &ValidRelocs);
200
201 void findValidRelocsMachO(const object::SectionRef &Section,
202 const object::MachOObjectFile &Obj,
203 const DebugMapObject &DMO,
204 std::vector<ValidReloc> &ValidRelocs);
205 /// @}
206
207 /// Checks that there is a relocation in the \p Relocs array against a
208 /// debug map entry between \p StartOffset and \p NextOffset.
209 /// Print debug output if \p Verbose is set.
210 ///
211 /// \returns relocation value if relocation exist, otherwise std::nullopt.
212 std::optional<int64_t>
213 hasValidRelocationAt(const std::vector<ValidReloc> &Relocs,
214 uint64_t StartOffset, uint64_t EndOffset,
215 bool Verbose);
216
217 std::optional<int64_t> getExprOpAddressRelocAdjustment(
218 DWARFUnit &U, const DWARFExpression::Operation &Op,
219 uint64_t StartOffset, uint64_t EndOffset, bool Verbose) override;
220
221 std::optional<int64_t> getSubprogramRelocAdjustment(const DWARFDie &DIE,
222 bool Verbose) override;
223
224 std::optional<StringRef> getLibraryInstallName() override;
225
226 bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset,
227 bool IsLittleEndian) override;
228
229 bool needToSaveValidRelocs() override { return true; }
230
231 void updateAndSaveValidRelocs(bool IsDWARF5, uint64_t OriginalUnitOffset,
232 int64_t LinkedOffset, uint64_t StartOffset,
233 uint64_t EndOffset) override;
234
235 void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset,
236 uint64_t OutputUnitOffset) override;
237
238 void clear() override {
239 ValidDebugInfoRelocs.clear();
240 ValidDebugAddrRelocs.clear();
241 AddressRanges.clear();
242 }
243
244 std::optional<AssemblyRange>
245 getAssemblyRangeForAddress(uint64_t Addr) override {
246 if (auto Range = AddressRanges.getRangeThatContains(Addr))
247 return AssemblyRange(Range->Range.start(), Range->Range.end());
248 return std::nullopt;
249 }
250 };
251
252private:
253 /// \defgroup Helpers Various helper methods.
254 ///
255 /// @{
256 template <typename OutStreamer>
257 bool createStreamer(const Triple &TheTriple,
258 typename OutStreamer::OutputFileType FileType,
259 std::unique_ptr<OutStreamer> &Streamer,
260 raw_fd_ostream &OutFile);
261
262 /// Attempt to load a debug object from disk.
263 ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj,
264 const Triple &triple);
265 ErrorOr<std::unique_ptr<dwarf_linker::DWARFFile>>
266 loadObject(const DebugMapObject &Obj, const DebugMap &DebugMap,
267 remarks::RemarkLinker &RL,
268 std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM);
269
270 void collectRelocationsToApplyToSwiftReflectionSections(
271 const object::SectionRef &Section, StringRef &Contents,
272 const llvm::object::MachOObjectFile *MO,
273 const std::vector<uint64_t> &SectionToOffsetInDwarf,
274 const llvm::dsymutil::DebugMapObject *Obj,
275 std::vector<MachOUtils::DwarfRelocationApplicationInfo>
276 &RelocationsToApply) const;
277
278 Error copySwiftInterfaces(StringRef Architecture) const;
279
280 Error copyEmbeddedResources() const;
281
282 void copySwiftReflectionMetadata(
283 const llvm::dsymutil::DebugMapObject *Obj,
284 classic::DwarfStreamer *Streamer,
285 std::vector<uint64_t> &SectionToOffsetInDwarf,
286 std::vector<MachOUtils::DwarfRelocationApplicationInfo>
287 &RelocationsToApply);
288
289 template <typename Linker>
290 bool linkImpl(const DebugMap &Map,
291 typename Linker::OutputFileType ObjectType);
292
293 Error emitRelocations(const DebugMap &DM,
294 std::vector<ObjectWithRelocMap> &ObjectsForLinking);
295
296 raw_fd_ostream &OutFile;
297 BinaryHolder &BinHolder;
298 LinkOptions Options;
299 std::mutex &ErrorHandlerMutex;
300 ThreadPoolInterface *ThreadPool;
301
302 std::vector<std::string> EmptyWarnings;
303
304 /// A list of all .swiftinterface files referenced by the debug
305 /// info, mapping Module name to path on disk. The entries need to
306 /// be uniqued and sorted and there are only few entries expected
307 /// per compile unit, which is why this is a std::map.
308 std::map<std::string, std::string> ParseableSwiftInterfaces;
309
310 bool ModuleCacheHintDisplayed = false;
311 bool ArchiveHintDisplayed = false;
312 bool HasVerificationErrors = false;
313};
314
315} // end namespace dsymutil
316} // end namespace llvm
317
318#endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H
319