https://sourceware.org/bugzilla/show_bug.cgi?id=34960
Bug ID: 34960
Summary: GNU ld -z start-stop-gc retains the first SECNAME
input section
Product: binutils
Version: 2.48 (HEAD)
Status: UNCONFIRMED
Severity: normal
Priority: P2
Component: ld
Assignee: unassigned at sourceware dot org
Reporter: basavesh.shivakumar at wsu dot edu
Target Milestone: ---
Created attachment 17252
--> https://sourceware.org/bugzilla/attachment.cgi?id=17252&action=edit
repro
## Description
GNU ld does not fully apply `-z start-stop-gc` to sections referenced through
synthesized `__start_SECNAME` and `__stop_SECNAME` symbols. With
`--gc-sections`, it retains the first matching input section while collecting
the rest.
The GNU ld documentation says that `-z start-stop-gc` disables the normal
retention caused by synthesized start/stop symbols, allowing the sections to
be garbage collected as if those symbols were not defined. In a five-object
registration list, the documented result is therefore an empty list. GNU ld
2.46 instead produces a one-entry list containing the first input section in
command-line order. LLD 21.1.8 leaves the list empty with the same
Clang-produced objects.
The behavior appears to come from `_bfd_elf_gc_mark_rsec` in
`bfd/elflink.c`. The `start-stop-gc` exception returns `NULL` only while the
synthesized symbol has not already been marked. A later relocation against the
same `__start` symbol falls through to ordinary symbol marking and retains the
input section that defines the synthesized symbol, which is the first matching
section.
The attached standalone x86-64 reproducer demonstrates both the retention
error and its constant-time consequence. An empty `ctprov` list selects a
provider that always reads `safe_table[0]`. The unexpectedly retained first
entry selects a provider that returns the same value, 42, while reading
`table[0]` or `table[16]` according to one input bit. GNU ld 2.46 creates an
eight-byte `__start_ctprov`-to-`__stop_ctprov` interval and selects the
registered provider. The two executions have equal outputs and instruction
traces but read `table+0` and `table+0x40`. LLD leaves the list empty, selects
the fixed-address provider, and reads `safe_table+0` for both inputs.
Binutils master commit `b89169e9d45` (GNU ld snapshot 2.47.50.20261020)
reproduced the result. An earlier head build at
`ab7102f76c0831ad46773cb5fd073c20f9f65297` was built with ASan and UBSan,
reproduced the result, and produced no sanitizer diagnostic. I also fetched
and built current Binutils master commit
`f735688fcb997db4821ed6f9df40425b31b0cd0c` from the official repository. The
resulting GNU ld 2.47.50.20261105 reproduced the same one-entry retention and
provider-address divergence. The exact `ld-new` binary had SHA-256
`953add0461db43a5ee8b5fd8047f6020ffcb7737ce58c74c7991b024d869468f`.
This is a semantic garbage-collection error; no memory-safety claim is made.
Reproduction:
```sh
tar -xzf bfd-start-stop-gc-ct-provider-substitution-bugzilla.tar.gz
cd artifact
./repro.sh
```
The script requires an x86-64 Linux system with Clang, GNU ld, `nm`, Valgrind,
and Python 3. Set `BFD_HEAD=/path/to/ld-new` to test another GNU ld build. If
LLD is installed, the script runs the empty-list comparison automatically. It
compiles each input section separately, links them in a fixed order, executes
both input values under Valgrind Lackey, and checks list cardinality, provider
selection, output, instruction trace, and exact addresses. It finishes with:
```text
PASS: GNU ld retains one ctprov entry; empty-table controls use fixed
addressing
```
The 64-byte spacing and secret-dependent provider are diagnostic fixtures. The
reproducer establishes that partial retention can silently change a
registration table's selected implementation and its architectural
memory-access behavior; it does not claim measured cache timing or key
recovery.
--
You are receiving this mail because:
You are on the CC list for the bug.