[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