[Dwarf-discuss] [DWARF 6 proposal] Unify definition addresses after linkage selection

Longjun Luo luolongjuna@gmail.com
Tue Sep 22 07:19:48 GMT 2026


My original message was rewrapped during sending, leaving awkward line
breaks. Here is the same proposal with corrected wrapping. There are
no content changes.

Type: Enhancement
Requested target: DWARF 6
Basis: July 3, 2026 working draft, July 25 snapshot [1]. Page numbers
below are printed page numbers. This is a discussion proposal.

## Background

We use DWARF information when creating user-space hot patches and
validate its correctness and consistency as part of that process. Our
investigation identified two failures: incorrect source attribution
for a data address, and incorrect function selection when evaluating a
name.

To illustrate them, link these units weak first, without garbage
collection. Both function bodies and both data allocations survive:

```c
/* weak.c */
__attribute__((weak)) int value = 22;
__attribute__((weak, noinline)) int action(void) { return 22; }
/* strong.c */
int value = 11;
__attribute__((noinline)) int action(void) { return 11; }
```

Address lookup: querying the selected variable's address can report
weak.c instead of strong.c. We reproduced this with LLVM DATA lookup,
also used by ASan, across BFD, gold, LLD and mold. The resulting
overflow diagnostic can identify the wrong source definition.

Name lookup: in constructed weak-unit contexts, unmodified GDB and
LLDB can evaluate `action()` by calling the weak body and return 22,
while the program calls the strong body and returns 11. A related LLDB
report is [5].

## Overview and scope

We propose two complementary rules for variables and subprograms:

1. Own-instance addresses reflect actual code/storage in the output.
   If the instance survives, its defining location or ranges describe
   its real position in the retained section or sections. If it is
   removed but its defining address description remains, that
   description uses the reserved target address according to its
   construct's rules.
2. For definitions subject to linker selection, supporting producers
   emit `DW_AT_selected_address` to record the address of the instance
   to which static linkage actually binds the symbol. This records
   binding separately from the represented definition's own code or
   storage.

For each symbol separately, A is the surviving overridden instance's
own address, B is the selected instance's address, and T is the
reserved target address for non-existence (Section 2.4.1, p. 26). They
are addresses, not contents or return values such as 22 and 11.

The pair (own, selected) expresses B/B for a retained selected
instance, A/B for a retained overridden instance, T/B for a removed
instance with a surviving replacement, and T/T when neither survives.
A/T was also observed: an independent alias kept the weak storage,
while garbage collection removed the unused selected strong storage.
Absence of the new attribute makes no binding assertion; T is not a
default meaning "selected", and equal addresses are not a general
identity test.

The scope is compatible definitions with fixed data storage or
out-of-line code under static weak-symbol or COMDAT selection,
including subsequent garbage collection. Dynamic interposition, copy
relocations, TLS, IFUNC and ICF/shared-instance mappings are excluded.

### Why the two rules are needed

Weak selection exposes different address roles for code and data. In
our GCC/Clang tests, the surviving weak function's code addresses
retain A through local/section references. The weak variable's
`DW_AT_location` uses a named-symbol relocation and resolves to B,
just like the strong variable's location. This loses the distinction
between the two storage instances. Their name, type, size, location
and final symbol address do not identify which definition emitted the
storage at B. Detecting this ambiguity alone does not meet our
source-attribution needs.

COMDAT selection removes the losing group's code/storage [3]. The same
own-instance rule therefore requires T for both, while the selected
address remains B. With the original producer output, the tested
unmodified linkers give these code/data addresses for a discarded
copy:

- BFD: B / B.
- gold: 0 / B.
- LLD: 0 / B.
- mold: 0 / B.
- Proposed: T / T, with selected address B for both.

Here 0 denotes observed legacy handling, not the reserved target
address. These equivalent-definition COMDAT samples establish
representation differences, not an additional consumer failure.
Together with weak selection, they show why one rule should cover both
mechanisms and both kinds of definition, independently of whether a
replacement survives.

Name lookup needs the second address role. Today, choosing the
overridden DIE can call weak code at A, but the same mistake for data
can still read 11 because that DIE's location already points to B.
Correcting its location to A exposes the same binding defect as a read
of 22. The new attribute lets runtime-name lookup distinguish the
binding target from an original instance that remains available for
explicit inspection. Final ELF symbols can provide binding evidence in
restricted cases; the attribute preserves that evidence in DWARF even
when those symbols are stripped.

Sections 4.3.3 (p. 101) and 5.1 (p. 121) describe generated
instructions and runtime variable locations separately. This proposal
supplies a common post-link contract for own instances and binding.
Multiple same-name DIEs are permitted by Section 7.1.1 (p. 163); no
global uniqueness violation is claimed. The request is to make these
distinct roles explicit and usable.

## Proposed changes

Quoted paragraphs are proposed normative wording; italic paragraphs
are non-normative implementation guidance.

1. Make defining addresses reflect the surviving instance. Add to
   Section 5.1, "Data Object Entries," p. 121, item 4, after line 14:

> For a concrete variable definition with fixed storage participating
> in static linkage selection, `DW_AT_location`, if present, describes
> the actual output location of that input definition's own storage
> while it survives. Selection of another definition does not
> substitute that other definition's storage for the represented
> instance. Runtime name binding and inspection of this emitted
> instance are distinct operations.

Scope-specific locations on non-defining declarations remain
unchanged. Add to Section 4.3.3, "Subroutine and Entry Point
Locations," p. 101, after line 24:

> A concrete subprogram's code ranges and entry address describe the
> actual output locations of its own emitted instructions while they
> survive, independently of static linkage selection.

For these defining addresses, add after Section 2.4.1, "Reserved
Target Address for Non-Existent Entity," p. 26, line 28:

> An address describing an emitted instance that was removed, if
> retained in its defining description, uses the reserved target
> address, subject to the rules of the containing construct. A
> surviving replacement does not substitute for the removed instance.

This strengthens the existing permissive removal note for these
retained addresses; removing the description remains possible. It
covers defining variable location addresses and subprogram
`DW_AT_low_pc`/`DW_AT_high_pc`/ `DW_AT_ranges`/`DW_AT_entry_pc`
descriptions, with the existing range rules in Sections 2.16 (pp.
35-39) and 2.17 (p. 40).

2. Add "Static linkage binding" near Section 2.12.2 (p. 34, after line
   9; final placement and numbering at the editor's discretion):

> A concrete `DW_TAG_variable` definition with fixed storage or a
> concrete out-of-line `DW_TAG_subprogram` definition may have a
> `DW_AT_selected_address` attribute of class address. Its value is
> the starting storage address or executable entry address,
> respectively, of the definition to which static linkage binds
> references to that symbol. The reserved target address indicates
> that no such instance survives in the output.
>
> This attribute describes binding; it does not replace the entry's
> own location, code ranges or entry address. Its absence makes no
> binding assertion. It is not inherited through `DW_AT_specification`
> or `DW_AT_abstract_origin`.

Name and code are provisional. Add the attribute to Table 2.2 (p. 17
ff.), Table 8.5 (p. 238 ff.) and the variable/subprogram entries in
Appendix A. Add inheritance exceptions in Sections 2.12.2 (p. 34),
4.3.8.1 (p. 106, lines 8-15) and 7.1.1.1 (p. 164, lines 22-26).

3. Add guidance after Section 8.3.1, "Relocatable Object Files," p.
   214, lines 9-13:

*Supporting producers should emit binding addresses for potentially
replaceable weak and COMDAT definitions. Own-instance references
should preserve the association with the original section and position
within it; binding references should follow symbol selection. Distinct
address-table entries are needed when these results can differ.
Partial links should preserve both associations and recorded
non-existence through final links.*

*Existing local/section references and ordinary symbol-address
relocations can carry the two roles. Debug references to discarded
code already receive special treatment in linkers [4].*

Review note: The relationship between the general ELF group-reference
wording [3] and this established debug-reference treatment merits
review. This proposal requests no gABI amendment and has no such
prerequisite. Existing address forms and `.debug_addr` support the new
attribute; no new selection-state relocation or change to the
constant/flag note on p. 216, line 4, is needed in the tested x86-64
implementation.

## Validation and compatibility

Removing only the weak unit's debug information restores data source
attribution without changing checked runtime section addresses, sizes
or contents. A previous Linux Clang/LLD build gave incorrect sources
for 24 of 43 examined weak-data candidates. These observations
establish failures, not prevalence. Ordinary GDB name breakpoints
still enumerate both code instances correctly; the binding failure
concerns particular lookup paths.

A Clang/mold/GDB prototype uses private attribute 0x3f03,
`DW_FORM_addrx` and ordinary `R_X86_64_64` relocations. Tests cover
weak/COMDAT, both link orders, collection, repeated partial linking,
ordinary/Split DWARF and package-only DWP. Updated GDB calls/reads 11
in live sessions and reads 11 from the same crash core, including with
the final ELF symbols removed and with `.gdb_index`. Explicit
weak-file inspection still calls/reads 22. C++ namespace and overload
paths were also tested.

Corrected own locations repair ordinary DATA/ASan attribution without
new attribute support in those consumers. Split DATA additionally
needs a local DWO-traversal fix.

Production patches add/remove 27/0 lines in Clang, 15/2 in mold and
115/9 in GDB, including comments and excluding generated parser
output. Final uncompressed debug sections grew by 100 bytes for six
annotated definitions and 10,384 bytes for 1,024 in a generated
C++/COMDAT sample. Input growth was 244 and 34,960 bytes. These are
restricted prototypes and sample totals, not complete upstream fixes
or production-scale cost estimates.

The common binding defect already affects code and becomes visible for
data after address correction. For an unchanged GDB, however, reading
22 instead of the previous 11 is a real compatibility regression.
Deployment needs consumer support for runtime binding while preserving
explicit instance inspection. Without binding metadata or reliable
external evidence, lookup can remain unresolved; old weak DIEs in
mixed builds can retain B/B attribution ambiguity.

The DWARF 5 experiment is not a safely ignorable vendor extension
under Section 8.1 (p. 213). Adoption needs versioned semantics and
coordinated producer/linker/consumer support; a version number alone
is not a safe rollout mechanism. The deployment boundary remains for
discussion.

An implemented alternative keeps variable `DW_AT_location=B` and adds
an own-instance attribute. It preserves old name evaluation but needs
updates to address consumers and leaves different roles in existing
code/data address attributes. We prefer a common own-instance meaning.
Redirecting code ranges to B would detach them from their original
instructions and subordinate descriptions.

## Dependencies and references

The removal rule builds on issue 200609.1 [2]. Earlier
debug-relocation and tombstone work [4] and LLDB's weak-function
report [5] are related work.

[1] DWARF 6 working draft, July 25 snapshot:
https://snapshots.sourceware.org/dwarfstd/dwarf-spec/2026-07-25_19-12_1785006721/dwarf6-20260725-1911.pdf
[2] Issue 200609.1, reserved target address:
https://dwarfstd.org/issues/200609.1.html
[3] ELF gABI, symbol binding and section groups:
https://gabi.xinuos.com/elf/05-symtab.html
https://gabi.xinuos.com/elf/03-sheader.html#section-groups
[4] Earlier debug-relocation and tombstone work:
https://reviews.llvm.org/D62840
https://reviews.llvm.org/D81784
https://sourceware.org/pipermail/elfutils-devel/2020q2/002722.html
[5] LLDB weak-function expression evaluation:
https://github.com/llvm/llvm-project/issues/189539

Would the committee support these two address roles, and what
migration boundary would make the change acceptable? I would welcome
feedback and a champion. Reproducers, patches and detailed results are
available on request; this proposal is self-contained.

Best regards,

Longjun Luo


More information about the Dwarf-discuss mailing list