[llvm] [DWARFVerifier] Do not demand a .debug_names entry for a unit root (PR #219727)

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


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

>From 890b3e315c5ac9c744b251ec69eb902c596d06ba Mon Sep 17 00:00:00 2001
From: Arcadiy Ivanov <arcadiy at ivanov.biz>
Date: Sat, 29 Aug 2026 16:50:57 -0400
Subject: [PATCH] [DWARFVerifier] Do not demand a .debug_names entry for a unit
 root

llvm-dwarfdump --verify checks that .debug_names has an entry for every
named DIE, and excludes unit roots by tag, listing only
DW_TAG_compile_unit. A named DW_TAG_partial_unit root is therefore
reported as missing from the index, although its DW_AT_name is the path
of the primary source file and the index owes it no entry.

Exclude the unit root by position instead. It is the only DIE in a unit
without a parent, so this covers every unit root tag. DW_TAG_module
stays excluded by tag.

The test indexes a partial unit and a compile unit, neither root
indexed, and expects no errors. A second run leaves a subprogram under
the partial unit root out of the index and checks that it is the only
missing entry reported.

Fixes #219720.

Assisted-by: Claude Code
---
 llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp    |   7 +-
 ...g-names-verify-completeness-partial-unit.s | 173 ++++++++++++++++++
 2 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness-partial-unit.s

diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
index ebd54e18bbca9d..01ae09450a0e0a 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
@@ -1966,14 +1966,17 @@ void DWARFVerifier::verifyNameIndexCompleteness(
   if (EntryNames.empty())
     return;
 
+  // Unit roots, whatever their tag, have names but shouldn't be indexed.
+  if (!Die.getParent())
+    return;
+
   // We deviate from the specification here, which says:
   // "The name index must contain an entry for each debugging information entry
   // that defines a named subprogram, label, variable, type, or namespace,
   // subject to ..."
   // Explicitly exclude all TAGs that we know shouldn't be indexed.
   switch (Die.getTag()) {
-  // Compile units and modules have names but shouldn't be indexed.
-  case DW_TAG_compile_unit:
+  // Modules have names but shouldn't be indexed.
   case DW_TAG_module:
     return;
 
diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness-partial-unit.s b/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness-partial-unit.s
new file mode 100644
index 00000000000000..a5ecc541dc4312
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-completeness-partial-unit.s
@@ -0,0 +1,173 @@
+# Check that the .debug_names completeness check does not demand an entry for
+# a DW_TAG_partial_unit root, as for a DW_TAG_compile_unit root. One index
+# covers the partial unit U01 and the compile unit U02.
+
+# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj -o %t.o
+# RUN: llvm-dwarfdump --verify %t.o | FileCheck %s --implicit-check-not=error:
+
+# CHECK: No errors.
+
+# A DIE under the partial unit root that is missing from the index is still
+# reported.
+
+# RUN: llvm-mc -triple x86_64-pc-linux --defsym UNINDEXED=1 %s -filetype=obj -o %t.unindexed.o
+# RUN: not llvm-dwarfdump --verify %t.unindexed.o | FileCheck %s --check-prefix=UNINDEXED
+
+# UNINDEXED:      error: Name Index @ 0x0: Entry for DIE @ {{.*}} (DW_TAG_subprogram) with name unindexed missing.
+# UNINDEXED:      error: Aggregated error counts:
+# UNINDEXED-NEXT: error: Name Index DIE entry missing name occurred 1 time(s).
+
+	.section	.debug_str,"MS", at progbits,1
+.Lstring_producer:
+	.asciz	"Hand-written DWARF"
+.Lstring_dwz_common:
+	.asciz	"dwz-common.h"
+.Lstring_main:
+	.asciz	"main.c"
+.Lstring_foo:
+	.asciz	"foo"
+.Lstring_bar:
+	.asciz	"bar"
+.Lstring_unindexed:
+	.asciz	"unindexed"
+
+	.section	.debug_abbrev,"", at progbits
+.Lsection_abbrev:
+	.byte	1                       # Abbreviation Code
+	.byte	60                      # DW_TAG_partial_unit
+	.byte	1                       # DW_CHILDREN_yes
+	.byte	37                      # DW_AT_producer
+	.byte	14                      # DW_FORM_strp
+	.byte	3                       # DW_AT_name
+	.byte	14                      # DW_FORM_strp
+	.byte	19                      # DW_AT_language
+	.byte	5                       # DW_FORM_data2
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+
+	.byte	2                       # Abbreviation Code
+	.byte	46                      # DW_TAG_subprogram
+	.byte	0                       # DW_CHILDREN_no
+	.byte	3                       # DW_AT_name
+	.byte	14                      # DW_FORM_strp
+	.byte	17                      # DW_AT_low_pc
+	.byte	1                       # DW_FORM_addr
+	.byte	18                      # DW_AT_high_pc
+	.byte	6                       # DW_FORM_data4
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+
+	.byte	3                       # Abbreviation Code
+	.byte	17                      # DW_TAG_compile_unit
+	.byte	1                       # DW_CHILDREN_yes
+	.byte	37                      # DW_AT_producer
+	.byte	14                      # DW_FORM_strp
+	.byte	3                       # DW_AT_name
+	.byte	14                      # DW_FORM_strp
+	.byte	19                      # DW_AT_language
+	.byte	5                       # DW_FORM_data2
+	.byte	17                      # DW_AT_low_pc
+	.byte	1                       # DW_FORM_addr
+	.byte	18                      # DW_AT_high_pc
+	.byte	6                       # DW_FORM_data4
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+
+	.byte	0                       # EOM(3)
+
+	.section	.debug_info,"", at progbits
+# U01, the partial unit. Its root has no address range, as in dwz output.
+.Lcu_begin0:
+	.long	.Lcu_end0-.Lcu_start0   # Length of Unit
+.Lcu_start0:
+	.short	5                       # DWARF version number
+	.byte	3                       # DW_UT_partial
+	.byte	8                       # Address Size (in bytes)
+	.long	.Lsection_abbrev        # Offset Into Abbrev. Section
+	.byte	1                       # Abbrev [1] DW_TAG_partial_unit
+	.long	.Lstring_producer       # DW_AT_producer
+	.long	.Lstring_dwz_common     # DW_AT_name
+	.short	12                      # DW_AT_language
+.Ldie_foo:
+	.byte	2                       # Abbrev [2] DW_TAG_subprogram
+	.long	.Lstring_foo            # DW_AT_name
+	.quad	0x1000                  # DW_AT_low_pc
+	.long	0x10                    # DW_AT_high_pc
+.ifdef UNINDEXED
+.Ldie_unindexed:
+	.byte	2                       # Abbrev [2] DW_TAG_subprogram
+	.long	.Lstring_unindexed      # DW_AT_name
+	.quad	0x1010                  # DW_AT_low_pc
+	.long	0x10                    # DW_AT_high_pc
+.endif
+	.byte	0                       # End Of Children Mark
+.Lcu_end0:
+
+# U02, the control.
+.Lcu_begin1:
+	.long	.Lcu_end1-.Lcu_start1   # Length of Unit
+.Lcu_start1:
+	.short	5                       # DWARF version number
+	.byte	1                       # DW_UT_compile
+	.byte	8                       # Address Size (in bytes)
+	.long	.Lsection_abbrev        # Offset Into Abbrev. Section
+	.byte	3                       # Abbrev [3] DW_TAG_compile_unit
+	.long	.Lstring_producer       # DW_AT_producer
+	.long	.Lstring_main           # DW_AT_name
+	.short	12                      # DW_AT_language
+	.quad	0x2000                  # DW_AT_low_pc
+	.long	0x100                   # DW_AT_high_pc
+.Ldie_bar:
+	.byte	2                       # Abbrev [2] DW_TAG_subprogram
+	.long	.Lstring_bar            # DW_AT_name
+	.quad	0x2000                  # DW_AT_low_pc
+	.long	0x10                    # DW_AT_high_pc
+	.byte	0                       # End Of Children Mark
+.Lcu_end1:
+
+	.section	.debug_names,"", at progbits
+	.long	.Lnames_end0-.Lnames_start0 # Header: contribution length
+.Lnames_start0:
+	.short	5                       # Header: version
+	.short	0                       # Header: padding
+	.long	2                       # Header: compilation unit count
+	.long	0                       # Header: local type unit count
+	.long	0                       # Header: foreign type unit count
+	.long	2                       # Header: bucket count
+	.long	2                       # Header: name count
+	.long	.Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
+	.long	0                       # Header: augmentation length
+	.long	.Lcu_begin0             # Compilation unit 0
+	.long	.Lcu_begin1             # Compilation unit 1
+	.long	1                       # Bucket 0
+	.long	2                       # Bucket 1
+	.long	193487034               # Hash in Bucket 0: bar
+	.long	193491849               # Hash in Bucket 1: foo
+	.long	.Lstring_bar            # String in Bucket 0: bar
+	.long	.Lstring_foo            # String in Bucket 1: foo
+	.long	.Lnames_bar-.Lnames_entries0 # Offset in Bucket 0
+	.long	.Lnames_foo-.Lnames_entries0 # Offset in Bucket 1
+.Lnames_abbrev_start0:
+	.byte	46                      # Abbrev code
+	.byte	46                      # DW_TAG_subprogram
+	.byte	1                       # DW_IDX_compile_unit
+	.byte	11                      # DW_FORM_data1
+	.byte	3                       # DW_IDX_die_offset
+	.byte	19                      # DW_FORM_ref4
+	.byte	0                       # End of abbrev
+	.byte	0                       # End of abbrev
+	.byte	0                       # End of abbrev list
+.Lnames_abbrev_end0:
+.Lnames_entries0:
+.Lnames_bar:
+	.byte	46                      # Abbrev code
+	.byte	1                       # DW_IDX_compile_unit
+	.long	.Ldie_bar-.Lcu_begin1   # DW_IDX_die_offset
+	.long	0                       # End of list: bar
+.Lnames_foo:
+	.byte	46                      # Abbrev code
+	.byte	0                       # DW_IDX_compile_unit
+	.long	.Ldie_foo-.Lcu_begin0   # DW_IDX_die_offset
+	.long	0                       # End of list: foo
+	.p2align	2
+.Lnames_end0:



More information about the llvm-commits mailing list