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
  • [Bug debug/128617] New: [LTO] Mi... luolongjuna at gmail dot com via Gcc-bugs

Reply via email to