[llvm] [DWARFLinker] Emit DW_AT_addr_base for units that had none (PR #219357)

Arcadiy Ivanov via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 3 10:27:10 PDT 2026


https://github.com/arcivanov updated https://github.com/llvm/llvm-project/pull/219357

>From 4e18b5a2b3c997c1bfa70084b7104e9ab9486afb Mon Sep 17 00:00:00 2001
From: Arcadiy Ivanov <arcadiy at ivanov.biz>
Date: Fri, 28 Aug 2026 03:09:13 -0400
Subject: [PATCH 1/2] [DWARFLinker] Key str_offsets_base and unit_type on the
 unit root

Fix DWARFv5 output for a unit whose root is DW_TAG_partial_unit, as
produced by dwz. The classic and parallel linkers rewrite strings into
DW_FORM_strx but add DW_AT_str_offsets_base only to a
DW_TAG_compile_unit root, so every string in a partial unit reads back
empty. Both also write DW_UT_compile into every unit header,
contradicting a partial unit root.

Add DW_AT_str_offsets_base to the unit root whatever its tag, and emit
DW_UT_partial for a DW_TAG_partial_unit root. Its header has the same
layout as DW_UT_compile.

The test links a partial unit with both linkers, with and without
garbage collection, and checks the unit type, the attribute, the strings
and --verify. The update mode runs pass --build-accelerator, because
--no-garbage-collection alone copies the file without linking it.

Fixes #219365.

Assisted-by: Claude Code
---
 llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp  |   4 +-
 .../lib/DWARFLinker/Classic/DWARFStreamer.cpp |   4 +-
 .../Parallel/DIEAttributeCloner.cpp           |   6 +-
 .../DWARFLinker/Parallel/DWARFEmitterImpl.cpp |   4 +-
 .../ELF/X86/dwarf5-partial-unit.test          | 120 ++++++++++++++++++
 5 files changed, 131 insertions(+), 7 deletions(-)
 create mode 100644 llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-partial-unit.test

diff --git a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp
index 454143ec001209..9254a33dee60d8 100644
--- a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp
@@ -2013,8 +2013,8 @@ DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE,
     }
   }
 
-  if (Unit.getOrigUnit().getVersion() >= 5 && !AttrInfo.AttrStrOffsetBaseSeen &&
-      Die->getTag() == dwarf::DW_TAG_compile_unit) {
+  if (Die == Unit.getOutputUnitDIE() && Unit.getOrigUnit().getVersion() >= 5 &&
+      !AttrInfo.AttrStrOffsetBaseSeen) {
     // No DW_AT_str_offsets_base seen, add it to the DIE.
     Die->addValue(DIEAlloc, dwarf::DW_AT_str_offsets_base,
                   dwarf::DW_FORM_sec_offset, DIEInteger(8));
diff --git a/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp b/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp
index c6ccc71889fc41..26c0bc00a73898 100644
--- a/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp
+++ b/llvm/lib/DWARFLinker/Classic/DWARFStreamer.cpp
@@ -188,7 +188,9 @@ void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit,
   Asm->emitInt16(DwarfVersion);
 
   if (DwarfVersion >= 5) {
-    Asm->emitInt8(dwarf::DW_UT_compile);
+    dwarf::Tag RootTag = Unit.getOutputUnitDIE()->getTag();
+    Asm->emitInt8(RootTag == dwarf::DW_TAG_partial_unit ? dwarf::DW_UT_partial
+                                                        : dwarf::DW_UT_compile);
     Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize());
     // We share one abbreviations table across all units so it's always at the
     // start of the section.
diff --git a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
index 68d7b9d72cf618..67545ee03d15b5 100644
--- a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
@@ -115,10 +115,10 @@ void DIEAttributeCloner::clone() {
   }
 
   // We convert source strings into the indexed form for DWARFv5.
-  // Check if original compile unit already has DW_AT_str_offsets_base
+  // Check if original unit already has DW_AT_str_offsets_base
   // attribute.
-  if (InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit &&
-      InUnit.getVersion() >= 5 && !AttrInfo.HasStringOffsetBaseAttr) {
+  if (InputDIEIdx == 0 && InUnit.getVersion() >= 5 &&
+      !AttrInfo.HasStringOffsetBaseAttr && OutUnit.isCompileUnit()) {
     DebugInfoOutputSection.notePatchWithOffsetUpdate(
         DebugOffsetPatch{AttrOutOffset,
                          &OutUnit->getOrCreateSectionDescriptor(
diff --git a/llvm/lib/DWARFLinker/Parallel/DWARFEmitterImpl.cpp b/llvm/lib/DWARFLinker/Parallel/DWARFEmitterImpl.cpp
index 18d94ac2d834ee..efd70eaef0d266 100644
--- a/llvm/lib/DWARFLinker/Parallel/DWARFEmitterImpl.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DWARFEmitterImpl.cpp
@@ -139,7 +139,9 @@ void DwarfEmitterImpl::emitCompileUnitHeader(DwarfUnit &Unit) {
   Asm->emitInt16(Unit.getVersion());
 
   if (Unit.getVersion() >= 5) {
-    Asm->emitInt8(dwarf::DW_UT_compile);
+    dwarf::Tag RootTag = Unit.getTag();
+    Asm->emitInt8(RootTag == dwarf::DW_TAG_partial_unit ? dwarf::DW_UT_partial
+                                                        : dwarf::DW_UT_compile);
     Asm->emitInt8(Unit.getFormParams().AddrSize);
     // Proper offset to the abbreviations table will be set later.
     Asm->emitInt32(0);
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-partial-unit.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-partial-unit.test
new file mode 100644
index 00000000000000..6b9185defb2af6
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-partial-unit.test
@@ -0,0 +1,120 @@
+## Check that both linkers emit a DWARFv5 partial unit with a DW_UT_partial
+## header and a DW_AT_str_offsets_base.
+
+# RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil %t.o %t1
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+# RUN: llvm-dwarfdump --verify %t1 | FileCheck %s --check-prefix=VERIFY
+
+# RUN: llvm-dwarfutil --linker parallel %t.o %t2
+# RUN: llvm-dwarfdump -a --verbose %t2 | FileCheck %s
+# RUN: llvm-dwarfdump --verify %t2 | FileCheck %s --check-prefix=VERIFY
+
+## Without --build-accelerator, --no-garbage-collection does not run the linker.
+# RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t3
+# RUN: llvm-dwarfdump -a --verbose %t3 | FileCheck %s
+# RUN: llvm-dwarfdump --verify %t3 | FileCheck %s --check-prefix=VERIFY
+
+# RUN: llvm-dwarfutil --linker parallel --no-garbage-collection --build-accelerator=DWARF %t.o %t4
+# RUN: llvm-dwarfdump -a --verbose %t4 | FileCheck %s
+# RUN: llvm-dwarfdump --verify %t4 | FileCheck %s --check-prefix=VERIFY
+
+#CHECK: .debug_info contents:
+#CHECK: Compile Unit: {{.*}} version = 0x0005, unit_type = DW_UT_partial
+
+#CHECK: DW_TAG_partial_unit
+#CHECK: DW_AT_producer [DW_FORM_strx] {{.*}} "by_hand"
+#CHECK: DW_AT_name [DW_FORM_strx] {{.*}} "PU1"
+#CHECK: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
+
+#CHECK: DW_TAG_subprogram
+#CHECK: DW_AT_name [DW_FORM_strx] {{.*}} "foo1"
+#CHECK: DW_TAG_subprogram
+#CHECK: DW_AT_name [DW_FORM_strx] {{.*}} "foo2"
+
+#VERIFY: No errors.
+
+## DW_AT_ranges keeps the unit alive under garbage collection, and
+## DW_AT_addr_base resolves the ranges the linker rewrites.
+--- !ELF
+FileHeader:
+  Class:    ELFCLASS64
+  Data:     ELFDATA2LSB
+  Type:     ET_REL
+  Machine:  EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1130
+    Size:            0x30
+  - Name:            .debug_rnglists
+    Type:            SHT_PROGBITS
+    Flags:           [  ]
+    Content:        "1d0000000500080000000000073011000000000000100750110000000000001000"
+  - Name:            .debug_addr
+    Type:            SHT_PROGBITS
+    Flags:           [  ]
+    Content:        "0c000000050008003011000000000000"
+DWARF:
+  debug_abbrev:
+    - Table:
+      - Tag:      DW_TAG_partial_unit
+        Children: DW_CHILDREN_yes
+        Attributes:
+          - Attribute: DW_AT_producer
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_ranges
+            Form:      DW_FORM_sec_offset
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+          - Attribute: DW_AT_addr_base
+            Form:      DW_FORM_sec_offset
+      - Tag:      DW_TAG_subprogram
+        Children: DW_CHILDREN_no
+        Attributes:
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+          - Attribute: DW_AT_high_pc
+            Form:      DW_FORM_data8
+          - Attribute: DW_AT_type
+            Form:      DW_FORM_ref4
+      - Tag:      DW_TAG_base_type
+        Children: DW_CHILDREN_no
+        Attributes:
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+  debug_info:
+    - Version: 5
+      UnitType:   DW_UT_partial
+      Entries:
+        - AbbrCode: 1
+          Values:
+            - CStr: by_hand
+            - CStr: PU1
+            - Value:  0xc
+            - Value:  0x0
+            - Value:  0x8
+        - AbbrCode: 2
+          Values:
+            - CStr: foo1
+            - Value: 0x1130
+            - Value: 0x10
+## 0x5d is the offset of the DW_TAG_base_type DIE below.
+            - Value: 0x5d
+        - AbbrCode: 2
+          Values:
+            - CStr: foo2
+            - Value: 0x1150
+            - Value: 0x10
+            - Value: 0x5d
+        - AbbrCode: 3
+          Values:
+            - CStr: int
+        - AbbrCode: 0
+...

>From 25a6b2419cc38104d9b3d467f0bab8d9f2d7349d Mon Sep 17 00:00:00 2001
From: Arcadiy Ivanov <arcadiy at ivanov.biz>
Date: Fri, 28 Aug 2026 00:10:29 -0400
Subject: [PATCH 2/2] [DWARFLinker] Emit DW_AT_addr_base for units that had
 none

Fix DWARFv5 units that reach the linker without DW_AT_addr_base, as GCC
emits them at -O2. Both linkers rewrite range and location lists into
base_addressx entries, which resolve through DW_AT_addr_base, but they
only ever patch an attribute copied from the input. With nothing to
patch, the classic linker hits the llvm_unreachable in patchAddrBase(),
and the parallel linker writes indices with no base, so the unit's
ranges resolve from address 0.

Add DW_AT_addr_base to every DWARFv5 unit root that lacks one, whatever
its tag, and emit the unit's .debug_addr contribution even when no
address was pooled, so the attribute always points at a table. Update
mode does not rewrite ranges, so nothing is added there.

The tests link a compile unit root and a partial unit root with both
linkers and check the attribute, the address table, the range list and
--verify. The compile unit test also checks that update mode adds no
attribute, and a third test links a unit that pools no address.

Fixes #218825.

Assisted-by: Claude Code
---
 llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp  |  16 ++-
 .../Parallel/DIEAttributeCloner.cpp           |  20 +++
 .../DWARFLinker/Parallel/DIEAttributeCloner.h |   3 +
 .../Parallel/DWARFLinkerCompileUnit.cpp       |   5 +-
 .../ELF/X86/dwarf5-no-addr-base-empty.test    |  74 +++++++++++
 .../X86/dwarf5-no-addr-base-partial-unit.test | 105 ++++++++++++++++
 .../ELF/X86/dwarf5-no-addr-base.test          | 116 ++++++++++++++++++
 7 files changed, 334 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-empty.test
 create mode 100644 llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-partial-unit.test
 create mode 100644 llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base.test

diff --git a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp
index 9254a33dee60d8..960bd16015c74f 100644
--- a/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/Classic/DWARFLinker.cpp
@@ -2021,6 +2021,15 @@ DIE *DWARFLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE,
     OutOffset += 4;
   }
 
+  // No DW_AT_addr_base seen, as GCC emits; add one for the addrx-indexed
+  // entries. emitDebugAddrSection() patches in the real offset.
+  if (!Update && Die == Unit.getOutputUnitDIE() && U.getVersion() >= 5 &&
+      !Die->findAttribute(dwarf::DW_AT_addr_base)) {
+    OutOffset += Die->addValue(DIEAlloc, dwarf::DW_AT_addr_base,
+                               dwarf::DW_FORM_sec_offset, DIEInteger(0))
+                     ->sizeOf(U.getFormParams());
+  }
+
   DIEAbbrev NewAbbrev = Die->generateAbbrev();
   if (HasChildren)
     NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes);
@@ -2216,7 +2225,10 @@ Error DWARFLinker::DIECloner::emitDebugAddrSection(
   if (DwarfVersion < 5)
     return Error::success();
 
-  if (AddrPool.getValues().empty())
+  // Every cloned unit root has DW_AT_addr_base, so emit the table even when it
+  // is empty (DWARFv5 section 7.27).
+  DIE *OutputUnitDIE = Unit.getOutputUnitDIE();
+  if (OutputUnitDIE == nullptr)
     return Error::success();
 
   MCSymbol *EndLabel = Emitter->emitDwarfDebugAddrsHeader(Unit);
@@ -2226,7 +2238,7 @@ Error DWARFLinker::DIECloner::emitDebugAddrSection(
     return createStringError(".debug_addr section offset 0x" +
                              Twine::utohexstr(AddrOffset) + " exceeds the " +
                              dwarf::FormatString(FP.Format) + " limit");
-  patchAddrBase(*Unit.getOutputUnitDIE(), DIEInteger(AddrOffset));
+  patchAddrBase(*OutputUnitDIE, DIEInteger(AddrOffset));
   Emitter->emitDwarfDebugAddrs(AddrPool.getValues(),
                                Unit.getOrigUnit().getAddressByteSize());
   Emitter->emitDwarfDebugAddrsFooter(Unit, EndLabel);
diff --git a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
index 67545ee03d15b5..c0daf2d57493c4 100644
--- a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
@@ -133,6 +133,25 @@ void DIEAttributeCloner::clone() {
                                 OutUnit->getDebugStrOffsetsHeaderSize())
             .second;
   }
+
+  // No DW_AT_addr_base seen, as GCC emits; add one for the addrx-indexed
+  // entries. Not in update mode, where HasAddrBaseAttr is never set.
+  if (InputDIEIdx == 0 && InUnit.getVersion() >= 5 &&
+      !AttrInfo.HasAddrBaseAttr &&
+      !InUnit.getGlobalData().getOptions().UpdateIndexTablesOnly) {
+    DebugInfoOutputSection.notePatchWithOffsetUpdate(
+        DebugOffsetPatch{
+            AttrOutOffset,
+            &OutUnit->getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr),
+            true},
+        PatchesOffsets);
+
+    AttrOutOffset += Generator
+                         .addScalarAttribute(dwarf::DW_AT_addr_base,
+                                             dwarf::DW_FORM_sec_offset,
+                                             OutUnit->getDebugAddrHeaderSize())
+                         .second;
+  }
 }
 
 bool DIEAttributeCloner::shouldSkipAttribute(
@@ -532,6 +551,7 @@ size_t DIEAttributeCloner::cloneScalarAttr(
 
     // Use size of .debug_addr header as attribute value. The offset to
     // .debug_addr would be added later while patching.
+    AttrInfo.HasAddrBaseAttr = true;
     return Generator
         .addScalarAttribute(AttrSpec.Attr, AttrSpec.Form,
                             OutUnit->getDebugAddrHeaderSize())
diff --git a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.h b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.h
index 2a5a2250cc5b28..7444bc59c71b28 100644
--- a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.h
+++ b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.h
@@ -39,6 +39,9 @@ struct AttributesInfo {
 
   /// Does the DIE have a string offset attribute?
   bool HasStringOffsetBaseAttr = false;
+
+  /// Does the DIE have an address base attribute?
+  bool HasAddrBaseAttr = false;
 };
 
 /// This class creates clones of input DIE attributes.
diff --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp
index 4138a9fd69aca8..92cc6e4222a00d 100644
--- a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp
@@ -750,9 +750,8 @@ Error CompileUnit::emitDebugAddrSection() {
   if (getVersion() < 5)
     return Error::success();
 
-  if (DebugAddrIndexMap.empty())
-    return Error::success();
-
+  // Every cloned unit root has DW_AT_addr_base, so emit the table even when it
+  // is empty (DWARFv5 section 7.27).
   SectionDescriptor &OutAddrSection =
       getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr);
 
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-empty.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-empty.test
new file mode 100644
index 00000000000000..60b5d2a1b3e588
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-empty.test
@@ -0,0 +1,74 @@
+## Check that a DWARFv5 unit that pools no address gets an empty .debug_addr
+## table for its DW_AT_addr_base to point at.
+
+# RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil %t.o %t1
+# RUN: llvm-dwarfdump --verify %t1 | FileCheck %s --check-prefix=VERIFY
+# RUN: llvm-dwarfdump --debug-info --debug-addr %t1 | FileCheck %s
+
+# RUN: llvm-dwarfutil --linker parallel %t.o %t2
+# RUN: llvm-dwarfdump --verify %t2 | FileCheck %s --check-prefix=VERIFY
+# RUN: llvm-dwarfdump --debug-info --debug-addr %t2 | FileCheck %s
+
+# VERIFY: No errors.
+
+# CHECK:      DW_TAG_compile_unit
+# CHECK:        DW_AT_addr_base (0x00000008)
+# CHECK:      .debug_addr contents:
+# CHECK-NEXT: Address table header: length = 0x00000004, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00
+# CHECK-NOT:  Addrs:
+
+## Only DW_FORM_addr addresses and no DW_AT_ranges, so nothing is pooled.
+--- !ELF
+FileHeader:
+  Class:    ELFCLASS64
+  Data:     ELFDATA2LSB
+  Type:     ET_REL
+  Machine:  EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1130
+    Size:            0x10
+DWARF:
+  debug_abbrev:
+    - Table:
+      - Tag:      DW_TAG_compile_unit
+        Children: DW_CHILDREN_yes
+        Attributes:
+          - Attribute: DW_AT_producer
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+          - Attribute: DW_AT_high_pc
+            Form:      DW_FORM_data8
+      - Tag:      DW_TAG_subprogram
+        Children: DW_CHILDREN_no
+        Attributes:
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+          - Attribute: DW_AT_high_pc
+            Form:      DW_FORM_data8
+  debug_info:
+    - Version:  5
+      UnitType: DW_UT_compile
+      Entries:
+        - AbbrCode: 1
+          Values:
+            - CStr: by_hand
+            - CStr: CU1
+            - Value: 0x1130
+            - Value: 0x10
+        - AbbrCode: 2
+          Values:
+            - CStr: foo1
+            - Value: 0x1130
+            - Value: 0x10
+        - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-partial-unit.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-partial-unit.test
new file mode 100644
index 00000000000000..90681adfef375b
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base-partial-unit.test
@@ -0,0 +1,105 @@
+## Same as dwarf5-no-addr-base.test, with a DW_TAG_partial_unit root.
+
+# RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil %t.o %t1
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+# RUN: llvm-dwarfdump --verify %t1 | FileCheck %s --check-prefix=VERIFY
+
+# RUN: llvm-dwarfutil --linker parallel %t.o %t2
+# RUN: llvm-dwarfdump -a --verbose %t2 | FileCheck %s
+# RUN: llvm-dwarfdump --verify %t2 | FileCheck %s --check-prefix=VERIFY
+
+#VERIFY: No errors.
+
+#CHECK: DW_TAG_partial_unit
+#CHECK:   DW_AT_ranges [DW_FORM_sec_offset]
+#CHECK-NEXT: [0x0000000000001130, 0x0000000000001140)
+#CHECK-NEXT: [0x0000000000001150, 0x0000000000001160)
+#CHECK:   DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
+
+## Both ranges are offset pairs from one DW_RLE_base_addressx.
+#CHECK: .debug_addr contents:
+#CHECK: 0x00000000: Address table header: length = {{.*}} version = 0x0005, addr_size = 0x08, seg_size = 0x00
+#CHECK: Addrs: [
+#CHECK-NEXT: 0x0000000000001130
+#CHECK-NEXT: ]
+#CHECK: .debug_rnglists contents:
+#CHECK: [DW_RLE_base_addressx]: 0x0000000000000000
+#CHECK-NEXT: [DW_RLE_offset_pair ]: {{.*}} => [0x0000000000001130, 0x0000000000001140)
+#CHECK-NEXT: [DW_RLE_offset_pair ]: {{.*}} => [0x0000000000001150, 0x0000000000001160)
+
+## Two function ranges, no DW_AT_addr_base and no .debug_addr.
+--- !ELF
+FileHeader:
+  Class:    ELFCLASS64
+  Data:     ELFDATA2LSB
+  Type:     ET_REL
+  Machine:  EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1130
+    Size:            0x30
+  - Name:            .debug_rnglists
+    Type:            SHT_PROGBITS
+    Flags:           [  ]
+    Content:        "1d0000000500080000000000073011000000000000100750110000000000001000"
+DWARF:
+  debug_abbrev:
+    - Table:
+      - Tag:      DW_TAG_partial_unit
+        Children: DW_CHILDREN_yes
+        Attributes:
+          - Attribute: DW_AT_producer
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_ranges
+            Form:      DW_FORM_sec_offset
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+      - Tag:      DW_TAG_subprogram
+        Children: DW_CHILDREN_no
+        Attributes:
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+          - Attribute: DW_AT_high_pc
+            Form:      DW_FORM_data8
+          - Attribute: DW_AT_type
+            Form:      DW_FORM_ref4
+      - Tag:      DW_TAG_base_type
+        Children: DW_CHILDREN_no
+        Attributes:
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+  debug_info:
+    - Version: 5
+      UnitType:   DW_UT_partial
+      Entries:
+        - AbbrCode: 1
+          Values:
+            - CStr: by_hand
+            - CStr: PU1
+            - Value:  0xc
+            - Value:  0x0
+        - AbbrCode: 2
+          Values:
+            - CStr: foo1
+            - Value: 0x1130
+            - Value: 0x10
+            - Value: 0x59
+        - AbbrCode: 2
+          Values:
+            - CStr: foo2
+            - Value: 0x1150
+            - Value: 0x10
+            - Value: 0x59
+        - AbbrCode: 3
+          Values:
+            - CStr: int
+        - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base.test
new file mode 100644
index 00000000000000..e76e7f0852de3a
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-no-addr-base.test
@@ -0,0 +1,116 @@
+## Check that both linkers add DW_AT_addr_base to a DWARFv5 unit with
+## DW_AT_ranges but no DW_AT_addr_base, as GCC emits at -O2.
+
+# RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil %t.o %t1
+# RUN: llvm-dwarfdump --verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK
+
+# RUN: llvm-dwarfutil --linker parallel %t.o %t2
+# RUN: llvm-dwarfdump --verify %t2 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t2 | FileCheck %s --check-prefix DWARF-CHECK
+
+## Update mode does not rewrite the ranges, so it adds no DW_AT_addr_base.
+## Without --build-accelerator, --no-garbage-collection does not run the linker.
+
+# RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t3
+# RUN: llvm-dwarfdump --verify %t3 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t3 | FileCheck %s --check-prefix UPDATE-CHECK \
+# RUN:   --implicit-check-not=DW_AT_addr_base
+
+# RUN: llvm-dwarfutil --linker parallel --no-garbage-collection --build-accelerator=DWARF %t.o %t4
+# RUN: llvm-dwarfdump --verify %t4 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t4 | FileCheck %s --check-prefix UPDATE-CHECK \
+# RUN:   --implicit-check-not=DW_AT_addr_base
+
+#CHECK: No errors.
+
+#UPDATE-CHECK: .debug_info contents:
+#UPDATE-CHECK: DW_TAG_compile_unit
+#UPDATE-CHECK:   DW_AT_name [DW_FORM_strx] {{.*}} "CU1"
+#UPDATE-CHECK:   DW_AT_ranges [DW_FORM_sec_offset]
+#UPDATE-CHECK-NEXT: [0x0000000000001130, 0x0000000000001140))
+
+## DW_AT_addr_base points past the 8-byte .debug_addr header.
+#DWARF-CHECK: DW_TAG_compile_unit
+#DWARF-CHECK:   DW_AT_name [DW_FORM_strx] {{.*}} "CU1"
+#DWARF-CHECK:   DW_AT_ranges [DW_FORM_sec_offset]
+#DWARF-CHECK:   DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
+#DWARF-CHECK: .debug_addr contents:
+#DWARF-CHECK: 0x00000000: Address table header: length = {{.*}} version = 0x0005, addr_size = 0x08, seg_size = 0x00
+#DWARF-CHECK: Addrs: [
+#DWARF-CHECK: 0x0000000000001130
+#DWARF-CHECK: ]
+#DWARF-CHECK: .debug_rnglists contents:
+#DWARF-CHECK: [DW_RLE_base_addressx]: 0x0000000000000000
+
+## DW_AT_ranges, no DW_AT_addr_base and no .debug_addr.
+--- !ELF
+FileHeader:
+  Class:    ELFCLASS64
+  Data:     ELFDATA2LSB
+  Type:     ET_REL
+  Machine:  EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1130
+    Size:            0x20
+  - Name:            .debug_rnglists
+    Type:            SHT_PROGBITS
+    Flags:           [  ]
+    Content:        "1300000005000800000000000730110000000000001000"
+DWARF:
+  debug_abbrev:
+    - Table:
+      - Tag:      DW_TAG_compile_unit
+        Children: DW_CHILDREN_yes
+        Attributes:
+          - Attribute: DW_AT_producer
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_ranges
+            Form:      DW_FORM_sec_offset
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+      - Tag:      DW_TAG_subprogram
+        Children: DW_CHILDREN_yes
+        Attributes:
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+          - Attribute: DW_AT_low_pc
+            Form:      DW_FORM_addr
+          - Attribute: DW_AT_high_pc
+            Form:      DW_FORM_data8
+          - Attribute: DW_AT_type
+            Form:      DW_FORM_ref4
+      - Tag:      DW_TAG_base_type
+        Children: DW_CHILDREN_no
+        Attributes:
+          - Attribute: DW_AT_name
+            Form:      DW_FORM_string
+  debug_info:
+    - Version: 5
+      UnitType:   DW_UT_compile
+      Entries:
+        - AbbrCode: 1
+          Values:
+            - CStr: by_hand
+            - CStr: CU1
+            - Value:  0xc
+            - Value:  0x0
+        - AbbrCode: 2
+          Values:
+            - CStr: foo1
+            - Value: 0x1130
+            - Value: 0x10
+            - Value: 0x40
+        - AbbrCode: 0
+        - AbbrCode: 3
+          Values:
+            - CStr: int
+        - AbbrCode: 0
+...



More information about the llvm-commits mailing list