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