| 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 | |