[Bug symtab/34464] New: GCC binaries in -fPIE -ffunction-sections --gc-sections have DWARF subprograms overlapping main, breaking function resolution

nyanpasu64 at tuta dot io via Gdb-prs <[email protected]> Thu, 30 Jul 2026 06:09:04 +0000
Newsgroups gmane.comp.gdb.bugs.discuss
Message-ID <[email protected]/bugzilla/>
https://sourceware.org/bugzilla/show_bug.cgi?id=3D34464

            Bug ID: 34464
           Summary: GCC binaries in -fPIE -ffunction-sections
                    --gc-sections have DWARF subprograms overlapping main,
                    breaking function resolution
           Product: gdb
           Version: 17.2
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: symtab
          Assignee: unassigned at sourceware dot org
          Reporter: nyanpasu64 at tuta dot io
  Target Milestone: ---

Created attachment 16895
  --> https://sourceware.org/bugzilla/attachment.cgi?id=3D16895&action=3Ded=
it
Example binary showing this error, as well as a gdb log of reproducing this=
 bug
on Linux ARM64

When I build a binary in -ffunction-sections, and link it with --gc-section=
s,
unused functions have their .text sections dropped from the resulting progr=
am.
However the removed functions' DW_TAG_subprogram DIEs are retained, with th=
eir
DW_AT_low_pc set to 0x0 but DW_AT_high_pc distances left untouched. If the
binary is -fPIE, then actual functions (like main) can have their base-rela=
tive
PCs low enough to overlap with the ranges of stub functions.

I found that when unmodified Arch Linux GCC remote-debugs a binary on Ninte=
ndo
Switch using Atmosphere's debug stub, `bt` can receive the correct return
address from the unwinder, but output a function name and signature from a
"ghost" function (breaking stack unwinding, though you can't really unwind =
past
main anyway). I wrote a Switch-specific custom stack unwinder returning the
correct PC, but this did not improve name resolution. `info sym 0x...` retu=
rned
the actual function and offset, and `info line *0x...` returned select (a g=
c'd
function at offset 0 whose size overlaps with main's offset).

I've tried to reproduce it on x86-64, and got the same symptom of overlappi=
ng
subprograms as seen in dwex (which resolves main's addresses to multiple
functions), but gcc did not produce the same symptoms there. I tried tracing
gcc's internals but could not work out how address symbol loading worked and
where the two files diverged in parsing.

In the meantime I've written a workaround at
https://codeberg.org/nyanpasu64/patch-dwarf, which could be useful to
understand the triggers for the bug.

### Reproducing on ARM64 Linux

I've attached the binary I discovered this bug on (built from
https://github.com/nyanpasu64/sys-ftpd/tree/906c114a1613).

In an effort to reproduce this bug in a known-broken environment, I copied =
this
.elf file (not Switch binary) to my Switch's Ubuntu 24.04 installation. I r=
an
gdb on this binary, which expectedly crashed with a SIGILL on startup. dwex
shows that `main` lives at a base-relative offset of 0x180. `maintenance in=
fo
sections` showed that .text began at 0x7fb7f10000.

Bizarrely, this indicated that gdb has *non-deterministic* path-dependent
symbol resolution. When I ran `info line *0x7fb7f10180` (the address of mai=
n),
it correctly showed main. But after running `info line *0x7fb7f10300`, gdb
loaded in the debug info of a *new* GCed function `ini_puts` not previously
loaded, corrupting its symbol cache, and suddenly `info line *0x7fb7f10180`
(the same command as before) was reporting `ini_puts` rather than main!

- I was unfortunately unable to reproduce this phenomenon on my x86 binary.
With the Switch ELF on Ubuntu ARM, `p ini_puts` returns the starting addres=
s of
.text, but `p unused11` on x86 says "No symbol "unused11" in current contex=
t.",
despite unused11 existing in its DWARF info.
- Upon another gdb session, I *could* `p ini_puts` and `info line
*0x7fb7f10300`, but it did not break resolution of `main`. Yippee emergent
behavior...
- I've attached the exact sequence of commands I typed as "gdb symbol
resolution.txt". In practice I can reliably reproduce this bug through `info
line *0x7fb7f10300` then `info line *0x7fb7f10180`. If I don't `run` the
process first, I can reproduce it through `info line *0x180` then `info line
*0x300`.
  - One behavior of note is that if I `p main` before `info line *0x300`, t=
hen
`info line *0x180` is incorrect. If I load 0x300 *before* main, then `info =
line
*0x180` correctly resolves to main. So it seems there's a "last loaded symb=
ol
wins" effect going on.

----

This bug seems to have the same root cause as Bug 13415, but I'm running in=
to
different problems from this case. I believe the current specification at
https://dwarfstd.org/issues/200609.1.html is to set a DW_AT_low_pc of
(unsigned)-1 =3D 0x...FFFF, though GNU LD fails to follow this suggestion a=
nd
outputs 0 as mentioned. (I've heard some discussion that (unsigned)-2 =3D
0x...FFFE may be needed to avoid colliding with range base commands of some
sort, eg. https://reviews.llvm.org/D59553.)

IMO GNU ld should be fixed to output 0x...FFFF for removed functions. But it
would also help to fix gdb to ignore (or at least deprioritize as
possibly-invalid) functions located at DWARF offset 0, and perhaps outright
*discard* any DWARF sections at offset 0 with no corresponding ELF symbol
(would this break _start? IDK).

--=20
You are receiving this mail because:
You are on the CC list for the bug.=