[DWARF v5] Improved support for .debug_rnglists (consumer). Enables any consumer to 
extract DWARF v5 encoded rangelists.

Reviewer: JDevlieghere

Differential Revision: https://reviews.llvm.org/D45549

llvm-svn: 332759
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
index 8488fe8..08c77a0 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -248,6 +248,28 @@
   }
 }
 
+// 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 @@
 
   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 @@
   DWARFSectionMap LocDWOSection;
   DWARFSectionMap StringOffsetDWOSection;
   DWARFSectionMap RangeDWOSection;
+  DWARFSectionMap RnglistsDWOSection;
   DWARFSectionMap AddrSection;
   DWARFSectionMap AppleNamesSection;
   DWARFSectionMap AppleTypesSection;
@@ -1192,6 +1207,7 @@
         .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 @@
   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 3a273f8..0c2bb32 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 @@
     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 @@
                                                  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 @@
       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 @@
                          "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 @@
   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 @@
   // 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 0d045c5..33cb00d 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
@@ -257,6 +257,16 @@
       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 @@
   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 b3ecf8e..1aa43c6 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp
@@ -280,6 +280,7 @@
       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 @@
     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 6fa604c..11cec59 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 @@
   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 @@
       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 @@
   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 @@
   }
 }
 
+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)