https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128617
Bug ID: 128617
Summary: [LTO] Mixed -g0/-g inputs produce inconsistent or
missing abstract origins
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: ---
The following two mixed -g0/-g LTO testcases produce duplicate parameter and
local-variable descriptions, or an inline DIE without an abstract origin.
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
A. Borrowed function DIE and duplicate children
With -flto-partition=one and link order a.o b.o, repro.res records a.o as the
prevailing definition of _Z9transformi. Stopping at h.h:4 in the out-of-line
transform, 'info args' and 'info locals' print:
n = <optimized out>
n = <optimized out>
result = 4
result = <optimized out>
As in PR128616, the concrete parameter DIE has a valid location and its GDB
symbol evaluates to n=1; in GDB 17.2, 'info args' looks each argument up by
name and finds the inherited parameter, which has no location.
This is the -g0 counterpart of PR128616: during SCC merging, process_dref
supplies the surviving -g0 declaration with b.C's early DIE reference, while
the concrete children from a.o have no early DIEs of their own:
concrete transform -> early transform in b.C's CU
parameter n -> name + location, no DW_AT_abstract_origin
variable result -> name + location, no DW_AT_abstract_origin
Because the concrete children have no matching origins, GDB also inherits
another set of parameter and variable descriptions from b.C. With b.o before
a.o (which selects b.o's body), all origins resolve to b.C. Compiling both
inputs with -g keeps a.o's body and resolves all origins to a.C. Either way,
GDB prints n=1 and result=4 once.
B. Inline instance without DW_AT_abstract_origin
With -flto-partition=1to1, helper from a.C (-g0) is inlined into b.C's main
(-g) during LTRANS. gen_inlined_subroutine_die creates the inline entry even
when add_abstract_origin_attribute cannot add an origin reference:
DW_TAG_inlined_subroutine: code range, call coords, no name or
DW_AT_abstract_origin
DW_TAG_formal_parameter: no attributes
DW_TAG_variable: DW_AT_location only, no name or
DW_AT_abstract_origin
This DW_TAG_inlined_subroutine lacks the reference to an abstract instance
required by DWARF 5 section 3.3.8.2. The inline instance should either be
omitted or refer to a valid abstract instance. Stopping at the inlined asm
(a.C:4) with one argument, GDB shows:
#0 main (argc=2) at a.C:4
argc = 2
No locals.
Compiling both inputs with -g restores the inline frame with n=2 and value=5.
PR128061 hits the same emission path without LTO on an ignored C++ TLS wrapper
with an empty range; here the entry covers real code and has children,
including a variable with a location.
Reproducers
Save Case A and Case B in separate directories. Each run.sh compiles a.C with
-g0 by default; pass -g to compile both TUs with debug info.
=== Case A ===
--- h.h ---
inline __attribute__((noinline, noclone)) int transform (int n)
{
int result = n + 3;
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 main ()
{
int a = transform (1);
int b = local_value (2);
return a + b != 9;
}
--- run.sh ---
#!/bin/sh
set -eu
export LC_ALL=C
a_debug=${1:--g0}
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto "$a_debug" -c a.C -o a.o
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -c b.C -o b.o
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -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 h.h:4' -ex run \
-ex 'info args' -ex 'info locals' repro
=== Case B ===
--- a.C ---
int helper (int n)
{
int value = n + 3;
asm volatile ("nop" : "+r" (value) : : "memory");
return value;
}
--- b.C ---
int helper (int);
int main (int argc, char **)
{
if (__builtin_expect (argc > 1, 0))
return helper (argc);
return 0;
}
--- run.sh ---
#!/bin/sh
set -eu
export LC_ALL=C
a_debug=${1:--g0}
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto "$a_debug" -c a.C -o a.o
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -c b.C -o b.o
"${CXX:-g++}" -O2 -g -gdwarf-5 -flto -flto-partition=1to1 \
-save-temps -no-pie a.o b.o -o repro
"${GDB:-gdb}" -q -nx -batch -iex 'set debuginfod enabled off' \
-ex 'break a.C:4' -ex 'run probe' \
-ex 'bt 3' -ex 'info args' -ex 'info locals' repro
Run in each directory:
CXX=g++ GDB=gdb sh run.sh
readelf --debug-dump=info repro
CXX=g++ GDB=gdb sh run.sh -g