diff --git a/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test b/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test index d2881933135..094000aafd5 100644 --- a/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test +++ b/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test @@ -99,5 +99,9 @@ CHECK: DW_TAG_formal_parameter [16] CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000060] = "arg") CHECK: DW_TAG_inlined_subroutine [17] CHECK: DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x0044 => {0x0000015f} "inc") +CHECK: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000 +CHECK: [0x0000000100000f94 - 0x0000000100000f9a) +CHECK: [0x0000000100000f9f - 0x0000000100000fa7)) + CHECK: NULL CHECK: NULL diff --git a/test/tools/dsymutil/X86/basic-lto-linking-x86.test b/test/tools/dsymutil/X86/basic-lto-linking-x86.test index 7349a33b315..5322bde92d9 100644 --- a/test/tools/dsymutil/X86/basic-lto-linking-x86.test +++ b/test/tools/dsymutil/X86/basic-lto-linking-x86.test @@ -110,7 +110,9 @@ CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000100000f94) CHECK DW_AT_high_pc [DW_FORM_addr] (0x0000000100000fa7) CHECK: DW_TAG_inlined_subroutine [15] CHECK: DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x009a => {0x000001d4} "inc") - +CHECK: DW_AT_ranges [DW_FORM_data4] (0x00000000 +CHECK: [0x0000000100000f94 - 0x0000000100000f9a) +CHECK: [0x0000000100000f9f - 0x0000000100000fa7)) CHECK: NULL CHECK: NULL CHECK: DW_TAG_subprogram [11] diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index 1880028f9ea..480324f4111 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -56,6 +56,8 @@ using HalfOpenIntervalMap = IntervalMap::LeafSize, IntervalMapHalfOpenInfo>; +typedef HalfOpenIntervalMap FunctionIntervals; + /// \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. @@ -72,7 +74,7 @@ public: CompileUnit(DWARFUnit &OrigUnit) : OrigUnit(OrigUnit), LowPc(UINT64_MAX), HighPc(0), RangeAlloc(), - Ranges(RangeAlloc) { + Ranges(RangeAlloc), UnitRangeAttribute(nullptr) { Info.resize(OrigUnit.getNumDIEs()); } @@ -100,6 +102,11 @@ public: uint64_t getLowPc() const { return LowPc; } uint64_t getHighPc() const { return HighPc; } + DIEInteger *getUnitRangesAttribute() const { return UnitRangeAttribute; } + const FunctionIntervals &getFunctionRanges() const { return Ranges; } + const std::vector &getRangesAttributes() const { + return RangeAttributes; + } /// \brief Compute the end offset for this unit. Must be /// called after the CU's DIEs have been cloned. @@ -120,6 +127,10 @@ public: /// relocatad by applying offset \p PCOffset. void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + /// \bried Keep trak of a DW_AT_range attribute that we will need to + /// patch up later. + void noteRangeAttribute(const DIE &Die, DIEInteger *Attr); + private: DWARFUnit &OrigUnit; std::vector Info; ///< DIE info indexed by DIE index. @@ -140,11 +151,18 @@ private: std::vector> ForwardDIEReferences; - HalfOpenIntervalMap::Allocator RangeAlloc; + FunctionIntervals::Allocator RangeAlloc; /// \brief The ranges in that interval map are the PC ranges for /// functions in this unit, associated with the PC offset to apply /// to the addresses to get the linked address. - HalfOpenIntervalMap Ranges; + FunctionIntervals Ranges; + + /// \brief DW_AT_ranges attributes to patch after we have gathered + /// all the unit's function addresses. + /// @{ + std::vector RangeAttributes; + DIEInteger *UnitRangeAttribute; + /// @} }; uint64_t CompileUnit::computeNextUnitOffset() { @@ -182,6 +200,13 @@ void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); } +void CompileUnit::noteRangeAttribute(const DIE &Die, DIEInteger *Attr) { + if (Die.getTag() != dwarf::DW_TAG_compile_unit) + RangeAttributes.push_back(Attr); + else + UnitRangeAttribute = Attr; +} + /// \brief A string table that doesn't need relocations. /// /// We are doing a final link, no need for a string table that @@ -290,6 +315,8 @@ class DwarfStreamer { /// \brief the file we stream the linked Dwarf to. std::unique_ptr OutFile; + uint32_t RangesSectionSize; + public: /// \brief Actually create the streamer and the ouptut file. /// @@ -322,6 +349,19 @@ public: /// \brief Emit the string table described by \p Pool. void emitStrings(const NonRelocatableStringpool &Pool); + + /// \brief Emit debug_ranges for \p FuncRange by translating the + /// original \p Entries. + void emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + FunctionIntervals::const_iterator FuncRange, + const std::vector &Entries, + unsigned AddressSize); + + /// \brief Emit debug_ranges entries for a DW_TAG_compile_unit's DW_AT_ranges. + void emitUnitRangesEntries(CompileUnit &Unit); + + uint32_t getRangesSectionSize() const { return RangesSectionSize; } }; bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { @@ -387,6 +427,8 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { if (!Asm) return error("no asm printer for target " + TripleName, Context); + RangesSectionSize = 0; + return true; } @@ -449,6 +491,82 @@ void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) { StringRef(Entry->getKey().data(), Entry->getKey().size() + 1)); } +/// \brief Emit the debug_range section contents for \p FuncRange by +/// translating the original \p Entries. The debug_range section +/// format is totally trivial, consisting just of pairs of address +/// sized addresses describing the ranges. +void DwarfStreamer::emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + FunctionIntervals::const_iterator FuncRange, + const std::vector &Entries, + unsigned AddressSize) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + + // Offset each range by the right amount. + int64_t PcOffset = FuncRange.value() + UnitPcOffset; + for (const auto &Range : Entries) { + if (Range.isBaseAddressSelectionEntry(AddressSize)) { + warn("unsupported base address selection operation", + "emitting debug_ranges"); + break; + } + // Do not emit empty ranges. + if (Range.StartAddress == Range.EndAddress) + continue; + + // All range entries should lie in the function range. + if (!(Range.StartAddress + OrigLowPc >= FuncRange.start() && + Range.EndAddress + OrigLowPc <= FuncRange.stop())) + warn("inconsistent range data.", "emitting debug_ranges"); + MS->EmitIntValue(Range.StartAddress + PcOffset, AddressSize); + MS->EmitIntValue(Range.EndAddress + PcOffset, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->EmitIntValue(0, AddressSize); + MS->EmitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + +/// \brief Emit the debug_range contents for a compile_unit level +/// DW_AT_ranges attribute. Just aggregate all the ranges gathered +/// inside that unit. +void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + + // Offset each range by the right amount. + int64_t PcOffset = -Unit.getLowPc(); + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + // Gather the ranges in a vector, so that we can simplify them. The + // IntervalMap will have coalesced the non-linked ranges, but here + // we want to coalesce the linked addresses. + std::vector> Ranges; + const auto &FunctionRanges = Unit.getFunctionRanges(); + for (auto Range = FunctionRanges.begin(), End = FunctionRanges.end(); + Range != End; ++Range) + Ranges.push_back(std::make_pair(Range.start() + Range.value() + PcOffset, + Range.stop() + Range.value() + PcOffset)); + + // The object addresses where sorted, but again, the linked + // addresses might end up in a different order. + std::sort(Ranges.begin(), Ranges.end()); + + // Emit coalesced ranges. + for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; ++Range) { + MS->EmitIntValue(Range->first, AddressSize); + while (Range + 1 != End && Range->second == (Range + 1)->first) + ++Range; + MS->EmitIntValue(Range->second, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->EmitIntValue(0, AddressSize); + MS->EmitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + /// \brief The core of the Dwarf linking logic. /// /// The link of the dwarf information from the object files will be @@ -622,7 +740,7 @@ private: /// \brief Helper for cloneDIE. unsigned cloneScalarAttribute(DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, - const CompileUnit &U, AttributeSpec AttrSpec, + CompileUnit &U, AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize); /// \brief Helper for cloneDIE. @@ -639,6 +757,14 @@ private: /// be changed to a vecot of unique_ptrs. std::vector Abbreviations; + /// \brief Compute and emit debug_ranges section for \p Unit, and + /// patch the attributes referencing it. + void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf) const; + + /// \brief Generate and emit the DW_AT_ranges attribute for a + /// compile_unit if it had one. + void generateUnitRanges(CompileUnit &Unit) const; + /// \brief DIELoc objects that need to be destructed (but not freed!). std::vector DIELocs; /// \brief DIEBlock objects that need to be destructed (but not freed!). @@ -658,7 +784,7 @@ private: CompileUnit *getUnitForOffset(unsigned Offset); void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr, - const DWARFDebugInfoEntryMinimal *DIE = nullptr); + const DWARFDebugInfoEntryMinimal *DIE = nullptr) const; bool createStreamer(Triple TheTriple, StringRef OutputFilename); /// @} @@ -711,7 +837,7 @@ const DWARFDebugInfoEntryMinimal *DwarfLinker::resolveDIEReference( /// \brief Report a warning to the user, optionaly including /// information about a specific \p DIE related to the warning. void DwarfLinker::reportWarning(const Twine &Warning, const DWARFUnit *Unit, - const DWARFDebugInfoEntryMinimal *DIE) { + const DWARFDebugInfoEntryMinimal *DIE) const { StringRef Context = ""; if (CurrentDebugObject) Context = CurrentDebugObject->getObjectFilename(); @@ -1309,9 +1435,8 @@ unsigned DwarfLinker::cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, /// \brief Clone a scalar attribute and add it to \p Die. /// \returns the size of the new attribute. unsigned DwarfLinker::cloneScalarAttribute( - DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, - const CompileUnit &Unit, AttributeSpec AttrSpec, const DWARFFormValue &Val, - unsigned AttrSize) { + DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit, + AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize) { uint64_t Value; if (AttrSpec.Attr == dwarf::DW_AT_high_pc && Die.getTag() == dwarf::DW_TAG_compile_unit) { @@ -1330,8 +1455,11 @@ unsigned DwarfLinker::cloneScalarAttribute( &Unit.getOrigUnit(), &InputDIE); return 0; } + DIEInteger *Attr = new (DIEAlloc) DIEInteger(Value); + if (AttrSpec.Attr == dwarf::DW_AT_ranges) + Unit.noteRangeAttribute(Die, Attr); Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), - new (DIEAlloc) DIEInteger(Value)); + Attr); return AttrSize; } @@ -1533,6 +1661,61 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, return Die; } +/// \brief Patch the input object file relevant debug_ranges entries +/// and emit them in the output file. Update the relevant attributes +/// to point at the new entries. +void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, + DWARFContext &OrigDwarf) const { + DWARFDebugRangeList RangeList; + const auto &FunctionRanges = Unit.getFunctionRanges(); + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + DataExtractor RangeExtractor(OrigDwarf.getRangeSection(), + OrigDwarf.isLittleEndian(), AddressSize); + auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + const auto *OrigUnitDie = OrigUnit.getCompileUnitDIE(false); + uint64_t OrigLowPc = OrigUnitDie->getAttributeValueAsAddress( + &OrigUnit, dwarf::DW_AT_low_pc, -1ULL); + // Ranges addresses are based on the unit's low_pc. Compute the + // offset we need to apply to adapt to the the new unit's low_pc. + int64_t UnitPcOffset = 0; + if (OrigLowPc != -1ULL) + UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); + + for (const auto &RangeAttribute : Unit.getRangesAttributes()) { + uint32_t Offset = RangeAttribute->getValue(); + RangeAttribute->setValue(Streamer->getRangesSectionSize()); + RangeList.extract(RangeExtractor, &Offset); + const auto &Entries = RangeList.getEntries(); + const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); + + if (CurrRange == InvalidRange || First.StartAddress < CurrRange.start() || + First.StartAddress >= CurrRange.stop()) { + CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); + if (CurrRange == InvalidRange || + CurrRange.start() > First.StartAddress + OrigLowPc) { + reportWarning("no mapping for range."); + continue; + } + } + + Streamer->emitRangesEntries(UnitPcOffset, OrigLowPc, CurrRange, Entries, + AddressSize); + } +} + +/// \brief Generate the debug_ranges entries for \p Unit's +/// DW_AT_ranges attribute if there is one. +/// FIXME: this could actually be done right in patchRangesForUnit, +/// 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 { + if (DIEInteger *Attr = Unit.getUnitRangesAttribute()) { + Attr->setValue(Streamer->getRangesSectionSize()); + Streamer->emitUnitRangesEntries(Unit); + } +} + bool DwarfLinker::link(const DebugMap &Map) { if (Map.begin() == Map.end()) { @@ -1605,11 +1788,15 @@ bool DwarfLinker::link(const DebugMap &Map) { 11 /* Unit Header size */); CurrentUnit.setOutputUnitDIE(OutputDIE); OutputDebugInfoSize = CurrentUnit.computeNextUnitOffset(); + if (!OutputDIE || Options.NoOutput) + continue; + patchRangesForUnit(CurrentUnit, DwarfContext); } // Emit all the compile unit's debug information. if (!ValidRelocs.empty() && !Options.NoOutput) for (auto &CurrentUnit : Units) { + generateUnitRanges(CurrentUnit); CurrentUnit.fixupForwardReferences(); Streamer->emitCompileUnitHeader(CurrentUnit); if (!CurrentUnit.getOutputUnitDIE())