diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2015-06-09 19:06:30 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2015-06-09 19:06:30 +0000 |
| commit | 85d8b2bbe386bcfe669575d05b61482d7be07e5d (patch) | |
| tree | 1dc5e75ab222a9ead44c699eceafab7a6ca7b310 /tools/dsymutil | |
| parent | 5a5ac124e1efaf208671f01c46edb15f29ed2a0b (diff) | |
Notes
Diffstat (limited to 'tools/dsymutil')
| -rw-r--r-- | tools/dsymutil/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tools/dsymutil/DebugMap.cpp | 161 | ||||
| -rw-r--r-- | tools/dsymutil/DebugMap.h | 86 | ||||
| -rw-r--r-- | tools/dsymutil/DwarfLinker.cpp | 304 | ||||
| -rw-r--r-- | tools/dsymutil/MachODebugMapParser.cpp | 14 | ||||
| -rw-r--r-- | tools/dsymutil/dsymutil.cpp | 44 | ||||
| -rw-r--r-- | tools/dsymutil/dsymutil.h | 7 |
7 files changed, 516 insertions, 101 deletions
diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt index 59b37a9b2900..88f9f1f083db 100644 --- a/tools/dsymutil/CMakeLists.txt +++ b/tools/dsymutil/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS MC Object Support + Target ) add_llvm_tool(llvm-dsymutil diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp index 9fa3f788a89f..1a81848847f6 100644 --- a/tools/dsymutil/DebugMap.cpp +++ b/tools/dsymutil/DebugMap.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// #include "DebugMap.h" +#include "BinaryHolder.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/DataTypes.h" @@ -46,8 +47,9 @@ void DebugMapObject::print(raw_ostream &OS) const { [](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; }); for (const auto &Sym : Entries) { OS << format("\t%016" PRIx64 " => %016" PRIx64 "+0x%x\t%s\n", - Sym.second.ObjectAddress, Sym.second.BinaryAddress, - Sym.second.Size, Sym.first.data()); + uint64_t(Sym.second.ObjectAddress), + uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size), + Sym.first.data()); } OS << '\n'; } @@ -78,15 +80,160 @@ DebugMapObject::lookupObjectAddress(uint64_t Address) const { } void DebugMap::print(raw_ostream &OS) const { - OS << "DEBUG MAP: " << BinaryTriple.getTriple() - << "\n\tobject addr => executable addr\tsymbol name\n"; - for (const auto &Obj : objects()) - Obj->print(OS); - OS << "END DEBUG MAP\n"; + yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0); + yout << const_cast<DebugMap &>(*this); } #ifndef NDEBUG void DebugMap::dump() const { print(errs()); } #endif + +namespace { +struct YAMLContext { + StringRef PrependPath; + Triple BinaryTriple; +}; +} + +ErrorOr<std::unique_ptr<DebugMap>> +DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, + bool Verbose) { + auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile); + if (auto Err = ErrOrFile.getError()) + return Err; + + YAMLContext Ctxt; + + Ctxt.PrependPath = PrependPath; + + std::unique_ptr<DebugMap> Res; + yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt); + yin >> Res; + + if (auto EC = yin.error()) + return EC; + + return std::move(Res); +} +} + +namespace yaml { + +// Normalize/Denormalize between YAML and a DebugMapObject. +struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO { + YamlDMO(IO &io) {} + YamlDMO(IO &io, dsymutil::DebugMapObject &Obj); + dsymutil::DebugMapObject denormalize(IO &IO); + + std::string Filename; + std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries; +}; + +void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>:: + mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) { + io.mapRequired("sym", s.first); + io.mapRequired("objAddr", s.second.ObjectAddress); + io.mapRequired("binAddr", s.second.BinaryAddress); + io.mapOptional("size", s.second.Size); +} + +void MappingTraits<dsymutil::DebugMapObject>::mapping( + IO &io, dsymutil::DebugMapObject &DMO) { + MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO); + io.mapRequired("filename", Norm->Filename); + io.mapRequired("symbols", Norm->Entries); +} + +void ScalarTraits<Triple>::output(const Triple &val, void *, + llvm::raw_ostream &out) { + out << val.str(); +} + +StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) { + value = Triple(scalar); + return StringRef(); +} + +size_t +SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size( + IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) { + return seq.size(); +} + +dsymutil::DebugMapObject & +SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element( + IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, + size_t index) { + if (index >= seq.size()) { + seq.resize(index + 1); + seq[index].reset(new dsymutil::DebugMapObject); + } + return *seq[index]; +} + +void MappingTraits<dsymutil::DebugMap>::mapping(IO &io, + dsymutil::DebugMap &DM) { + io.mapRequired("triple", DM.BinaryTriple); + io.mapOptional("objects", DM.Objects); + if (void *Ctxt = io.getContext()) + reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple; +} + +void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping( + IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) { + if (!DM) + DM.reset(new DebugMap()); + io.mapRequired("triple", DM->BinaryTriple); + io.mapOptional("objects", DM->Objects); + if (void *Ctxt = io.getContext()) + reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple; +} + +MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO( + IO &io, dsymutil::DebugMapObject &Obj) { + Filename = Obj.Filename; + Entries.reserve(Obj.Symbols.size()); + for (auto &Entry : Obj.Symbols) + Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue())); +} + +dsymutil::DebugMapObject +MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { + BinaryHolder BinHolder(/* Verbose =*/false); + const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext()); + SmallString<80> Path(Ctxt.PrependPath); + StringMap<uint64_t> SymbolAddresses; + + sys::path::append(Path, Filename); + auto ErrOrObjectFile = BinHolder.GetObjectFile(Path); + if (auto EC = ErrOrObjectFile.getError()) { + llvm::errs() << "warning: Unable to open " << Path << " " << EC.message() + << '\n'; + } else { + // Rewrite the object file symbol addresses in the debug map. The + // YAML input is mainly used to test llvm-dsymutil without + // requiring binaries checked-in. If we generate the object files + // during the test, we can't hardcode the symbols addresses, so + // look them up here and rewrite them. + for (const auto &Sym : ErrOrObjectFile->symbols()) { + StringRef Name; + uint64_t Address; + if (Sym.getName(Name) || Sym.getAddress(Address)) + continue; + SymbolAddresses[Name] = Address; + } + } + + dsymutil::DebugMapObject Res(Path); + for (auto &Entry : Entries) { + auto &Mapping = Entry.second; + uint64_t ObjAddress = Mapping.ObjectAddress; + auto AddressIt = SymbolAddresses.find(Entry.first); + if (AddressIt != SymbolAddresses.end()) + ObjAddress = AddressIt->getValue(); + Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size); + } + return Res; +} } } diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h index ee48b093d4fd..d0edbabb404b 100644 --- a/tools/dsymutil/DebugMap.h +++ b/tools/dsymutil/DebugMap.h @@ -28,6 +28,8 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/YAMLTraits.h" #include <vector> namespace llvm { @@ -66,6 +68,12 @@ class DebugMap { typedef std::vector<std::unique_ptr<DebugMapObject>> ObjectContainer; ObjectContainer Objects; + /// For YAML IO support. + ///@{ + friend yaml::MappingTraits<std::unique_ptr<DebugMap>>; + friend yaml::MappingTraits<DebugMap>; + DebugMap() = default; + ///@} public: DebugMap(const Triple &BinaryTriple) : BinaryTriple(BinaryTriple) {} @@ -90,6 +98,10 @@ public: #ifndef NDEBUG void dump() const; #endif + + /// Read a debug map for \a InputFile. + static ErrorOr<std::unique_ptr<DebugMap>> + parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose); }; /// \brief The DebugMapObject represents one object file described by @@ -99,12 +111,14 @@ public: class DebugMapObject { public: struct SymbolMapping { - uint64_t ObjectAddress; - uint64_t BinaryAddress; - uint32_t Size; + yaml::Hex64 ObjectAddress; + yaml::Hex64 BinaryAddress; + yaml::Hex32 Size; SymbolMapping(uint64_t ObjectAddress, uint64_t BinaryAddress, uint32_t Size) : ObjectAddress(ObjectAddress), BinaryAddress(BinaryAddress), Size(Size) {} + /// For YAML IO support + SymbolMapping() = default; }; typedef StringMapEntry<SymbolMapping> DebugMapEntry; @@ -141,6 +155,72 @@ private: std::string Filename; StringMap<SymbolMapping> Symbols; DenseMap<uint64_t, DebugMapEntry *> AddressToMapping; + + /// For YAMLIO support. + ///@{ + typedef std::pair<std::string, SymbolMapping> YAMLSymbolMapping; + friend yaml::MappingTraits<dsymutil::DebugMapObject>; + friend yaml::SequenceTraits<std::vector<std::unique_ptr<DebugMapObject>>>; + friend yaml::SequenceTraits<std::vector<YAMLSymbolMapping>>; + DebugMapObject() = default; + +public: + DebugMapObject &operator=(DebugMapObject RHS) { + std::swap(Filename, RHS.Filename); + std::swap(Symbols, RHS.Symbols); + std::swap(AddressToMapping, RHS.AddressToMapping); + return *this; + } + DebugMapObject(DebugMapObject &&RHS) { + Filename = std::move(RHS.Filename); + Symbols = std::move(RHS.Symbols); + AddressToMapping = std::move(RHS.AddressToMapping); + } + ///@} +}; +} +} + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::dsymutil::DebugMapObject::YAMLSymbolMapping) + +namespace llvm { +namespace yaml { + +using namespace llvm::dsymutil; + +template <> +struct MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>> { + static void mapping(IO &io, + std::pair<std::string, DebugMapObject::SymbolMapping> &s); + static const bool flow = true; +}; + +template <> struct MappingTraits<dsymutil::DebugMapObject> { + struct YamlDMO; + static void mapping(IO &io, dsymutil::DebugMapObject &DMO); +}; + +template <> struct ScalarTraits<Triple> { + static void output(const Triple &val, void *, llvm::raw_ostream &out); + static StringRef input(StringRef scalar, void *, Triple &value); + static bool mustQuote(StringRef) { return true; } +}; + +template <> +struct SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>> { + static size_t + size(IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq); + static dsymutil::DebugMapObject & + element(IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, + size_t index); +}; + +template <> struct MappingTraits<dsymutil::DebugMap> { + static void mapping(IO &io, dsymutil::DebugMap &DM); +}; + +template <> struct MappingTraits<std::unique_ptr<dsymutil::DebugMap>> { + static void mapping(IO &io, std::unique_ptr<dsymutil::DebugMap> &DM); }; } } diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index a6e62a838945..7dc15b990ec5 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -60,6 +60,36 @@ using HalfOpenIntervalMap = typedef HalfOpenIntervalMap<uint64_t, int64_t> FunctionIntervals; +// FIXME: Delete this structure once DIE::Values has a stable iterator we can +// use instead. +struct PatchLocation { + DIE *Die; + unsigned Index; + + PatchLocation() : Die(nullptr), Index(0) {} + PatchLocation(DIE &Die, unsigned Index) : Die(&Die), Index(Index) {} + PatchLocation(DIE &Die) + : Die(&Die), Index(std::distance(Die.values_begin(), Die.values_end())) {} + + void set(uint64_t New) const { + assert(Die); + assert((signed)Index < + std::distance(Die->values_begin(), Die->values_end())); + const auto &Old = Die->values_begin()[Index]; + assert(Old.getType() == DIEValue::isInteger); + Die->setValue(Index, + DIEValue(Old.getAttribute(), Old.getForm(), DIEInteger(New))); + } + + uint64_t get() const { + assert(Die); + assert((signed)Index < + std::distance(Die->values_begin(), Die->values_end())); + assert(Die->values_begin()[Index].getType() == DIEValue::isInteger); + return Die->values_begin()[Index].getDIEInteger().getValue(); + } +}; + /// \brief Stores all information relating to a compile unit, be it in /// its original instance in the object file to its brand new cloned /// and linked DIE tree. @@ -76,7 +106,7 @@ public: CompileUnit(DWARFUnit &OrigUnit, unsigned ID) : OrigUnit(OrigUnit), ID(ID), LowPc(UINT64_MAX), HighPc(0), RangeAlloc(), - Ranges(RangeAlloc), UnitRangeAttribute(nullptr) { + Ranges(RangeAlloc) { Info.resize(OrigUnit.getNumDIEs()); } @@ -106,13 +136,15 @@ public: uint64_t getLowPc() const { return LowPc; } uint64_t getHighPc() const { return HighPc; } - DIEInteger *getUnitRangesAttribute() const { return UnitRangeAttribute; } + Optional<PatchLocation> getUnitRangesAttribute() const { + return UnitRangeAttribute; + } const FunctionIntervals &getFunctionRanges() const { return Ranges; } - const std::vector<DIEInteger *> &getRangesAttributes() const { + const std::vector<PatchLocation> &getRangesAttributes() const { return RangeAttributes; } - const std::vector<std::pair<DIEInteger *, int64_t>> & + const std::vector<std::pair<PatchLocation, int64_t>> & getLocationAttributes() const { return LocationAttributes; } @@ -127,7 +159,7 @@ public: /// RefUnit by \p Attr. The attribute should be fixed up later to /// point to the absolute offset of \p Die in the debug_info section. void noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - DIEInteger *Attr); + PatchLocation Attr); /// \brief Apply all fixups recored by noteForwardReference(). void fixupForwardReferences(); @@ -138,11 +170,11 @@ public: /// \brief Keep track of a DW_AT_range attribute that we will need to /// patch up later. - void noteRangeAttribute(const DIE &Die, DIEInteger *Attr); + void noteRangeAttribute(const DIE &Die, PatchLocation Attr); /// \brief Keep track of a location attribute pointing to a location /// list in the debug_loc section. - void noteLocationAttribute(DIEInteger *Attr, int64_t PcOffset); + void noteLocationAttribute(PatchLocation Attr, int64_t PcOffset); /// \brief Add a name accelerator entry for \p Die with \p Name /// which is stored in the string table at \p Offset. @@ -154,8 +186,8 @@ public: void addTypeAccelerator(const DIE *Die, const char *Name, uint32_t Offset); struct AccelInfo { - StringRef Name; ///< Name of the entry. - const DIE *Die; ///< DIE this entry describes. + StringRef Name; ///< Name of the entry. + const DIE *Die; ///< DIE this entry describes. uint32_t NameOffset; ///< Offset of Name in the string pool. bool SkipPubSection; ///< Emit this entry only in the apple_* sections. @@ -186,7 +218,7 @@ private: /// The offsets for the attributes in this array couldn't be set while /// cloning because for cross-cu forward refences the target DIE's /// offset isn't known you emit the reference attribute. - std::vector<std::tuple<DIE *, const CompileUnit *, DIEInteger *>> + std::vector<std::tuple<DIE *, const CompileUnit *, PatchLocation>> ForwardDIEReferences; FunctionIntervals::Allocator RangeAlloc; @@ -198,15 +230,15 @@ private: /// \brief DW_AT_ranges attributes to patch after we have gathered /// all the unit's function addresses. /// @{ - std::vector<DIEInteger *> RangeAttributes; - DIEInteger *UnitRangeAttribute; + std::vector<PatchLocation> RangeAttributes; + Optional<PatchLocation> UnitRangeAttribute; /// @} /// \brief Location attributes that need to be transfered from th /// original debug_loc section to the liked one. They are stored /// along with the PC offset that is to be applied to their /// function's address. - std::vector<std::pair<DIEInteger *, int64_t>> LocationAttributes; + std::vector<std::pair<PatchLocation, int64_t>> LocationAttributes; /// \brief Accelerator entries for the unit, both for the pub* /// sections and the apple* ones. @@ -229,7 +261,7 @@ uint64_t CompileUnit::computeNextUnitOffset() { /// \brief Keep track of a forward cross-cu reference from this unit /// to \p Die that lives in \p RefUnit. void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - DIEInteger *Attr) { + PatchLocation Attr) { ForwardDIEReferences.emplace_back(Die, RefUnit, Attr); } @@ -238,9 +270,9 @@ void CompileUnit::fixupForwardReferences() { for (const auto &Ref : ForwardDIEReferences) { DIE *RefDie; const CompileUnit *RefUnit; - DIEInteger *Attr; + PatchLocation Attr; std::tie(RefDie, RefUnit, Attr) = Ref; - Attr->setValue(RefDie->getOffset() + RefUnit->getStartOffset()); + Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); } } @@ -251,14 +283,14 @@ void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); } -void CompileUnit::noteRangeAttribute(const DIE &Die, DIEInteger *Attr) { +void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) { if (Die.getTag() != dwarf::DW_TAG_compile_unit) RangeAttributes.push_back(Attr); else UnitRangeAttribute = Attr; } -void CompileUnit::noteLocationAttribute(DIEInteger *Attr, int64_t PcOffset) { +void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) { LocationAttributes.emplace_back(Attr, PcOffset); } @@ -387,6 +419,7 @@ class DwarfStreamer { uint32_t RangesSectionSize; uint32_t LocSectionSize; uint32_t LineSectionSize; + uint32_t FrameSectionSize; /// \brief Emit the pubnames or pubtypes section contribution for \p /// Unit into \p Sec. The data is provided in \p Names. @@ -460,6 +493,15 @@ public: /// \brief Emit the .debug_pubtypes contribution for \p Unit. void emitPubTypesForUnit(const CompileUnit &Unit); + + /// \brief Emit a CIE. + void emitCIE(StringRef CIEBytes); + + /// \brief Emit an FDE with data \p Bytes. + void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, + StringRef Bytes); + + uint32_t getFrameSectionSize() const { return FrameSectionSize; } }; bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { @@ -529,6 +571,7 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { RangesSectionSize = 0; LocSectionSize = 0; LineSectionSize = 0; + FrameSectionSize = 0; return true; } @@ -717,8 +760,7 @@ void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit, /// point to the new entries. void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, DWARFContext &Dwarf) { - const std::vector<std::pair<DIEInteger *, int64_t>> &Attributes = - Unit.getLocationAttributes(); + const auto &Attributes = Unit.getLocationAttributes(); if (Attributes.empty()) return; @@ -737,8 +779,8 @@ void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); for (const auto &Attr : Attributes) { - uint32_t Offset = Attr.first->getValue(); - Attr.first->setValue(LocSectionSize); + uint32_t Offset = Attr.first.get(); + Attr.first.set(LocSectionSize); // This is the quantity to add to the old location address to get // the correct address for the new one. int64_t LocPcOffset = Attr.second + UnitPcOffset; @@ -934,7 +976,7 @@ void DwarfStreamer::emitPubSectionForUnit( Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Length Asm->OutStreamer->EmitLabel(BeginLabel); Asm->EmitInt16(dwarf::DW_PUBNAMES_VERSION); // Version - Asm->EmitInt32(Unit.getStartOffset()); // Unit offset + Asm->EmitInt32(Unit.getStartOffset()); // Unit offset Asm->EmitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset()); // Size HeaderEmitted = true; } @@ -961,6 +1003,28 @@ void DwarfStreamer::emitPubTypesForUnit(const CompileUnit &Unit) { "types", Unit, Unit.getPubtypes()); } +/// \brief Emit a CIE into the debug_frame section. +void DwarfStreamer::emitCIE(StringRef CIEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->EmitBytes(CIEBytes); + FrameSectionSize += CIEBytes.size(); +} + +/// \brief Emit a FDE into the debug_frame section. \p FDEBytes +/// contains the FDE data without the length, CIE offset and address +/// which will be replaced with the paramter values. +void DwarfStreamer::emitFDE(uint32_t CIEOffset, uint32_t AddrSize, + uint32_t Address, StringRef FDEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->EmitIntValue(FDEBytes.size() + 4 + AddrSize, 4); + MS->EmitIntValue(CIEOffset, 4); + MS->EmitIntValue(Address, AddrSize); + MS->EmitBytes(FDEBytes); + FrameSectionSize += FDEBytes.size() + 8 + AddrSize; +} + /// \brief The core of the Dwarf linking logic. /// /// The link of the dwarf information from the object files will be @@ -979,7 +1043,7 @@ class DwarfLinker { public: DwarfLinker(StringRef OutputFilename, const LinkOptions &Options) : OutputFilename(OutputFilename), Options(Options), - BinHolder(Options.Verbose) {} + BinHolder(Options.Verbose), LastCIEOffset(0) {} ~DwarfLinker() { for (auto *Abbrev : Abbreviations) @@ -1177,6 +1241,10 @@ private: /// \brief Emit the accelerator entries for \p Unit. void emitAcceleratorEntriesForUnit(CompileUnit &Unit); + /// \brief Patch the frame info for an object file and emit it. + void patchFrameInfoForObject(const DebugMapObject &, DWARFContext &, + unsigned AddressSize); + /// \brief DIELoc objects that need to be destructed (but not freed!). std::vector<DIELoc *> DIELocs; /// \brief DIEBlock objects that need to be destructed (but not freed!). @@ -1226,6 +1294,16 @@ private: /// /// See startDebugObject() for a more complete description of its use. std::map<uint64_t, std::pair<uint64_t, int64_t>> Ranges; + + /// \brief The CIEs that have been emitted in the output + /// section. The actual CIE data serves a the key to this StringMap, + /// this takes care of comparing the semantics of CIEs defined in + /// different object files. + StringMap<uint32_t> EmittedCIEs; + + /// Offset of the last CIE that has been emitted in the output + /// debug_frame section. + uint32_t LastCIEOffset; }; /// \brief Similar to DWARFUnitSection::getUnitForOffset(), but @@ -1477,15 +1555,15 @@ bool DwarfLinker::hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, return false; const auto &ValidReloc = ValidRelocs[NextValidReloc++]; + const auto &Mapping = ValidReloc.Mapping->getValue(); if (Options.Verbose) outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey() << " " << format("\t%016" PRIx64 " => %016" PRIx64, - ValidReloc.Mapping->getValue().ObjectAddress, - ValidReloc.Mapping->getValue().BinaryAddress); + uint64_t(Mapping.ObjectAddress), + uint64_t(Mapping.BinaryAddress)); - Info.AddrAdjust = int64_t(ValidReloc.Mapping->getValue().BinaryAddress) + - ValidReloc.Addend - - ValidReloc.Mapping->getValue().ObjectAddress; + Info.AddrAdjust = int64_t(Mapping.BinaryAddress) + ValidReloc.Addend - + Mapping.ObjectAddress; Info.InDebugMap = true; return true; } @@ -1760,7 +1838,7 @@ unsigned DwarfLinker::cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, const char *String = *Val.getAsCString(&U); unsigned Offset = StringPool.getStringOffset(String); Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, - new (DIEAlloc) DIEInteger(Offset)); + DIEInteger(Offset)); return 4; } @@ -1803,24 +1881,24 @@ unsigned DwarfLinker::cloneDieReferenceAttribute( // to find the unit offset. (We don't have a DwarfDebug) // FIXME: we should be able to design DIEEntry reliance on // DwarfDebug away. - DIEInteger *Attr; + uint64_t Attr; if (Ref < InputDIE.getOffset()) { // We must have already cloned that DIE. uint32_t NewRefOffset = RefUnit->getStartOffset() + NewRefDie->getOffset(); - Attr = new (DIEAlloc) DIEInteger(NewRefOffset); + Attr = NewRefOffset; } else { // A forward reference. Note and fixup later. - Attr = new (DIEAlloc) DIEInteger(0xBADDEF); - Unit.noteForwardReference(NewRefDie, RefUnit, Attr); + Attr = 0xBADDEF; + Unit.noteForwardReference(NewRefDie, RefUnit, PatchLocation(Die)); } Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_ref_addr, - Attr); + DIEInteger(Attr)); return AttrSize; } Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), - new (DIEAlloc) DIEEntry(*NewRefDie)); + DIEEntry(*NewRefDie)); return AttrSize; } @@ -1831,23 +1909,29 @@ unsigned DwarfLinker::cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize) { DIE *Attr; - DIEValue *Value; + DIEValue Value; DIELoc *Loc = nullptr; DIEBlock *Block = nullptr; // Just copy the block data over. if (AttrSpec.Form == dwarf::DW_FORM_exprloc) { - Loc = new (DIEAlloc) DIELoc(); + Loc = new (DIEAlloc) DIELoc; DIELocs.push_back(Loc); } else { - Block = new (DIEAlloc) DIEBlock(); + Block = new (DIEAlloc) DIEBlock; DIEBlocks.push_back(Block); } Attr = Loc ? static_cast<DIE *>(Loc) : static_cast<DIE *>(Block); - Value = Loc ? static_cast<DIEValue *>(Loc) : static_cast<DIEValue *>(Block); + + if (Loc) + Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), Loc); + else + Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), Block); ArrayRef<uint8_t> Bytes = *Val.getAsBlock(); for (auto Byte : Bytes) Attr->addValue(static_cast<dwarf::Attribute>(0), dwarf::DW_FORM_data1, - new (DIEAlloc) DIEInteger(Byte)); + DIEInteger(Byte)); // FIXME: If DIEBlock and DIELoc just reuses the Size field of // the DIE class, this if could be replaced by // Attr->setSize(Bytes.size()). @@ -1857,8 +1941,7 @@ unsigned DwarfLinker::cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, else Block->ComputeSize(&Streamer->getAsmPrinter()); } - Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), - Value); + Die.addValue(Value); return AttrSize; } @@ -1893,8 +1976,7 @@ unsigned DwarfLinker::cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, } Die.addValue(static_cast<dwarf::Attribute>(AttrSpec.Attr), - static_cast<dwarf::Form>(AttrSpec.Form), - new (DIEAlloc) DIEInteger(Addr)); + static_cast<dwarf::Form>(AttrSpec.Form), DIEInteger(Addr)); return Unit.getOrigUnit().getAddressByteSize(); } @@ -1922,15 +2004,15 @@ unsigned DwarfLinker::cloneScalarAttribute( &Unit.getOrigUnit(), &InputDIE); return 0; } - DIEInteger *Attr = new (DIEAlloc) DIEInteger(Value); + DIEInteger Attr(Value); if (AttrSpec.Attr == dwarf::DW_AT_ranges) - Unit.noteRangeAttribute(Die, Attr); + Unit.noteRangeAttribute(Die, PatchLocation(Die)); // A more generic way to check for location attributes would be // nice, but it's very unlikely that any other attribute needs a // location list. else if (AttrSpec.Attr == dwarf::DW_AT_location || AttrSpec.Attr == dwarf::DW_AT_frame_base) - Unit.noteLocationAttribute(Attr, Info.PCOffset); + Unit.noteLocationAttribute(PatchLocation(Die), Info.PCOffset); else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) Info.IsDeclaration = true; @@ -2157,14 +2239,15 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, Unit.addTypeAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset); } - DIEAbbrev &NewAbbrev = Die->getAbbrev(); + DIEAbbrev NewAbbrev = Die->generateAbbrev(); // If a scope DIE is kept, we must have kept at least one child. If // it's not the case, we'll just be emitting one wasteful end of // children marker, but things won't break. if (InputDIE.hasChildren()) NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); // Assign a permanent abbrev number - AssignAbbrev(Die->getAbbrev()); + AssignAbbrev(NewAbbrev); + Die->setAbbrevNumber(NewAbbrev.getNumber()); // Add the size of the abbreviation number to the output offset. OutOffset += getULEB128Size(Die->getAbbrevNumber()); @@ -2213,8 +2296,8 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); for (const auto &RangeAttribute : Unit.getRangesAttributes()) { - uint32_t Offset = RangeAttribute->getValue(); - RangeAttribute->setValue(Streamer->getRangesSectionSize()); + uint32_t Offset = RangeAttribute.get(); + RangeAttribute.set(Streamer->getRangesSectionSize()); RangeList.extract(RangeExtractor, &Offset); const auto &Entries = RangeList.getEntries(); const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); @@ -2241,10 +2324,10 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, /// but for the sake of initial bit-for-bit compatibility with legacy /// dsymutil, we have to do it in a delayed pass. void DwarfLinker::generateUnitRanges(CompileUnit &Unit) const { - DIEInteger *Attr = Unit.getUnitRangesAttribute(); + auto Attr = Unit.getUnitRangesAttribute(); if (Attr) - Attr->setValue(Streamer->getRangesSectionSize()); - Streamer->emitUnitRangesEntries(Unit, Attr != nullptr); + Attr->set(Streamer->getRangesSectionSize()); + Streamer->emitUnitRangesEntries(Unit, static_cast<bool>(Attr)); } /// \brief Insert the new line info sequence \p Seq into the current @@ -2286,8 +2369,7 @@ static void insertLineSequence(std::vector<DWARFDebugLine::Row> &Seq, /// are present in the binary. void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf) { - const DWARFDebugInfoEntryMinimal *CUDie = - Unit.getOrigUnit().getUnitDIE(); + const DWARFDebugInfoEntryMinimal *CUDie = Unit.getOrigUnit().getUnitDIE(); uint64_t StmtList = CUDie->getAttributeValueAsSectionOffset( &Unit.getOrigUnit(), dwarf::DW_AT_stmt_list, -1ULL); if (StmtList == -1ULL) @@ -2295,15 +2377,16 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, // Update the cloned DW_AT_stmt_list with the correct debug_line offset. if (auto *OutputDIE = Unit.getOutputUnitDIE()) { - const auto &Abbrev = OutputDIE->getAbbrev().getData(); - auto Stmt = std::find_if( - Abbrev.begin(), Abbrev.end(), [](const DIEAbbrevData &AbbrevData) { - return AbbrevData.getAttribute() == dwarf::DW_AT_stmt_list; - }); - assert(Stmt < Abbrev.end() && "Didn't find DW_AT_stmt_list in cloned DIE!"); - DIEInteger *StmtAttr = - cast<DIEInteger>(OutputDIE->getValues()[Stmt - Abbrev.begin()]); - StmtAttr->setValue(Streamer->getLineSectionSize()); + auto Stmt = + std::find_if(OutputDIE->values_begin(), OutputDIE->values_end(), + [](const DIEValue &Value) { + return Value.getAttribute() == dwarf::DW_AT_stmt_list; + }); + assert(Stmt != OutputDIE->values_end() && + "Didn't find DW_AT_stmt_list in cloned DIE!"); + OutputDIE->setValue(Stmt - OutputDIE->values_begin(), + DIEValue(Stmt->getAttribute(), Stmt->getForm(), + DIEInteger(Streamer->getLineSectionSize()))); } // Parse the original line info for the unit. @@ -2422,6 +2505,91 @@ void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { Streamer->emitPubTypesForUnit(Unit); } +/// \brief Read the frame info stored in the object, and emit the +/// patched frame descriptions for the linked binary. +/// +/// This is actually pretty easy as the data of the CIEs and FDEs can +/// be considered as black boxes and moved as is. The only thing to do +/// is to patch the addresses in the headers. +void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, + DWARFContext &OrigDwarf, + unsigned AddrSize) { + StringRef FrameData = OrigDwarf.getDebugFrameSection(); + if (FrameData.empty()) + return; + + DataExtractor Data(FrameData, OrigDwarf.isLittleEndian(), 0); + uint32_t InputOffset = 0; + + // Store the data of the CIEs defined in this object, keyed by their + // offsets. + DenseMap<uint32_t, StringRef> LocalCIES; + + while (Data.isValidOffset(InputOffset)) { + uint32_t EntryOffset = InputOffset; + uint32_t InitialLength = Data.getU32(&InputOffset); + if (InitialLength == 0xFFFFFFFF) + return reportWarning("Dwarf64 bits no supported"); + + uint32_t CIEId = Data.getU32(&InputOffset); + if (CIEId == 0xFFFFFFFF) { + // This is a CIE, store it. + StringRef CIEData = FrameData.substr(EntryOffset, InitialLength + 4); + LocalCIES[EntryOffset] = CIEData; + // The -4 is to account for the CIEId we just read. + InputOffset += InitialLength - 4; + continue; + } + + uint32_t Loc = Data.getUnsigned(&InputOffset, AddrSize); + + // Some compilers seem to emit frame info that doesn't start at + // the function entry point, thus we can't just lookup the address + // in the debug map. Use the linker's range map to see if the FDE + // describes something that we can relocate. + auto Range = Ranges.upper_bound(Loc); + if (Range != Ranges.begin()) + --Range; + if (Range == Ranges.end() || Range->first > Loc || + Range->second.first <= Loc) { + // The +4 is to account for the size of the InitialLength field itself. + InputOffset = EntryOffset + InitialLength + 4; + continue; + } + + // This is an FDE, and we have a mapping. + // Have we already emitted a corresponding CIE? + StringRef CIEData = LocalCIES[CIEId]; + if (CIEData.empty()) + return reportWarning("Inconsistent debug_frame content. Dropping."); + + // Look if we already emitted a CIE that corresponds to the + // referenced one (the CIE data is the key of that lookup). + auto IteratorInserted = EmittedCIEs.insert( + std::make_pair(CIEData, Streamer->getFrameSectionSize())); + // If there is no CIE yet for this ID, emit it. + if (IteratorInserted.second || + // FIXME: dsymutil-classic only caches the last used CIE for + // reuse. Mimic that behavior for now. Just removing that + // second half of the condition and the LastCIEOffset variable + // makes the code DTRT. + LastCIEOffset != IteratorInserted.first->getValue()) { + LastCIEOffset = Streamer->getFrameSectionSize(); + IteratorInserted.first->getValue() = LastCIEOffset; + Streamer->emitCIE(CIEData); + } + + // Emit the FDE with updated address and CIE pointer. + // (4 + AddrSize) is the size of the CIEId + initial_location + // fields that will get reconstructed by emitFDE(). + unsigned FDERemainingBytes = InitialLength - (4 + AddrSize); + Streamer->emitFDE(IteratorInserted.first->getValue(), AddrSize, + Loc + Range->second.second, + FrameData.substr(InputOffset, FDERemainingBytes)); + InputOffset += FDERemainingBytes; + } +} + bool DwarfLinker::link(const DebugMap &Map) { if (Map.begin() == Map.end()) { @@ -2519,6 +2687,10 @@ bool DwarfLinker::link(const DebugMap &Map) { Streamer->emitDIE(*CurrentUnit.getOutputUnitDIE()); } + if (!ValidRelocs.empty() && !Options.NoOutput && !Units.empty()) + patchFrameInfoForObject(*Obj, DwarfContext, + Units[0].getOrigUnit().getAddressByteSize()); + // Clean-up before starting working on the next object. endDebugObject(); } diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp index bf64303b9eab..b803e410199d 100644 --- a/tools/dsymutil/MachODebugMapParser.cpp +++ b/tools/dsymutil/MachODebugMapParser.cpp @@ -244,10 +244,16 @@ void MachODebugMapParser::loadMainBinarySymbols() { namespace llvm { namespace dsymutil { -llvm::ErrorOr<std::unique_ptr<DebugMap>> -parseDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose) { - MachODebugMapParser Parser(InputFile, PrependPath, Verbose); - return Parser.parse(); +llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile, + StringRef PrependPath, + bool Verbose, + bool InputIsYAML) { + if (!InputIsYAML) { + MachODebugMapParser Parser(InputFile, PrependPath, Verbose); + return Parser.parse(); + } else { + return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); + } } } } diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp index 4fc91b029f68..50091935a44e 100644 --- a/tools/dsymutil/dsymutil.cpp +++ b/tools/dsymutil/dsymutil.cpp @@ -30,26 +30,32 @@ using namespace llvm::cl; static opt<std::string> InputFile(Positional, desc("<input file>"), init("a.out")); -static opt<std::string> OutputFileOpt("o", desc("Specify the output file." - " default: <input file>.dwarf"), - value_desc("filename")); +static opt<std::string> + OutputFileOpt("o", + desc("Specify the output file. default: <input file>.dwarf"), + value_desc("filename")); -static opt<std::string> OsoPrependPath("oso-prepend-path", - desc("Specify a directory to prepend " - "to the paths of object files."), - value_desc("path")); +static opt<std::string> OsoPrependPath( + "oso-prepend-path", + desc("Specify a directory to prepend to the paths of object files."), + value_desc("path")); static opt<bool> Verbose("v", desc("Verbosity level"), init(false)); -static opt<bool> NoOutput("no-output", desc("Do the link in memory, but do " - "not emit the result file."), - init(false)); - static opt<bool> - ParseOnly("parse-only", - desc("Only parse the debug map, do not actaully link " - "the DWARF."), - init(false)); + NoOutput("no-output", + desc("Do the link in memory, but do not emit the result file."), + init(false)); + +static opt<bool> DumpDebugMap( + "dump-debug-map", + desc("Parse and dump the debug map to standard output. Not DWARF link " + "will take place."), + init(false)); + +static opt<bool> InputIsYAMLDebugMap( + "y", desc("Treat the input file is a YAML debug map rather than a binary."), + init(false)); } int main(int argc, char **argv) { @@ -59,7 +65,9 @@ int main(int argc, char **argv) { LinkOptions Options; llvm::cl::ParseCommandLineOptions(argc, argv, "llvm dsymutil\n"); - auto DebugMapPtrOrErr = parseDebugMap(InputFile, OsoPrependPath, Verbose); + + auto DebugMapPtrOrErr = + parseDebugMap(InputFile, OsoPrependPath, Verbose, InputIsYAMLDebugMap); Options.Verbose = Verbose; Options.NoOutput = NoOutput; @@ -75,10 +83,10 @@ int main(int argc, char **argv) { return 1; } - if (Verbose) + if (Verbose || DumpDebugMap) (*DebugMapPtrOrErr)->print(llvm::outs()); - if (ParseOnly) + if (DumpDebugMap) return 0; std::string OutputFile; diff --git a/tools/dsymutil/dsymutil.h b/tools/dsymutil/dsymutil.h index e9f7cd951878..408918779833 100644 --- a/tools/dsymutil/dsymutil.h +++ b/tools/dsymutil/dsymutil.h @@ -33,9 +33,10 @@ struct LinkOptions { /// \brief Extract the DebugMap from the given file. /// The file has to be a MachO object file. -llvm::ErrorOr<std::unique_ptr<DebugMap>> -parseDebugMap(StringRef InputFile, StringRef PrependPath = "", - bool Verbose = false); +llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile, + StringRef PrependPath, + bool Verbose, + bool InputIsYAML); /// \brief Link the Dwarf debuginfo as directed by the passed DebugMap /// \p DM into a DwarfFile named \p OutputFilename. |
