llvm / llvm/llvm-project

[llvm-dwarfdump] --verify demands a .debug_names entry for a DW_TAG_partial_unit root

Open Beginner friendly
#219,720 1 comment 0 reactions 0 assignees View on GitHub
tools:llvm-dwarfdump
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Summary

`llvm-dwarfdump --verify` checks that a `.debug_names` index is complete, and it decides which DIEs the index owes an entry by testing tags. `DW_TAG_compile_unit` and `DW_TAG_module` are excluded from that requirement; `DW_TAG_partial_unit` is not. A named partial unit root is therefore demanded of the index, and every file that leaves it out is reported as broken.

`dwz` is what produces partial units, and it is a standard step in Fedora, RHEL and Debian debuginfo packaging, so this is the ordinary shape of a distribution debuginfo file rather than a corner case.

## Reproducer

One DWARFv5 partial unit holding a subprogram `foo`, and a `.debug_names` index that contains `foo`. No linking is involved.

```console
$ yaml2obj nofx-pu.yaml -o nofx-pu.o
$ llvm-dwarfdump --verify nofx-pu.o
Verifying .debug_names...
warning: Name Index @ 0x0 does not contain a hash table.
error: Name Index @ 0x0: Entry for DIE @ 0xc (DW_TAG_partial_unit) with name dwz-common.h missing.
error: Aggregated error counts:
error: Name Index DIE entry missing name occurred 1 time(s).
Errors detected.
```

`dwz-common.h` is the root's `DW_AT_name`, which is a source file path rather than a program identifier.

Changing the root tag to `DW_TAG_compile_unit` and the unit type to `DW_UT_compile`, and nothing else, is clean:

```console
$ llvm-dwarfdump --verify nofx-cu.o
Verifying .debug_names...
warning: Name Index @ 0x0 does not contain a hash table.
No errors.
```

The hash table warning is an artifact of the hand-built index, which `yaml2obj` emits without one. It is present in both runs and is unrelated.

nofx-pu.yaml

```yaml
--- !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_str:
- foo
- dwz-common.h
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_strp
- Attribute: DW_AT_language
Form: DW_FORM_data2
- Tag: DW_TAG_subprogram
Children: DW_CHILDREN_no
Attributes:
- Attribute: DW_AT_name
Form: DW_FORM_strp
- 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_partial
Entries:
- AbbrCode: 1
Values:
- CStr: by_hand
- Value: 0x4
- Value: 0x04
- AbbrCode: 2
Values:
- Value: 0x0
- Value: 0x1130
- Value: 0x10
- AbbrCode: 0
debug_names:
Abbreviations:
- Code: 1
Tag: DW_TAG_subprogram
Indices:
- Idx: DW_IDX_die_offset
Form: DW_FORM_ref4
Entries:
- Name: 0x0
Code: 1
Values: [ 0x1b ]
...
```

The control is the same file with `DW_TAG_partial_unit` replaced by `DW_TAG_compile_unit` and `DW_UT_partial` by `DW_UT_compile`.

## Analysis

https://github.com/llvm/llvm-project/blob/d34be0ff2eb68d0e486016d8278695fce7d80c3f/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp#L1969-L1979

```cpp
// 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:
case DW_TAG_module:
return;
```

The comment states the rule correctly and the list under it implements only part of it. A partial compilation unit is a compilation unit, and its root has a name for the same reason a full one does, so it belongs in that exclusion beside `DW_TAG_compile_unit`.

Nothing else in the check distinguishes the two tags. `getNames()` reads the root's `DW_AT_name` either way, and the completeness loop that follows the switch then looks that name up in the index and reports it missing.

## Specification

DWARF Version 5, February 13, 2017.

Section 6.1.1.1 "Contents of the Name Index" (page 137) fixes what the index owes an entry to:

> The name index must contain an entry for each debugging information entry that defines a named subprogram, label, variable, type, or namespace, subject to the following rules:

A compilation unit entry defines none of those. What its name is instead is given by Section 3.1.1 "Full and Partial Compilation Unit Entries" (page 61), item 2, which applies to full and partial units alike:

> A `DW_AT_name` attribute whose value is a null-terminated string containing the full or relative path name (relative to the value of the `DW_AT_comp_dir` attribute, see below) of the primary source file from which the compilation unit was derived.

Section 3.1.1 (page 60) is what makes the two tags peers here:

> A full compilation unit is represented by a debugging information entry with the tag `DW_TAG_compile_unit`. A partial compilation unit is represented by a debugging information entry with the tag `DW_TAG_partial_unit`.

A path name is not a named subprogram, label, variable, type or namespace under either tag, so requiring an index entry for one and not the other has no basis in the standard.

## Interaction with #219599

Issue #219599 reports the other half of this disagreement: the linker puts a partial unit root into `.debug_names`, which it should not. The verifier here requires the entry that #219599 says should not exist, so only one of the two behaviours can be right.

PR #219641 fixes the linker side. With that patch applied, its own test input fails this check:

```console
$ llvm-dwarfutil --build-accelerator=DWARF pu.o pu.out
$ llvm-dwarfdump --verify pu.out
error: Name Index @ 0x0: Entry for DIE @ 0xc (DW_TAG_partial_unit) with name dwz-common.h missing.
```

Correcting the producer therefore makes every partial unit carrying an accelerator table fail `--verify` until this check is corrected too.

## Expected behavior

`--verify` does not require a `.debug_names` entry for a `DW_TAG_partial_unit` root, as it already does not for a `DW_TAG_compile_unit` root.

## Versions

Reproduces on stock Fedora `llvm-dwarfdump` 22.1.8, so it is long-standing. The code site is unchanged on `main` as of d34be0ff2eb68d0e486016d8278695fce7d80c3f, and a build of that tree reports the same error.

This report was produced with AI assistance; I have reviewed it and am accountable for it.

Contributor guide

Open the contributing guide

Research direction

Start in llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp at the name-index completeness check referenced in the issue, then inspect nofx-pu.yaml as the reproducer. Run llvm-dwarfdump --verify on the partial-unit input and its compile-unit control; done means the partial-unit root is not reported as a missing name-index entry while the existing verification behavior remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.