diff options
| -rw-r--r-- | llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRnglists.h | 30 | ||||
| -rw-r--r-- | llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h | 1 | ||||
| -rw-r--r-- | llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h | 22 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFContext.cpp | 53 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp | 74 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFDie.cpp | 21 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp | 5 | ||||
| -rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp | 91 | ||||
| -rw-r--r-- | llvm/test/DebugInfo/X86/dwarfdump-rnglists.s | 192 |
9 files changed, 459 insertions, 30 deletions
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRnglists.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRnglists.h index 748e6648597..49bea290e43 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRnglists.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugRnglists.h @@ -10,6 +10,7 @@ #ifndef LLVM_DEBUGINFO_DWARFDEBUGRNGLISTS_H #define LLVM_DEBUGINFO_DWARFDEBUGRNGLISTS_H +#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" @@ -30,6 +31,8 @@ public: uint32_t Offset; /// The DWARF encoding (DW_RLE_*). uint8_t EntryKind; + /// The index of the section this range belongs to. + uint64_t SectionIndex; /// The values making up the range list entry. Most represent a range with /// a start and end address or a start address and a length. Others are /// single value base addresses or end-of-list with no values. The unneeded @@ -51,6 +54,9 @@ public: void clear() { Entries.clear(); } Error extract(DWARFDataExtractor Data, uint32_t HeaderOffset, uint32_t End, uint32_t *OffsetPtr); + /// Build a DWARFAddressRangesVector from a rangelist. + DWARFAddressRangesVector + getAbsoluteRanges(llvm::Optional<BaseAddress> BaseAddr) const; }; /// A class representing a table of range lists as specified in DWARF v5. @@ -77,6 +83,7 @@ public: }; private: + dwarf::DwarfFormat Format; uint32_t HeaderOffset; Header HeaderData; std::vector<uint32_t> Offsets; @@ -88,8 +95,31 @@ public: Error extractHeaderAndOffsets(DWARFDataExtractor Data, uint32_t *OffsetPtr); /// Extract an entire table, including all rangelists. Error extract(DWARFDataExtractor Data, uint32_t *OffsetPtr); + /// Look up a rangelist based on a given offset. Extract it and enter it into + /// the ranges map if necessary. + Optional<DWARFDebugRnglist> findRangeList(DWARFDataExtractor Data, + uint32_t Offset); uint32_t getHeaderOffset() const { return HeaderOffset; } + uint8_t getAddrSize() const { return HeaderData.AddrSize; } void dump(raw_ostream &OS, DIDumpOptions DumpOpts) const; + /// Return the contents of the offset entry designated by a given index. + Optional<uint32_t> getOffsetEntry(uint32_t Index) const { + if (Index < Offsets.size()) + return Offsets[Index]; + return None; + } + /// Return the size of the table header including the length but not including + /// the offsets. This is dependent on the table format, which is unambiguously + /// derived from parsing the table. + uint8_t getHeaderSize() const { + switch (Format) { + case dwarf::DwarfFormat::DWARF32: + return 12; + case dwarf::DwarfFormat::DWARF64: + return 20; + } + llvm_unreachable("Invalid DWARF format (expected DWARF32 or DWARF64"); + } /// Returns the length of this table, including the length field, or 0 if the /// length has not been determined (e.g. because the table has not yet been diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h index 795eddd1c5d..6e8f370f4ae 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h @@ -63,6 +63,7 @@ public: return Dummy; } virtual const DWARFSection &getRangeDWOSection() const { return Dummy; } + virtual const DWARFSection &getRnglistsDWOSection() const { return Dummy; } virtual const DWARFSection &getAddrSection() const { return Dummy; } virtual const DWARFSection &getAppleNamesSection() const { return Dummy; } virtual const DWARFSection &getAppleTypesSection() const { return Dummy; } diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h index c1a94662ed4..ec79ca08c0d 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -18,6 +18,7 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" @@ -261,6 +262,9 @@ class DWARFUnit { /// offsets table (DWARF v5). Optional<StrOffsetsContributionDescriptor> StringOffsetsTableContribution; + /// A table of range lists (DWARF v5 and later). + Optional<DWARFDebugRnglistTable> RngListTable; + mutable const DWARFAbbreviationDeclarationSet *Abbrevs; llvm::Optional<BaseAddress> BaseAddr; /// The compile unit debug information entry items. @@ -430,6 +434,24 @@ public: const char *getCompilationDir(); Optional<uint64_t> getDWOId(); + /// Return a vector of address ranges resulting from a (possibly encoded) + /// range list starting at a given offset in the appropriate ranges section. + DWARFAddressRangesVector findRnglistFromOffset(uint32_t Offset); + + /// Return a vector of address ranges retrieved from an encoded range + /// list whose offset is found via a table lookup given an index (DWARF v5 + /// and later). + DWARFAddressRangesVector findRnglistFromIndex(uint32_t Index); + + /// Return a rangelist's offset based on an index. The index designates + /// an entry in the rangelist table's offset array and is supplied by + /// DW_FORM_rnglistx. + Optional<uint32_t> getRnglistOffset(uint32_t Index) { + if (RngListTable) + return RngListTable->getOffsetEntry(Index); + return None; + } + void collectAddressRanges(DWARFAddressRangesVector &CURanges); /// Returns subprogram DIE with address range encompassing the provided diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index 8488fe8ced7..08c77a01f6f 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -248,6 +248,28 @@ static void dumpStringOffsetsSection( } } +// Dump the .debug_rnglists or .debug_rnglists.dwo section (DWARF v5). +static void dumpRnglistsSection(raw_ostream &OS, + DWARFDataExtractor &rnglistData, + DIDumpOptions DumpOpts) { + uint32_t Offset = 0; + while (rnglistData.isValidOffset(Offset)) { + llvm::DWARFDebugRnglistTable Rnglists; + uint32_t TableOffset = Offset; + if (Error Err = Rnglists.extract(rnglistData, &Offset)) { + WithColor::error() << toString(std::move(Err)) << '\n'; + uint64_t Length = Rnglists.length(); + // Keep going after an error, if we can, assuming that the length field + // could be read. If it couldn't, stop reading the section. + if (Length == 0) + break; + Offset = TableOffset + Length; + } else { + Rnglists.dump(OS, DumpOpts); + } + } +} + void DWARFContext::dump( raw_ostream &OS, DIDumpOptions DumpOpts, std::array<Optional<uint64_t>, DIDT_ID_Count> DumpOffsets) { @@ -455,24 +477,16 @@ void DWARFContext::dump( if (shouldDump(Explicit, ".debug_rnglists", DIDT_ID_DebugRnglists, DObj->getRnglistsSection().Data)) { - DWARFDataExtractor rnglistData(*DObj, DObj->getRnglistsSection(), + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsSection(), isLittleEndian(), 0); - uint32_t Offset = 0; - while (rnglistData.isValidOffset(Offset)) { - DWARFDebugRnglistTable Rnglists; - uint32_t TableOffset = Offset; - if (Error Err = Rnglists.extract(rnglistData, &Offset)) { - WithColor::error() << toString(std::move(Err)) << '\n'; - uint64_t Length = Rnglists.length(); - // Keep going after an error, if we can, assuming that the length field - // could be read. If it couldn't, stop reading the section. - if (Length == 0) - break; - Offset = TableOffset + Length; - } else { - Rnglists.dump(OS, DumpOpts); - } - } + dumpRnglistsSection(OS, RnglistData, DumpOpts); + } + + if (shouldDump(ExplicitDWO, ".debug_rnglists.dwo", DIDT_ID_DebugRnglists, + DObj->getRnglistsDWOSection().Data)) { + DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsDWOSection(), + isLittleEndian(), 0); + dumpRnglistsSection(OS, RnglistData, DumpOpts); } if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, @@ -1173,6 +1187,7 @@ class DWARFObjInMemory final : public DWARFObject { DWARFSectionMap LocDWOSection; DWARFSectionMap StringOffsetDWOSection; DWARFSectionMap RangeDWOSection; + DWARFSectionMap RnglistsDWOSection; DWARFSectionMap AddrSection; DWARFSectionMap AppleNamesSection; DWARFSectionMap AppleTypesSection; @@ -1192,6 +1207,7 @@ class DWARFObjInMemory final : public DWARFObject { .Case("debug_loc.dwo", &LocDWOSection) .Case("debug_line.dwo", &LineDWOSection) .Case("debug_names", &DebugNamesSection) + .Case("debug_rnglists.dwo", &RnglistsDWOSection) .Case("debug_str_offsets.dwo", &StringOffsetDWOSection) .Case("debug_addr", &AddrSection) .Case("apple_names", &AppleNamesSection) @@ -1450,6 +1466,9 @@ public: const DWARFSection &getRangeDWOSection() const override { return RangeDWOSection; } + const DWARFSection &getRnglistsDWOSection() const override { + return RnglistsDWOSection; + } const DWARFSection &getAddrSection() const override { return AddrSection; } StringRef getCUIndexSection() const override { return CUIndexSection; } StringRef getGdbIndexSection() const override { return GdbIndexSection; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp index 3a273f802e2..0c2bb324d74 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" - #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" @@ -44,6 +44,7 @@ Error DWARFDebugRnglistTable::extractHeaderAndOffsets(DWARFDataExtractor Data, return createError( "DWARF64 is not supported in .debug_rnglists at offset 0x%" PRIx32, HeaderOffset); + Format = dwarf::DwarfFormat::DWARF32; if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) return createError(".debug_rnglists table at offset 0x%" PRIx32 " has too small length (0x%" PRIx32 @@ -90,6 +91,7 @@ Error DWARFDebugRnglist::RangeListEntry::extract(DWARFDataExtractor Data, uint32_t End, uint32_t *OffsetPtr) { Offset = *OffsetPtr; + SectionIndex = -1ULL; // The caller should guarantee that we have at least 1 byte available, so // we just assert instead of revalidate. assert(*OffsetPtr < End && @@ -128,7 +130,7 @@ Error DWARFDebugRnglist::RangeListEntry::extract(DWARFDataExtractor Data, return createError("insufficient space remaining in table for " "DW_RLE_base_address encoding at offset 0x%" PRIx32, *OffsetPtr - 1); - Value0 = Data.getAddress(OffsetPtr); + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); break; } case dwarf::DW_RLE_start_end: { @@ -137,13 +139,13 @@ Error DWARFDebugRnglist::RangeListEntry::extract(DWARFDataExtractor Data, "DW_RLE_start_end encoding " "at offset 0x%" PRIx32, *OffsetPtr - 1); - Value0 = Data.getAddress(OffsetPtr); - Value1 = Data.getAddress(OffsetPtr); + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); + Value1 = Data.getRelocatedAddress(OffsetPtr); break; } case dwarf::DW_RLE_start_length: { uint32_t PreviousOffset = *OffsetPtr - 1; - Value0 = Data.getAddress(OffsetPtr); + Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); Value1 = Data.getULEB128(OffsetPtr); if (End < *OffsetPtr) return createError("read past end of table when reading " @@ -161,6 +163,49 @@ Error DWARFDebugRnglist::RangeListEntry::extract(DWARFDataExtractor Data, return Error::success(); } +DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges( + llvm::Optional<BaseAddress> BaseAddr) const { + DWARFAddressRangesVector Res; + for (const RangeListEntry &RLE : Entries) { + if (RLE.EntryKind == dwarf::DW_RLE_end_of_list) + break; + if (RLE.EntryKind == dwarf::DW_RLE_base_address) { + BaseAddr = {RLE.Value0, RLE.SectionIndex}; + continue; + } + + DWARFAddressRange E; + E.SectionIndex = RLE.SectionIndex; + if (BaseAddr && E.SectionIndex == -1ULL) + E.SectionIndex = BaseAddr->SectionIndex; + + switch (RLE.EntryKind) { + case dwarf::DW_RLE_offset_pair: + E.LowPC = RLE.Value0; + E.HighPC = RLE.Value1; + if (BaseAddr) { + E.LowPC += BaseAddr->Address; + E.HighPC += BaseAddr->Address; + } + break; + case dwarf::DW_RLE_start_end: + E.LowPC = RLE.Value0; + E.HighPC = RLE.Value1; + break; + case dwarf::DW_RLE_start_length: + E.LowPC = RLE.Value0; + E.HighPC = E.LowPC + RLE.Value1; + break; + default: + // Unsupported encodings should have been reported during extraction, + // so we should not run into any here. + llvm_unreachable("Unsupported range list encoding"); + } + Res.push_back(E); + } + return Res; +} + Error DWARFDebugRnglist::extract(DWARFDataExtractor Data, uint32_t HeaderOffset, uint32_t End, uint32_t *OffsetPtr) { Entries.clear(); @@ -305,3 +350,22 @@ uint32_t DWARFDebugRnglistTable::length() const { // TODO: DWARF64 support. return HeaderData.Length + sizeof(uint32_t); } + +Optional<DWARFDebugRnglist> +DWARFDebugRnglistTable::findRangeList(DWARFDataExtractor Data, + uint32_t Offset) { + auto Entry = Ranges.find(Offset); + if (Entry != Ranges.end()) + return Entry->second; + + // Extract the rangelist from the section and enter it into the ranges map. + DWARFDebugRnglist RngList; + uint32_t End = HeaderOffset + length(); + uint32_t StartingOffset = Offset; + if (Error E = RngList.extract(Data, HeaderOffset, End, &Offset)) { + llvm::consumeError(std::move(E)); + return None; + } + Ranges[StartingOffset] = RngList; + return RngList; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index 0d045c5d4bc..33cb00dec9e 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -257,6 +257,16 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, dumpApplePropertyAttribute(OS, *OptVal); } else if (Attr == DW_AT_ranges) { const DWARFObject &Obj = Die.getDwarfUnit()->getContext().getDWARFObj(); + // For DW_FORM_rnglistx we need to dump the offset separately, since + // we have only dumped the index so far. + Optional<DWARFFormValue> Value = Die.find(DW_AT_ranges); + if (Value && Value->getForm() == DW_FORM_rnglistx) + if (auto RangeListOffset = + U->getRnglistOffset(*Value->getAsSectionOffset())) { + DWARFFormValue FV(dwarf::DW_FORM_sec_offset); + FV.setUValue(*RangeListOffset); + FV.dump(OS, DumpOpts); + } dumpRanges(Obj, OS, Die.getAddressRanges(), U->getAddressByteSize(), sizeof(BaseIndent) + Indent + 4, DumpOpts); } @@ -380,12 +390,11 @@ DWARFAddressRangesVector DWARFDie::getAddressRanges() const { if (getLowAndHighPC(LowPC, HighPC, Index)) return {{LowPC, HighPC, Index}}; - // Multiple ranges from .debug_ranges section. - auto RangesOffset = toSectionOffset(find(DW_AT_ranges)); - if (RangesOffset) { - DWARFDebugRangeList RangeList; - if (U->extractRangeList(*RangesOffset, RangeList)) - return RangeList.getAbsoluteRanges(U->getBaseAddress()); + Optional<DWARFFormValue> Value = find(DW_AT_ranges); + if (Value) { + if (Value->getForm() == DW_FORM_rnglistx) + return U->findRnglistFromIndex(*Value->getAsSectionOffset()); + return U->findRnglistFromOffset(*Value->getAsSectionOffset()); } return DWARFAddressRangesVector(); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp index b3ecf8e2dab..1aa43c6b651 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -280,6 +280,7 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, break; case DW_FORM_udata: case DW_FORM_ref_udata: + case DW_FORM_rnglistx: Value.uval = Data.getULEB128(OffsetPtr); break; case DW_FORM_string: @@ -483,6 +484,10 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << "DW_FORM_indirect"; break; + case DW_FORM_rnglistx: + OS << format("indexed (0x%x) rangelist = ", (uint32_t)UValue); + break; + // Should be formatted to 64-bit for DWARF64. case DW_FORM_sec_offset: AddrOS << format("0x%08x", (uint32_t)UValue); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index 6fa604c6c8e..11cec59565b 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/Support/DataExtractor.h" @@ -150,6 +151,29 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, return true; } +// Parse the rangelist table header, including the optional array of offsets +// following it (DWARF v5 and later). +static Expected<DWARFDebugRnglistTable> +parseRngListTableHeader(DWARFDataExtractor &DA, uint32_t Offset) { + // TODO: Support DWARF64 + // We are expected to be called with Offset 0 or pointing just past the table + // header, which is 12 bytes long for DWARF32. + if (Offset > 0) { + if (Offset < 12U) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format( + "Did not detect a valid range list table with base = 0x%x", Offset); + return make_error<StringError>(Stream.str(), inconvertibleErrorCode()); + } + Offset -= 12U; + } + llvm::DWARFDebugRnglistTable Table; + if (Error E = Table.extractHeaderAndOffsets(DA, &Offset)) + return E; + return Table; +} + bool DWARFUnit::extractRangeList(uint32_t RangeListOffset, DWARFDebugRangeList &RangeList) const { // Require that compile unit is extracted. @@ -281,6 +305,32 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { StringOffsetsTableContribution = determineStringOffsetsTableContribution( DA, StringOffsetsContributionBase); + // DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to + // describe address ranges. + if (getVersion() >= 5) { + if (isDWO) + setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); + else + setRangesSection(&Context.getDWARFObj().getRnglistsSection(), + toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0)); + // Parse the range list table header. Individual range lists are + // extracted lazily. + DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, + isLittleEndian, 0); + if (auto TableOrError = + parseRngListTableHeader(RangesDA, RangeSectionBase)) + RngListTable = TableOrError.get(); + else + WithColor::error() << "parsing a range list table: " + << toString(std::move(TableOrError.takeError())) + << '\n'; + + // In a split dwarf unit, there is no DW_AT_rnglists_base attribute. + // Adjust RangeSectionBase to point past the table header. + if (isDWO && RngListTable) + RangeSectionBase = RngListTable->getHeaderSize(); + } + // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for // skeleton CU DIE, so that DWARF users not aware of it are not broken. } @@ -319,8 +369,23 @@ bool DWARFUnit::parseDWO() { DWO = std::shared_ptr<DWARFCompileUnit>(std::move(DWOContext), DWOCU); // Share .debug_addr and .debug_ranges section with compile unit in .dwo DWO->setAddrOffsetSection(AddrOffsetSection, AddrOffsetSectionBase); - auto DWORangesBase = UnitDie.getRangesBaseAttribute(); - DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); + if (getVersion() >= 5) { + DWO->setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); + DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, + isLittleEndian, 0); + if (auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase)) + DWO->RngListTable = TableOrError.get(); + else + WithColor::error() << "parsing a range list table: " + << toString(std::move(TableOrError.takeError())) + << '\n'; + if (DWO->RngListTable) + DWO->RangeSectionBase = DWO->RngListTable->getHeaderSize(); + } else { + auto DWORangesBase = UnitDie.getRangesBaseAttribute(); + DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); + } + return true; } @@ -331,6 +396,28 @@ void DWARFUnit::clearDIEs(bool KeepCUDie) { } } +DWARFAddressRangesVector DWARFUnit::findRnglistFromOffset(uint32_t Offset) { + if (getVersion() <= 4) { + DWARFDebugRangeList RangeList; + if (extractRangeList(Offset, RangeList)) + return RangeList.getAbsoluteRanges(getBaseAddress()); + return DWARFAddressRangesVector(); + } + if (RngListTable) { + DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, + isLittleEndian, RngListTable->getAddrSize()); + if (auto RangeList = RngListTable->findRangeList(RangesData, Offset)) + return RangeList->getAbsoluteRanges(getBaseAddress()); + } + return DWARFAddressRangesVector(); +} + +DWARFAddressRangesVector DWARFUnit::findRnglistFromIndex(uint32_t Index) { + if (auto Offset = getRnglistOffset(Index)) + return findRnglistFromOffset(*Offset + RangeSectionBase); + return DWARFAddressRangesVector(); +} + void DWARFUnit::collectAddressRanges(DWARFAddressRangesVector &CURanges) { DWARFDie UnitDie = getUnitDIE(); if (!UnitDie) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-rnglists.s b/llvm/test/DebugInfo/X86/dwarfdump-rnglists.s new file mode 100644 index 00000000000..3916e102f3f --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-rnglists.s @@ -0,0 +1,192 @@ +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o %t.o +# RUN: llvm-dwarfdump -v -debug-info %t.o 2> %t.err | FileCheck %s +# RUN: FileCheck %s --input-file %t.err --check-prefix=ERR + +# Test object to verify dwarfdump handles v5 range lists. +# We use very simplified compile unit dies. +# There are 2 full CUs with DW_AT_rnglists_base, one with a DW_AT_ranges +# attribute using DW_FORM_sec_offset, the other with DW_AT_ranges using +# DW_FORM_rnglistx. + + .section .debug_abbrev,"",@progbits + .byte 0x01 # Abbrev code + .byte 0x11 # DW_TAG_compile_unit + .byte 0x00 # DW_CHILDREN_no + .byte 0x74 # DW_AT_rnglists_base + .byte 0x17 # DW_FORM_sec_offset + .byte 0x55 # DW_AT_ranges + .byte 0x17 # DW_FORM_sec_offset + .byte 0x00 # EOM(1) + .byte 0x00 # EOM(2) + .byte 0x02 # Abbrev code + .byte 0x11 # DW_TAG_compile_unit + .byte 0x00 # DW_CHILDREN_no + .byte 0x74 # DW_AT_rnglists_base + .byte 0x17 # DW_FORM_sec_offset + .byte 0x55 # DW_AT_ranges + .byte 0x23 # DW_FORM_rnglistx + .byte 0x00 # EOM(1) + .byte 0x00 # EOM(2) + .byte 0x00 # EOM(3) + +# The split CU uses DW_FORM_rnglistx (the only correct option). +# There is no DW_AT_rnglists_base in split units. + .section .debug_abbrev.dwo,"",@progbits + .byte 0x01 # Abbrev code + .byte 0x11 # DW_TAG_compile_unit + .byte 0x00 # DW_CHILDREN_no + .byte 0x55 # DW_AT_ranges + .byte 0x23 # DW_FORM_rnglistx + .byte 0x00 # EOM(1) + .byte 0x00 # EOM(2) + .byte 0x00 # EOM(3) + + .section .debug_info,"",@progbits +# DWARF v5 CU header. + .long CU1_5_end-CU1_5_version # Length of Unit +CU1_5_version: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 4 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section +# The compile-unit DIE, which has DW_AT_rnglists_base and DW_AT_ranges. + .byte 1 # Abbreviation code + .long Rnglist_Table0_base # DW_AT_rnglists_base + .long Rnglist_Table0_Rnglist0 # DW_AT_ranges + .byte 0 # NULL + .byte 0 # NULL +CU1_5_end: + +# DWARF v5 CU header + .long CU2_5_end-CU2_5_version # Length of Unit +CU2_5_version: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 4 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section +# The compile-unit DIE, which has DW_AT_rnglists_base and DW_AT_ranges. + .byte 2 # Abbreviation code + .long Rnglist_Table0_base # DW_AT_rnglists_base + .uleb128 1 # DW_AT_ranges + .byte 0 # NULL +CU2_5_end: +# A CU with an invalid DW_AT_rnglists_base attribute + .long CU3_5_end-CU3_5_version # Length of Unit +CU3_5_version: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 4 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section +# The compile-unit DIE, which has DW_AT_rnglists_base and DW_AT_ranges. + .byte 2 # Abbreviation code + .long 0x8 # DW_AT_rnglists_base + .long 0 # DW_AT_ranges + .byte 0 # NULL +CU3_5_end: + + .section .debug_info.dwo,"",@progbits + +# DWARF v5 split CU header. + .long CU_split_5_end-CU_split_5_version # Length of Unit +CU_split_5_version: + .short 5 # DWARF version number + .byte 5 # DWARF Unit Type + .byte 4 # Address Size (in bytes) + .long 0 # Offset Into Abbrev Section +# The compile-unit DIE, which has DW_AT_rnglists_base and DW_AT_ranges. + .byte 1 # Abbreviation code + .uleb128 1 # DW_AT_ranges + .byte 0 # NULL +CU_split_5_end: + + .section .debug_rnglists,"",@progbits +# A rnglist table with 2 range lists. The first one uses DW_RLE_start_end +# and DW_RLE_start_length. The second one uses DW_RLE_base_address and +# DW_RLE_offset_pair. The range lists have entries in the offset table. + .long Rnglist_Table0_end - Rnglist_Table0 # table length +Rnglist_Table0: + .short 5 # version + .byte 4 # address size + .byte 0 # segment selector size + .long 2 # offset entry count +Rnglist_Table0_base: +# 2 offset entries which can be used by DW_FORM_rnglistx. + .long Rnglist_Table0_Rnglist0 - Rnglist_Table0_base + .long Rnglist_Table0_Rnglist1 - Rnglist_Table0_base +Rnglist_Table0_Rnglist0: + .byte 6 # DW_RLE_start_end + .long Range0_start + .long Range0_end + .byte 7 # DW_RLE_start_length + .long Range1_start + .uleb128 Range1_end - Range1_start + .byte 0 # DW_RLE_end_of_list +Rnglist_Table0_Rnglist1: + .byte 5 # DW_RLE_base_address + .long Range0_start + .byte 4 # DW_RLE_offset_pair + .uleb128 Range1_start - Range0_start + .uleb128 Range1_end - Range0_start + .byte 0 # DW_RLE_end_of_list +Rnglist_Table0_end: + +# A rnglist table for the split unit with an empty rangelist and one that +# uses DW_RLE_base_address and DW_RLE_offset_pair. The ranges have entries +# in the offset table. We use the empty range list so we can test +# DW_FORM_rnglistx with an index other than 0. + .section .debug_rnglists.dwo,"",@progbits + .long Rnglist_Table0_dwo_end - Rnglist_Table0_dwo # table length +Rnglist_Table0_dwo: + .short 5 # version + .byte 4 # address size + .byte 0 # segment selector size + .long 2 # offset entry count +Rnglist_Table0_base_dwo: +# 2 offset entries which can be used by DW_FORM_rnglistx. + .long Rnglist_Table0_Rnglist0_dwo - Rnglist_Table0_base_dwo + .long Rnglist_Table0_Rnglist1_dwo - Rnglist_Table0_base_dwo +Rnglist_Table0_Rnglist0_dwo: + .byte 0 # DW_RLE_start_end +Rnglist_Table0_Rnglist1_dwo: + .byte 5 # DW_RLE_base_address + .long Range0_start - .text + .byte 4 # DW_RLE_offset_pair + .uleb128 Range1_start - Range0_start + .uleb128 Range1_end - Range0_start + .byte 0 # DW_RLE_end_of_list +Rnglist_Table0_dwo_end: + +.text + .space 20 +Range0_start: # Range0: 0x14 - 0x1c + .space 10 +Range0_end: + .space 12 +Range1_start: # Range1: 0x2a - 0x34 + .space 10 +Range1_end: + +# CHECK: .debug_info contents: +# CHECK: Compile Unit: +# CHECK-NOT: Compile Unit: +# CHECK: DW_TAG_compile_unit +# CHECK-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c) +# CHECK-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000014 +# CHECK-NEXT: [0x00000014, 0x0000001e) ".text" +# CHECK-NEXT: [0x0000002a, 0x00000034) ".text") + +# CHECK: Compile Unit: +# CHECK-NOT: Compile Unit: +# CHECK: DW_TAG_compile_unit +# CHECK-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c) +# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000018 +# CHECK-NEXT: [0x0000002a, 0x00000034) ".text") + +# CHECK: .debug_info.dwo contents: +# CHECK: Compile Unit: +# CHECK-NOT: contents: +# CHECK: DW_TAG_compile_unit +# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000009 +# CHECK-NEXT: [0x0000002a, 0x00000034)) + +#ERR: error: parsing a range list table: Did not detect a valid range list table with base = 0x8 |

