From 16acc91bc407d1912bcb198f248356fc76d06646 Mon Sep 17 00:00:00 2001 From: Frederic Riss Date: Fri, 13 Feb 2015 23:18:22 +0000 Subject: [PATCH] [dsymutil] Find relocations that correspond to debug map entries. These 'valid relocations' in the debug_info section will be how dsymutil identifies the DIEs it needs to keep in the linked debug information. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229178 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/dsymutil/DwarfLinker.cpp | 149 ++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index 51520884a4e..7a148e266ab 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -12,6 +12,7 @@ #include "dsymutil.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/Object/MachO.h" #include namespace llvm { @@ -44,6 +45,19 @@ private: }; /// \brief The core of the Dwarf linking logic. +/// +/// The link of the dwarf information from the object files will be +/// driven by the selection of 'root DIEs', which are DIEs that +/// describe variables or functions that are present in the linked +/// binary (and thus have entries in the debug map). All the debug +/// information that will be linked (the DIEs, but also the line +/// tables, ranges, ...) is derived from that set of root DIEs. +/// +/// The root DIEs are identified because they contain relocations that +/// correspond to a debug map entry at specific places (the low_pc for +/// a function, the location for a variable). These relocations are +/// called ValidRelocs in the DwarfLinker and are gathered as a very +/// first step when we start processing a DebugMapObject. class DwarfLinker { public: DwarfLinker(StringRef OutputFilename, bool Verbose) @@ -59,6 +73,45 @@ private: /// \brief Called at the end of a debug object link. void endDebugObject(); + /// \defgroup FindValidRelocations Translate debug map into a list + /// of relevant relocations + /// + /// @{ + struct ValidReloc { + uint32_t Offset; + uint32_t Size; + uint64_t Addend; + const DebugMapObject::DebugMapEntry *Mapping; + + ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend, + const DebugMapObject::DebugMapEntry *Mapping) + : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} + + bool operator<(const ValidReloc &RHS) const { return Offset < RHS.Offset; } + }; + + /// \brief The valid relocations for the current DebugMapObject. + /// This vector is sorted by relocation offset. + std::vector ValidRelocs; + + /// \brief Index into ValidRelocs of the next relocation to + /// consider. As we walk the DIEs in acsending file offset and as + /// ValidRelocs is sorted by file offset, keeping this index + /// uptodate is all we have to do to have a cheap lookup during the + /// root DIE selection. + unsigned NextValidReloc; + + bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + bool findValidRelocs(const object::SectionRef &Section, + const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + void findValidRelocsMachO(const object::SectionRef &Section, + const object::MachOObjectFile &Obj, + const DebugMapObject &DMO); + /// @} private: std::string OutputFilename; bool Verbose; @@ -83,9 +136,96 @@ void GatherDIEParents(const DWARFDebugInfoEntryMinimal *DIE, unsigned ParentIdx, void DwarfLinker::startDebugObject(DWARFContext &Dwarf) { Units.reserve(Dwarf.getNumCompileUnits()); + NextValidReloc = 0; } -void DwarfLinker::endDebugObject() { Units.clear(); } +void DwarfLinker::endDebugObject() { + Units.clear(); + ValidRelocs.clear(); +} + +/// \brief Iterate over the relocations of the given \p Section and +/// store the ones that correspond to debug map entries into the +/// ValidRelocs array. +void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section, + const object::MachOObjectFile &Obj, + const DebugMapObject &DMO) { + StringRef Contents; + Section.getContents(Contents); + DataExtractor Data(Contents, Obj.isLittleEndian(), 0); + + for (const object::RelocationRef &Reloc : Section.relocations()) { + object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl(); + MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef); + unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); + uint64_t Offset64; + if ((RelocSize != 4 && RelocSize != 8) || Reloc.getOffset(Offset64)) { + errs() << "warning: unsupported relocation in debug_info section.\n"; + continue; + } + uint32_t Offset = Offset64; + // Mach-o uses REL relocations, the addend is at the relocation offset. + uint64_t Addend = Data.getUnsigned(&Offset, RelocSize); + + auto Sym = Reloc.getSymbol(); + if (Sym != Obj.symbol_end()) { + StringRef SymbolName; + if (Sym->getName(SymbolName)) { + errs() << "warning: error getting relocation symbol name.\n"; + continue; + } + if (const auto *Mapping = DMO.lookupSymbol(SymbolName)) + ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping); + } else if (const auto *Mapping = DMO.lookupObjectAddress(Addend)) { + // Do not store the addend. The addend was the address of the + // symbol in the object file, the address in the binary that is + // stored in the debug map doesn't need to be offseted. + ValidRelocs.emplace_back(Offset64, RelocSize, 0, Mapping); + } + } +} + +/// \brief Dispatch the valid relocation finding logic to the +/// appropriate handler depending on the object file format. +bool DwarfLinker::findValidRelocs(const object::SectionRef &Section, + const object::ObjectFile &Obj, + const DebugMapObject &DMO) { + // Dispatch to the right handler depending on the file type. + if (auto *MachOObj = dyn_cast(&Obj)) + findValidRelocsMachO(Section, *MachOObj, DMO); + else + errs() << "warning: unsupported object file type: " << Obj.getFileName() + << '\n'; + + if (ValidRelocs.empty()) + return false; + + // Sort the relocations by offset. We will walk the DIEs linearly in + // the file, this allows us to just keep an index in the relocation + // array that we advance during our walk, rather than resorting to + // some associative container. See DwarfLinker::NextValidReloc. + std::sort(ValidRelocs.begin(), ValidRelocs.end()); + return true; +} + +/// \brief Look for relocations in the debug_info section that match +/// entries in the debug map. These relocations will drive the Dwarf +/// link by indicating which DIEs refer to symbols present in the +/// linked binary. +/// \returns wether there are any valid relocations in the debug info. +bool DwarfLinker::findValidRelocsInDebugInfo(const object::ObjectFile &Obj, + const DebugMapObject &DMO) { + // Find the debug_info section. + for (const object::SectionRef &Section : Obj.sections()) { + StringRef SectionName; + Section.getName(SectionName); + SectionName = SectionName.substr(SectionName.find_first_not_of("._")); + if (SectionName != "debug_info") + continue; + return findValidRelocs(Section, Obj, DMO); + } + return false; +} bool DwarfLinker::link(const DebugMap &Map) { @@ -103,6 +243,13 @@ bool DwarfLinker::link(const DebugMap &Map) { continue; } + // Look for relocations that correspond to debug map entries. + if (!findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { + if (Verbose) + outs() << "No valid relocations found. Skipping.\n"; + continue; + } + // Setup access to the debug info. DWARFContextInMemory DwarfContext(*ErrOrObj); startDebugObject(DwarfContext); -- 2.34.1