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 | |
23 | namespace llvm { |
24 | using namespace dwarf_linker; |
25 | |
26 | namespace dsymutil { |
27 | |
28 | /// DwarfLinkerForBinaryRelocationMap contains the logic to handle the |
29 | /// relocations and to store them inside an associated RelocationMap. |
30 | class DwarfLinkerForBinaryRelocationMap { |
31 | public: |
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 | |
56 | struct 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. |
73 | class DwarfLinkerForBinary { |
74 | public: |
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 | |
102 | private: |
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 | /// Returns list of valid relocations from \p Relocs, |
125 | /// between \p StartOffset and \p NextOffset. |
126 | /// |
127 | /// \returns true if any relocation is found. |
128 | std::vector<ValidReloc> |
129 | getRelocations(const std::vector<ValidReloc> &Relocs, uint64_t StartPos, |
130 | uint64_t EndPos); |
131 | |
132 | /// Resolve specified relocation \p Reloc. |
133 | /// |
134 | /// \returns resolved value. |
135 | uint64_t relocate(const ValidReloc &Reloc) const; |
136 | |
137 | /// \returns value for the specified \p Reloc. |
138 | int64_t getRelocValue(const ValidReloc &Reloc); |
139 | |
140 | /// Print contents of debug map entry for the specified \p Reloc. |
141 | void printReloc(const ValidReloc &Reloc); |
142 | |
143 | public: |
144 | AddressManager(DwarfLinkerForBinary &Linker, const object::ObjectFile &Obj, |
145 | const DebugMapObject &DMO, |
146 | std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM) |
147 | : Linker(Linker), SrcFileName(DMO.getObjectFilename()), |
148 | DebugMapObjectType(MachO::N_OSO), DwarfLinkerRelocMap(DLBRM) { |
149 | if (DMO.getRelocationMap().has_value()) { |
150 | DebugMapObjectType = MachO::N_LIB; |
151 | LibInstallName.emplace(args: DMO.getInstallName().value()); |
152 | const RelocationMap &RM = DMO.getRelocationMap().value(); |
153 | for (const auto &Reloc : RM.relocations()) { |
154 | const auto *DebugMapEntry = DMO.lookupSymbol(SymbolName: Reloc.SymbolName); |
155 | if (!DebugMapEntry) |
156 | continue; |
157 | std::optional<uint64_t> ObjAddress; |
158 | ObjAddress.emplace(args: DebugMapEntry->getValue().ObjectAddress.value()); |
159 | ValidDebugInfoRelocs.emplace_back( |
160 | args: Reloc.Offset, args: Reloc.Size, args: Reloc.Addend, args: Reloc.SymbolName, |
161 | args: SymbolMapping(ObjAddress, DebugMapEntry->getValue().BinaryAddress, |
162 | DebugMapEntry->getValue().Size)); |
163 | // FIXME: Support relocations debug_addr. |
164 | } |
165 | } else { |
166 | findValidRelocsInDebugSections(Obj, DMO); |
167 | } |
168 | } |
169 | ~AddressManager() override { clear(); } |
170 | |
171 | bool hasValidRelocs() override { |
172 | return !ValidDebugInfoRelocs.empty() || !ValidDebugAddrRelocs.empty(); |
173 | } |
174 | |
175 | /// \defgroup FindValidRelocations Translate debug map into a list |
176 | /// of relevant relocations |
177 | /// |
178 | /// @{ |
179 | bool findValidRelocsInDebugSections(const object::ObjectFile &Obj, |
180 | const DebugMapObject &DMO); |
181 | |
182 | bool findValidRelocs(const object::SectionRef &Section, |
183 | const object::ObjectFile &Obj, |
184 | const DebugMapObject &DMO, |
185 | std::vector<ValidReloc> &ValidRelocs); |
186 | |
187 | void findValidRelocsMachO(const object::SectionRef &Section, |
188 | const object::MachOObjectFile &Obj, |
189 | const DebugMapObject &DMO, |
190 | std::vector<ValidReloc> &ValidRelocs); |
191 | /// @} |
192 | |
193 | /// Checks that there is a relocation in the \p Relocs array against a |
194 | /// debug map entry between \p StartOffset and \p NextOffset. |
195 | /// Print debug output if \p Verbose is set. |
196 | /// |
197 | /// \returns relocation value if relocation exist, otherwise std::nullopt. |
198 | std::optional<int64_t> |
199 | hasValidRelocationAt(const std::vector<ValidReloc> &Relocs, |
200 | uint64_t StartOffset, uint64_t EndOffset, |
201 | bool Verbose); |
202 | |
203 | std::optional<int64_t> getExprOpAddressRelocAdjustment( |
204 | DWARFUnit &U, const DWARFExpression::Operation &Op, |
205 | uint64_t StartOffset, uint64_t EndOffset, bool Verbose) override; |
206 | |
207 | std::optional<int64_t> getSubprogramRelocAdjustment(const DWARFDie &DIE, |
208 | bool Verbose) override; |
209 | |
210 | std::optional<StringRef> getLibraryInstallName() override; |
211 | |
212 | bool applyValidRelocs(MutableArrayRef<char> Data, uint64_t BaseOffset, |
213 | bool IsLittleEndian) override; |
214 | |
215 | bool needToSaveValidRelocs() override { return true; } |
216 | |
217 | void updateAndSaveValidRelocs(bool IsDWARF5, uint64_t OriginalUnitOffset, |
218 | int64_t LinkedOffset, uint64_t StartOffset, |
219 | uint64_t EndOffset) override; |
220 | |
221 | void updateRelocationsWithUnitOffset(uint64_t OriginalUnitOffset, |
222 | uint64_t OutputUnitOffset) override; |
223 | |
224 | void clear() override { |
225 | ValidDebugInfoRelocs.clear(); |
226 | ValidDebugAddrRelocs.clear(); |
227 | } |
228 | }; |
229 | |
230 | private: |
231 | /// \defgroup Helpers Various helper methods. |
232 | /// |
233 | /// @{ |
234 | template <typename OutStreamer> |
235 | bool createStreamer(const Triple &TheTriple, |
236 | typename OutStreamer::OutputFileType FileType, |
237 | std::unique_ptr<OutStreamer> &Streamer, |
238 | raw_fd_ostream &OutFile); |
239 | |
240 | /// Attempt to load a debug object from disk. |
241 | ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj, |
242 | const Triple &triple); |
243 | ErrorOr<std::unique_ptr<dwarf_linker::DWARFFile>> |
244 | (const DebugMapObject &Obj, const DebugMap &DebugMap, |
245 | remarks::RemarkLinker &RL, |
246 | std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM); |
247 | |
248 | void collectRelocationsToApplyToSwiftReflectionSections( |
249 | const object::SectionRef &Section, StringRef &Contents, |
250 | const llvm::object::MachOObjectFile *MO, |
251 | const std::vector<uint64_t> &SectionToOffsetInDwarf, |
252 | const llvm::dsymutil::DebugMapObject *Obj, |
253 | std::vector<MachOUtils::DwarfRelocationApplicationInfo> |
254 | &RelocationsToApply) const; |
255 | |
256 | Error copySwiftInterfaces(StringRef Architecture) const; |
257 | |
258 | void copySwiftReflectionMetadata( |
259 | const llvm::dsymutil::DebugMapObject *Obj, |
260 | classic::DwarfStreamer *Streamer, |
261 | std::vector<uint64_t> &SectionToOffsetInDwarf, |
262 | std::vector<MachOUtils::DwarfRelocationApplicationInfo> |
263 | &RelocationsToApply); |
264 | |
265 | template <typename Linker> |
266 | bool linkImpl(const DebugMap &Map, |
267 | typename Linker::OutputFileType ObjectType); |
268 | |
269 | Error emitRelocations(const DebugMap &DM, |
270 | std::vector<ObjectWithRelocMap> &ObjectsForLinking); |
271 | |
272 | raw_fd_ostream &OutFile; |
273 | BinaryHolder &BinHolder; |
274 | LinkOptions Options; |
275 | std::mutex &ErrorHandlerMutex; |
276 | |
277 | std::vector<std::string> EmptyWarnings; |
278 | |
279 | /// A list of all .swiftinterface files referenced by the debug |
280 | /// info, mapping Module name to path on disk. The entries need to |
281 | /// be uniqued and sorted and there are only few entries expected |
282 | /// per compile unit, which is why this is a std::map. |
283 | std::map<std::string, std::string> ParseableSwiftInterfaces; |
284 | |
285 | bool ModuleCacheHintDisplayed = false; |
286 | bool ArchiveHintDisplayed = false; |
287 | bool HasVerificationErrors = false; |
288 | }; |
289 | |
290 | } // end namespace dsymutil |
291 | } // end namespace llvm |
292 | |
293 | #endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H |
294 | |