[DWARFLinker][DWARFv5] Add support for .debug_rnglists.
This patch adds support of DWARFv5 .debug_rnglists table.
As DWARFLinker resolves relocations, it is able to always
use DW_FORM_addr instead of DW_FORM_addrx. DW_FORM_addrx
helps to minimize number of relocations, it is also used for
split DWARF. Both of these cases are not relevant for the
DWARFLinker. Thus, this patch converts all DW_FORM_addrx
forms into the DW_FORM_addr. And, as the result, it converts
range lists of DW_FORM_rnglistx form into the DW_FORM_sec_offset.
For the --update case all DW_FORM_addrx, DW_FORM_rnglistx
are preserved as is.
Reviewed By: aprantl
Differential Revision: https://reviews.llvm.org/D143903
GitOrigin-RevId: 1a50207f4ea575da2a3e861369227ce9f1d885e7
diff --git a/lib/DWARFLinker/DWARFLinker.cpp b/lib/DWARFLinker/DWARFLinker.cpp
index abe77ea..7640dfa 100644
--- a/lib/DWARFLinker/DWARFLinker.cpp
+++ b/lib/DWARFLinker/DWARFLinker.cpp
@@ -1203,8 +1203,8 @@
dwarf::Form Form = AttrSpec.Form;
- // FIXME: Generation of DWARFv5 .debug_addr table is not supported yet.
- // Convert attribute into the dwarf::DW_FORM_addr.
+ // DWARFLinker does not use addrx forms since it generates relocated
+ // addresses. Replace DW_FORM_addrx with DW_FORM_addr here.
if (Form == dwarf::DW_FORM_addrx)
Form = dwarf::DW_FORM_addr;
@@ -1257,8 +1257,30 @@
return AttrSize;
}
- if (AttrSpec.Attr == dwarf::DW_AT_high_pc &&
- Die.getTag() == dwarf::DW_TAG_compile_unit) {
+ [[maybe_unused]] dwarf::Form OriginalForm = AttrSpec.Form;
+ if (AttrSpec.Form == dwarf::DW_FORM_rnglistx) {
+ // DWARFLinker does not generate .debug_addr table. Thus we need to change
+ // all "addrx" related forms to "addr" version. Change DW_FORM_rnglistx
+ // to DW_FORM_sec_offset here.
+ std::optional<uint64_t> Index = Val.getAsSectionOffset();
+ if (!Index) {
+ Linker.reportWarning("Cannot read the attribute. Dropping.", File,
+ &InputDIE);
+ return 0;
+ }
+ std::optional<uint64_t> Offset =
+ Unit.getOrigUnit().getRnglistOffset(*Index);
+ if (!Offset) {
+ Linker.reportWarning("Cannot read the attribute. Dropping.", File,
+ &InputDIE);
+ return 0;
+ }
+
+ Value = *Offset;
+ AttrSpec.Form = dwarf::DW_FORM_sec_offset;
+ AttrSize = Unit.getOrigUnit().getFormParams().getDwarfOffsetByteSize();
+ } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc &&
+ Die.getTag() == dwarf::DW_TAG_compile_unit) {
std::optional<uint64_t> LowPC = Unit.getLowPc();
if (!LowPC)
return 0;
@@ -1279,11 +1301,11 @@
PatchLocation Patch =
Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr),
dwarf::Form(AttrSpec.Form), DIEInteger(Value));
- if (AttrSpec.Attr == dwarf::DW_AT_ranges) {
+ if (AttrSpec.Attr == dwarf::DW_AT_ranges ||
+ AttrSpec.Attr == dwarf::DW_AT_start_scope) {
Unit.noteRangeAttribute(Die, Patch);
Info.HasRanges = true;
}
-
// 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.
@@ -1294,6 +1316,10 @@
} else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value)
Info.IsDeclaration = true;
+ // check that all dwarf::DW_FORM_rnglistx are handled previously.
+ assert((Info.HasRanges || (OriginalForm != dwarf::DW_FORM_rnglistx)) &&
+ "Unhandled DW_FORM_rnglistx attribute");
+
return AttrSize;
}
@@ -1343,6 +1369,7 @@
case dwarf::DW_FORM_sec_offset:
case dwarf::DW_FORM_flag:
case dwarf::DW_FORM_flag_present:
+ case dwarf::DW_FORM_rnglistx:
return cloneScalarAttribute(Die, InputDIE, File, Unit, AttrSpec, Val,
AttrSize, Info);
default:
@@ -1412,6 +1439,15 @@
case dwarf::DW_AT_high_pc:
case dwarf::DW_AT_ranges:
return !Update && SkipPC;
+ case dwarf::DW_AT_addr_base:
+ // In case !Update the .debug_addr table is not generated/preserved.
+ return !Update;
+ case dwarf::DW_AT_rnglists_base:
+ // In case !Update the .debug_addr table is not generated/preserved.
+ // Thus instead of DW_FORM_rnglistx the DW_FORM_sec_offset is used.
+ // Since DW_AT_rnglists_base is used for only DW_FORM_rnglistx the
+ // DW_AT_rnglists_base is removed.
+ return !Update;
case dwarf::DW_AT_str_offsets_base:
// FIXME: Use the string offset table with Dwarf 5.
return true;
@@ -1612,77 +1648,75 @@
return Die;
}
-/// Patch the input object file relevant debug_ranges entries
-/// and emit them in the output file. Update the relevant attributes
+/// Patch the input object file relevant debug_ranges or debug_rnglists
+/// 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,
+void DWARFLinker::generateUnitRanges(CompileUnit &Unit,
const DWARFFile &File) const {
- DWARFDebugRangeList RangeList;
+ if (LLVM_UNLIKELY(Options.Update))
+ return;
+
const auto &FunctionRanges = Unit.getFunctionRanges();
- unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize();
- DWARFDataExtractor RangeExtractor(OrigDwarf.getDWARFObj(),
- OrigDwarf.getDWARFObj().getRangesSection(),
- OrigDwarf.isLittleEndian(), AddressSize);
- std::optional<AddressRangeValuePair> CachedRange;
- DWARFUnit &OrigUnit = Unit.getOrigUnit();
- auto OrigUnitDie = OrigUnit.getUnitDIE(false);
- uint64_t UnitBaseAddress =
- dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), 0);
- for (const auto &RangeAttribute : Unit.getRangesAttributes()) {
- uint64_t Offset = RangeAttribute.get();
- RangeAttribute.set(TheDwarfEmitter->getRangesSectionSize());
- if (Error E = RangeList.extract(RangeExtractor, &Offset)) {
- llvm::consumeError(std::move(E));
- reportWarning("invalid range list ignored.", File);
- RangeList.clear();
- }
- const auto &Entries = RangeList.getEntries();
+ // Build set of linked address ranges for unit function ranges.
+ AddressRanges LinkedFunctionRanges;
+ for (const AddressRangeValuePair &Range : FunctionRanges)
+ LinkedFunctionRanges.insert(
+ {Range.Range.start() + Range.Value, Range.Range.end() + Range.Value});
- uint64_t BaseAddress = UnitBaseAddress;
- AddressRanges LinkedRanges;
+ // Emit LinkedFunctionRanges into .debug_aranges
+ if (!LinkedFunctionRanges.empty())
+ TheDwarfEmitter->emitDwarfDebugArangesTable(Unit, LinkedFunctionRanges);
- if (!Entries.empty()) {
- for (const auto &Range : Entries) {
- if (Range.isBaseAddressSelectionEntry(
- Unit.getOrigUnit().getAddressByteSize())) {
- BaseAddress = Range.EndAddress;
- continue;
+ RngListAttributesTy AllRngListAttributes = Unit.getRangesAttributes();
+ std::optional<PatchLocation> UnitRngListAttribute =
+ Unit.getUnitRangesAttribute();
+
+ if (!AllRngListAttributes.empty() || UnitRngListAttribute) {
+ std::optional<AddressRangeValuePair> CachedRange;
+ MCSymbol *EndLabel = TheDwarfEmitter->emitDwarfDebugRangeListHeader(Unit);
+
+ // Read original address ranges, apply relocation value, emit linked address
+ // ranges.
+ for (PatchLocation &AttributePatch : AllRngListAttributes) {
+ // Get ranges from the source DWARF corresponding to the current
+ // attribute.
+ AddressRanges LinkedRanges;
+ if (Expected<DWARFAddressRangesVector> OriginalRanges =
+ Unit.getOrigUnit().findRnglistFromOffset(AttributePatch.get())) {
+ // Apply relocation adjustment.
+ for (const auto &Range : *OriginalRanges) {
+ if (!CachedRange || !CachedRange->Range.contains(Range.LowPC))
+ CachedRange = FunctionRanges.getRangeThatContains(Range.LowPC);
+
+ // All range entries should lie in the function range.
+ if (!CachedRange) {
+ reportWarning("inconsistent range data.", File);
+ continue;
+ }
+
+ // Store range for emiting.
+ LinkedRanges.insert({Range.LowPC + CachedRange->Value,
+ Range.HighPC + CachedRange->Value});
}
-
- if (!CachedRange ||
- !CachedRange->Range.contains(Range.StartAddress + BaseAddress))
- CachedRange = FunctionRanges.getRangeThatContains(Range.StartAddress +
- BaseAddress);
-
- // All range entries should lie in the function range.
- if (!CachedRange) {
- reportWarning("inconsistent range data.", File);
- continue;
- }
-
- LinkedRanges.insert(
- {Range.StartAddress + BaseAddress + CachedRange->Value,
- Range.EndAddress + BaseAddress + CachedRange->Value});
+ } else {
+ llvm::consumeError(OriginalRanges.takeError());
+ reportWarning("invalid range list ignored.", File);
}
+
+ // Emit linked ranges.
+ TheDwarfEmitter->emitDwarfDebugRangeListFragment(Unit, LinkedRanges,
+ AttributePatch);
}
- TheDwarfEmitter->emitDwarfDebugRangesTableFragment(Unit, LinkedRanges);
- }
-}
+ // Emit ranges for Unit AT_ranges attribute.
+ if (UnitRngListAttribute.has_value())
+ TheDwarfEmitter->emitDwarfDebugRangeListFragment(
+ Unit, LinkedFunctionRanges, *UnitRngListAttribute);
-/// Generate the debug_aranges entries for \p Unit and if the
-/// unit has a DW_AT_ranges attribute, also emit the debug_ranges
-/// contribution for this attribute.
-/// 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 {
- auto Attr = Unit.getUnitRangesAttribute();
- if (Attr)
- Attr->set(TheDwarfEmitter->getRangesSectionSize());
- TheDwarfEmitter->emitUnitRangesEntries(Unit, static_cast<bool>(Attr));
+ // Emit ranges footer.
+ TheDwarfEmitter->emitDwarfDebugRangeListFooter(Unit, EndLabel);
+ }
}
/// Insert the new line info sequence \p Seq into the current
@@ -2265,7 +2299,8 @@
if (LLVM_UNLIKELY(Linker.Options.Update))
continue;
- Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, File);
+ Linker.generateUnitRanges(*CurrentUnit, File);
+
auto ProcessExpr = [&](StringRef Bytes,
SmallVectorImpl<uint8_t> &Buffer) {
DWARFUnit &OrigUnit = CurrentUnit->getOrigUnit();
@@ -2287,9 +2322,6 @@
// Emit all the compile unit's debug information.
for (auto &CurrentUnit : CompileUnits) {
- if (LLVM_LIKELY(!Linker.Options.Update))
- Linker.generateUnitRanges(*CurrentUnit);
-
CurrentUnit->fixupForwardReferences();
if (!CurrentUnit->getOutputUnitDIE())
@@ -2384,6 +2416,10 @@
Dwarf.getDWARFObj().getFrameSection().Data, "debug_frame");
TheDwarfEmitter->emitSectionContents(Dwarf.getDWARFObj().getArangesSection(),
"debug_aranges");
+ TheDwarfEmitter->emitSectionContents(
+ Dwarf.getDWARFObj().getAddrSection().Data, "debug_addr");
+ TheDwarfEmitter->emitSectionContents(
+ Dwarf.getDWARFObj().getRnglistsSection().Data, "debug_rnglists");
}
void DWARFLinker::addObjectFile(DWARFFile &File, objFileLoader Loader,
@@ -2474,16 +2510,6 @@
// support or update references to these tables. Thus we report warning
// and skip corresponding object file.
if (!OptContext.File.Dwarf->getDWARFObj()
- .getRnglistsSection()
- .Data.empty()) {
- reportWarning("'.debug_rnglists' is not currently supported: file "
- "will be skipped",
- OptContext.File);
- OptContext.Skip = true;
- continue;
- }
-
- if (!OptContext.File.Dwarf->getDWARFObj()
.getLoclistsSection()
.Data.empty()) {
reportWarning("'.debug_loclists' is not currently supported: file "
diff --git a/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp b/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
index 6c9e052..870a841 100644
--- a/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
+++ b/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
@@ -135,10 +135,12 @@
}
void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) {
- if (Die.getTag() != dwarf::DW_TAG_compile_unit)
- RangeAttributes.push_back(Attr);
- else
+ if (Die.getTag() == dwarf::DW_TAG_compile_unit) {
UnitRangeAttribute = Attr;
+ return;
+ }
+
+ RangeAttributes.emplace_back(Attr);
}
void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) {
diff --git a/lib/DWARFLinker/DWARFStreamer.cpp b/lib/DWARFLinker/DWARFStreamer.cpp
index a34c407..3af7cde 100644
--- a/lib/DWARFLinker/DWARFStreamer.cpp
+++ b/lib/DWARFLinker/DWARFStreamer.cpp
@@ -108,6 +108,7 @@
Asm->setDwarfUsesRelocationsAcrossSections(false);
RangesSectionSize = 0;
+ RngListsSectionSize = 0;
LocSectionSize = 0;
LineSectionSize = 0;
FrameSectionSize = 0;
@@ -202,6 +203,9 @@
.Case("debug_frame", MC->getObjectFileInfo()->getDwarfFrameSection())
.Case("debug_aranges",
MC->getObjectFileInfo()->getDwarfARangesSection())
+ .Case("debug_addr", MC->getObjectFileInfo()->getDwarfAddrSection())
+ .Case("debug_rnglists",
+ MC->getObjectFileInfo()->getDwarfRnglistsSection())
.Default(nullptr);
if (Section) {
@@ -363,11 +367,13 @@
}
void DwarfStreamer::emitDwarfDebugRangesTableFragment(
- const CompileUnit &Unit, const AddressRanges &LinkedRanges) {
- unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize();
+ const CompileUnit &Unit, const AddressRanges &LinkedRanges,
+ PatchLocation Patch) {
+ Patch.set(RangesSectionSize);
// Make .debug_ranges to be current section.
MS->switchSection(MC->getObjectFileInfo()->getDwarfRangesSection());
+ unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize();
// Emit ranges.
uint64_t BaseAddress = 0;
@@ -390,27 +396,91 @@
RangesSectionSize += AddressSize;
}
-/// Emit the debug_aranges contribution of a unit and
-/// if \p DoDebugRanges is true the debug_range contents for a
-/// compile_unit level DW_AT_ranges attribute (Which are basically the
-/// same thing with a different base address).
-/// Just aggregate all the ranges gathered inside that unit.
-void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit,
- bool DoDebugRanges) {
- const RangesTy &FunctionRanges = Unit.getFunctionRanges();
+MCSymbol *
+DwarfStreamer::emitDwarfDebugRangeListHeader(const CompileUnit &Unit) {
+ if (Unit.getOrigUnit().getVersion() < 5)
+ return nullptr;
- // Linked addresses might end up in a different order.
- // Build linked address ranges.
- AddressRanges LinkedRanges;
- for (const AddressRangeValuePair &Range : FunctionRanges)
- LinkedRanges.insert(
- {Range.Range.start() + Range.Value, Range.Range.end() + Range.Value});
+ // Make .debug_rnglists to be current section.
+ MS->switchSection(MC->getObjectFileInfo()->getDwarfRnglistsSection());
- if (!FunctionRanges.empty())
- emitDwarfDebugArangesTable(Unit, LinkedRanges);
+ MCSymbol *BeginLabel = Asm->createTempSymbol("Brnglists");
+ MCSymbol *EndLabel = Asm->createTempSymbol("Ernglists");
+ unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize();
- if (DoDebugRanges)
- emitDwarfDebugRangesTableFragment(Unit, LinkedRanges);
+ // Length
+ Asm->emitLabelDifference(EndLabel, BeginLabel, sizeof(uint32_t));
+ Asm->OutStreamer->emitLabel(BeginLabel);
+ RngListsSectionSize += sizeof(uint32_t);
+
+ // Version.
+ MS->emitInt16(5);
+ RngListsSectionSize += sizeof(uint16_t);
+
+ // Address size.
+ MS->emitInt8(AddressSize);
+ RngListsSectionSize++;
+
+ // Seg_size
+ MS->emitInt8(0);
+ RngListsSectionSize++;
+
+ // Offset entry count
+ MS->emitInt32(0);
+ RngListsSectionSize += sizeof(uint32_t);
+
+ return EndLabel;
+}
+
+void DwarfStreamer::emitDwarfDebugRangeListFragment(
+ const CompileUnit &Unit, const AddressRanges &LinkedRanges,
+ PatchLocation Patch) {
+ if (Unit.getOrigUnit().getVersion() < 5) {
+ emitDwarfDebugRangesTableFragment(Unit, LinkedRanges, Patch);
+ return;
+ }
+
+ emitDwarfDebugRngListsTableFragment(Unit, LinkedRanges, Patch);
+}
+
+void DwarfStreamer::emitDwarfDebugRangeListFooter(const CompileUnit &Unit,
+ MCSymbol *EndLabel) {
+ if (Unit.getOrigUnit().getVersion() < 5)
+ return;
+
+ // Make .debug_rnglists to be current section.
+ MS->switchSection(MC->getObjectFileInfo()->getDwarfRnglistsSection());
+
+ if (EndLabel != nullptr)
+ Asm->OutStreamer->emitLabel(EndLabel);
+}
+
+void DwarfStreamer::emitDwarfDebugRngListsTableFragment(
+ const CompileUnit &Unit, const AddressRanges &LinkedRanges,
+ PatchLocation Patch) {
+ Patch.set(RngListsSectionSize);
+
+ // Make .debug_rnglists to be current section.
+ MS->switchSection(MC->getObjectFileInfo()->getDwarfRnglistsSection());
+
+ unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize();
+
+ for (const AddressRange &Range : LinkedRanges) {
+ // Emit type of entry.
+ MS->emitInt8(dwarf::DW_RLE_start_length);
+ RngListsSectionSize += 1;
+
+ // Emit start address.
+ MS->emitIntValue(Range.start(), AddressSize);
+ RngListsSectionSize += AddressSize;
+
+ // Emit length of the range.
+ RngListsSectionSize += MS->emitSLEB128IntValue(Range.end() - Range.start());
+ }
+
+ // Emit the terminator entry.
+ MS->emitInt8(dwarf::DW_RLE_end_of_list);
+ RngListsSectionSize += 1;
}
/// Emit location lists for \p Unit and update attributes to point to the new