https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128622
Bug ID: 128622
Summary: [LTO] Function-local static missing from info locals
when its definition and function body come from
different TUs
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: debug
Assignee: unassigned at gcc dot gnu.org
Reporter: luolongjuna at gmail dot com
Target Milestone: ---
In the two-TU testcase below, LTO selects the prevailing definition of a
function-local static (transform(int)::x) from a.o and the out-of-line function
body (transform(int)) from b.o. Stopping inside transform, GDB can evaluate
'print x', but omits x from 'info locals' and from GDB/MI '-stack-list-locals'
/ '-stack-list-variables'. Building with -fno-lto lists x in the active scope.
Reproduced on x86_64 GNU/Linux with GCC 17.0.0 (g:bd921def14a4), GDB 17.2 and
binutils 2.46.1, and with Ubuntu GCC 13.3.0.
GCC 17 build configuration:
--enable-languages=c,c++ --disable-bootstrap --disable-multilib
--disable-nls --disable-werror --enable-checking=yes
--without-isl --with-system-zlib
Both inputs use -O2 -g -gdwarf-5 -flto, and a.C adds -fno-strict-aliasing.
During LGEN, a.C inlines transform and retains its static x, but no out-of-line
transform definition. b.C takes transform's address and retains both
definitions. With link order a.o b.o and -flto-partition=one, repro.res
records:
_ZZ9transformiE1x a.o transform(int)::x
_Z9transformi b.o transform(int)
Stopping at h.h:6 in the out-of-line transform:
With -flto: With -fno-lto:
(gdb) info locals (gdb) info locals
result = 10 result = 10
(gdb) print x x = 10
$1 = 10 (gdb) print x
$1 = 10
In late DWARF, the address-bearing DIE for x is attached to a separate
rangeless subprogram stub rather than to the executing function DIE (arrows
denote DW_AT_abstract_origin):
DW_TAG_subprogram [code range] -> b.C's early transform
DW_TAG_formal_parameter (n) -> b.C's n
DW_TAG_variable (result) -> b.C's result
DW_TAG_subprogram [no code range] -> a.C's early transform
DW_TAG_variable (x) [DW_OP_addr] -> a.C's x
With different per-TU options, SCC merging keeps the two function declarations
distinct. free_lang_data removes x from BLOCK_VARS, and its DECL_CONTEXT
remains a.C's transform. flush_limbo_die_list attaches x's late DIE to that
function, which has no selected body. The resulting subprogram DIE has no code
range and is skipped by GDB. This is the cross-TU counterpart of the
DECL_CONTEXT placement issue in PR124864 comment 5.
Varying link order vs. per-TU options separates symbol selection from
declaration merging:
Link / compile configuration Prevailing (fn / x) Fn decls merge
GDB in out-of-line transform
--------------------------------------- ------------------- --------------
-------------------------------------------------
a.o b.o (a.o with -fno-strict-aliasing) b.o / a.o no
info locals omits x (this PR)
b.o a.o (a.o with -fno-strict-aliasing) b.o / b.o no
info locals lists result = 10, x = 10
a.o b.o (matching options on both) b.o / a.o yes
x = 10 listed; n and result duplicated (PR128616)
Reproducer
--- h.h ---
inline int transform (int n)
{
static volatile int x = 7;
int result = x + n;
x = result;
asm volatile ("nop" : "+r" (result) : : "memory");
return result;
}
__attribute__((noipa)) int local_value (int);
--- a.C ---
#include "h.h"
int local_value (int n) { return transform (n); }
--- b.C ---
#include "h.h"
int (*volatile pointer) (int) = transform;
int main ()
{
int a = transform (1);
int b = pointer (2);
int c = local_value (3);
return a + b + c != 31;
}
--- run.sh ---
#!/bin/sh
set -eu
export LC_ALL=C
mode=${1:--flto}
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" -fno-strict-aliasing -c a.C -o a.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" -c b.C -o b.o
"${CXX:-g++}" -O2 -g -gdwarf-5 "$mode" -flto-partition=one \
-save-temps -no-pie a.o b.o -o repro
"${GDB:-gdb}" -q -nx -batch -iex 'set debuginfod enabled off' \
-ex 'break *_Z9transformi' -ex run \
-ex 'advance h.h:6' \
-ex 'info args' -ex 'info locals' -ex 'print x' repro
Run with:
CXX=g++ GDB=gdb sh run.sh -flto
readelf --debug-dump=info repro
cat repro.res
CXX=g++ GDB=gdb sh run.sh -fno-lto