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