[llvm] r232232 - [dsymutil] Implement DW_AT_ranges linking.
Frederic Riss
friss at apple.com
Fri Mar 13 16:30:31 PDT 2015
Author: friss
Date: Fri Mar 13 18:30:31 2015
New Revision: 232232
URL: http://llvm.org/viewvc/llvm-project?rev=232232&view=rev
Log:
[dsymutil] Implement DW_AT_ranges linking.
Nothing fancy, just a straightforward offset to apply to the original
debug_ranges entries to get them in line with the linked addresses.
Modified:
llvm/trunk/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test
llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test
llvm/trunk/tools/dsymutil/DwarfLinker.cpp
Modified: llvm/trunk/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test?rev=232232&r1=232231&r2=232232&view=diff
==============================================================================
--- llvm/trunk/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test (original)
+++ llvm/trunk/test/tools/dsymutil/X86/basic-lto-dw4-linking-x86.test Fri Mar 13 18:30:31 2015
@@ -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
Modified: llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test?rev=232232&r1=232231&r2=232232&view=diff
==============================================================================
--- llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test (original)
+++ llvm/trunk/test/tools/dsymutil/X86/basic-lto-linking-x86.test Fri Mar 13 18:30:31 2015
@@ -110,7 +110,9 @@ CHECK: DW_AT_low_pc [DW_FORM_addr
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]
Modified: llvm/trunk/tools/dsymutil/DwarfLinker.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/DwarfLinker.cpp?rev=232232&r1=232231&r2=232232&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/DwarfLinker.cpp (original)
+++ llvm/trunk/tools/dsymutil/DwarfLinker.cpp Fri Mar 13 18:30:31 2015
@@ -56,6 +56,8 @@ using HalfOpenIntervalMap =
IntervalMap<KeyT, ValT, IntervalMapImpl::NodeSizer<KeyT, ValT>::LeafSize,
IntervalMapHalfOpenInfo<KeyT>>;
+typedef HalfOpenIntervalMap<uint64_t, int64_t> 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<DIEInteger *> &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<DIEInfo> Info; ///< DIE info indexed by DIE index.
@@ -140,11 +151,18 @@ private:
std::vector<std::tuple<DIE *, const CompileUnit *, DIEInteger *>>
ForwardDIEReferences;
- HalfOpenIntervalMap<uint64_t, int64_t>::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<uint64_t, int64_t> Ranges;
+ FunctionIntervals Ranges;
+
+ /// \brief DW_AT_ranges attributes to patch after we have gathered
+ /// all the unit's function addresses.
+ /// @{
+ std::vector<DIEInteger *> RangeAttributes;
+ DIEInteger *UnitRangeAttribute;
+ /// @}
};
uint64_t CompileUnit::computeNextUnitOffset() {
@@ -182,6 +200,13 @@ void CompileUnit::addFunctionRange(uint6
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<raw_fd_ostream> 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<DWARFDebugRangeList::RangeListEntry> &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 TheTripl
if (!Asm)
return error("no asm printer for target " + TripleName, Context);
+ RangesSectionSize = 0;
+
return true;
}
@@ -449,6 +491,82 @@ void DwarfStreamer::emitStrings(const No
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<DWARFDebugRangeList::RangeListEntry> &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<std::pair<uint64_t, uint64_t>> 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<DIEAbbrev *> 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<DIELoc *> 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 *DwarfL
/// \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 = "<debug map>";
if (CurrentDebugObject)
Context = CurrentDebugObject->getObjectFilename();
@@ -1309,9 +1435,8 @@ unsigned DwarfLinker::cloneAddressAttrib
/// \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::cloneScalarAttribu
&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 DWARFDe
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 &M
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())
More information about the llvm-commits
mailing list