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

Longjun Luo luolongjuna@gmail.com
Tue Sep 22 04:34:42 GMT 2026


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