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 isInitialized()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 { ObjectWithRelocMapObjectWithRelocMap57 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: DwarfLinkerForBinary(raw_fd_ostream & OutFile,BinaryHolder & BinHolder,LinkOptions Options,std::mutex & ErrorHandlerMutex)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. InputVerificationFailed()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: AddressManager(DwarfLinkerForBinary & Linker,const object::ObjectFile & Obj,const DebugMapObject & DMO,std::shared_ptr<DwarfLinkerForBinaryRelocationMap> DLBRM)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(DMO.getInstallName().value()); 152 const RelocationMap &RM = DMO.getRelocationMap().value(); 153 for (const auto &Reloc : RM.relocations()) { 154 const auto *DebugMapEntry = DMO.lookupSymbol(Reloc.SymbolName); 155 if (!DebugMapEntry) 156 continue; 157 std::optional<uint64_t> ObjAddress; 158 ObjAddress.emplace(DebugMapEntry->getValue().ObjectAddress.value()); 159 ValidDebugInfoRelocs.emplace_back( 160 Reloc.Offset, Reloc.Size, Reloc.Addend, Reloc.SymbolName, 161 SymbolMapping(ObjAddress, DebugMapEntry->getValue().BinaryAddress, 162 DebugMapEntry->getValue().Size)); 163 // FIXME: Support relocations debug_addr. 164 } 165 } else { 166 findValidRelocsInDebugSections(Obj, DMO); 167 } 168 } ~AddressManager()169 ~AddressManager() override { clear(); } 170 hasValidRelocs()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 needToSaveValidRelocs()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 clear()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 loadObject(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