mirror of
https://github.com/RPCS3/llvm.git
synced 2024-12-17 08:57:34 +00:00
[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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@232232 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
f13be986fb
commit
e031501c91
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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(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<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 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<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 *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 = "<debug map>";
|
||||
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())
|
||||
|
Loading…
Reference in New Issue
Block a user