On Wed, Jun 24, 2026 at 1:31 AM Vineet Gupta <[email protected]> wrote: > > --- > Changes since v1: > - Richi's suggestion to not modify qualified_type in-place so follow on > code-paths are not inadvertently affected. > --- > > typedef chain traversal could sometimes skip an intermediate type. > This could happen when two DIEs have a common underlying type but diverge > due to presence of additional attribute in one case and a qualifier in > other. > In the example from testcase, variable "perm" typechain misses interim u16. > > typedef unsigned short __u16; > typedef __u16 u16; > __attribute__((btf_type_tag(""))) u16 a; > const u16 perm; > > generates > > .uleb128 0x1 # (DIE (0x66) DW_TAG_variable) > .long .LASF3 # DW_AT_name: "perm" > # DW_AT_decl_file (1, pr125421.c) > .byte 0xa # DW_AT_decl_line > .byte 0xb # DW_AT_decl_column > .long 0x2f # DW_AT_type > > .uleb128 0x4 # (DIE (0x2f) DW_TAG_const_type) > .long 0x23 # DW_AT_type <-- BUG > > .uleb128 0x3 # (DIE (0x23) DW_TAG_typedef) > .long .LASF5 # DW_AT_name: "__u16" > .byte 0x1 # DW_AT_decl_file (pr125421.c) > .byte 0x7 # DW_AT_decl_line > .byte 0xf # DW_AT_decl_column > .long 0x34 # DW_AT_type > > .uleb128 0x5 # (DIE (0x34) DW_TAG_base_type) > .byte 0x2 # DW_AT_byte_size > .byte 0x5 # DW_AT_encoding > .long .LASF6 # DW_AT_name: "short int" > > What makes this issue worse is depending on the order in which the types > are specified in source, and processed, the problem may or maynot show up. > So in the test code above if lines 3 and 4 are swppaed, "perm" gets the > missing type u16 as expected. > > The issue is in modified_type_die (), get_qualified_type (type) returned > pointer may not be identical to dtype: TREE_TYPE (TYPE_NAME (qualified_type)) > while having the same underlying base type. And due to the failed > pointer identity test, subsequent usage of DECL_ORIGINAL_TYPE () for recursive > chain processing can peel away the needed type. > > The implications are for a multi CU build, structurally similar but > non identical variants of types (due to an embedded member getting a > different chained typdef) can be generated and accumulate at link time > in the final .debug_info. > > Fix is to handle qualified_type != dtype as if they were identical when > they are different variant nodes of the same underlying typedef. > > This would be OK / unnoticed for usual dwarf debugging purposes. > However in BFP workflow: kernel binary linking invokes pahole to process > the dwarf and dedup it for btf generation (gcc can emit btf directly but > thats not been done currently for other reasons). The slightly different > variations of same structure due to this issue causes pahole dedup to > go into combinatiorial explosion of trying to match and failing repeatedly > in what seems like infinite recursion. The problem showed up and was > excerbated > when enabling support for btf_type_tag in kernel (with gcc) for the first > time which landed reently in gcc-16. > > Bootstrapped and regtested on x86 and aarch64.
LGTM. Thanks, Richard. > PR debug/125421 > > gcc/ChangeLog: > > * dwarf2out.cc (modified_type_die): Handle qualified_type being > a variant of dtype as if they were identical to avoid peeling a > typdef. > For named types, use dentry unconditionally as that is valid for > both cases of qualified_type == and != dentry. > > gcc/testsuite/ChangeLog: > > * gcc.dg/debug/dwarf2/pr125421.c: New Test. > > Signed-off-by: Vineet Gupta <[email protected]> > --- > gcc/dwarf2out.cc | 15 ++++++++++++--- > gcc/testsuite/gcc.dg/debug/dwarf2/pr125421.c | 18 ++++++++++++++++++ > 2 files changed, 30 insertions(+), 3 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/debug/dwarf2/pr125421.c > > diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc > index 32aa3adefd7b..7ba18f6f57c2 100644 > --- a/gcc/dwarf2out.cc > +++ b/gcc/dwarf2out.cc > @@ -13997,7 +13997,16 @@ modified_type_die (tree type, int cv_quals, tree > type_attrs, bool reverse, > tree dtype = TREE_TYPE (name); > > /* Skip the typedef for base types with DW_AT_endianity, no big deal. > */ > - if (qualified_type == dtype && !reverse_type) > + if (!reverse_type > + && (qualified_type == dtype > + /* Pointer identity check above might fail when qualified_type > + is a different variant node of the same typedef yet requires > + the same handling as if they matched (see PR/125421). > + Skip this when btf_type_tag attributes are present, as those > + need to be handled in the else branch below. */ > + || (TYPE_NAME (qualified_type) == name > + && TYPE_QUALS (qualified_type) == TYPE_QUALS (dtype) > + && !lookup_attribute ("btf_type_tag", type_attrs)))) > { > tree origin = decl_ultimate_origin (name); > > @@ -14009,8 +14018,8 @@ modified_type_die (tree type, int cv_quals, tree > type_attrs, bool reverse, > reverse, context_die); > > /* For a named type, use the typedef. */ > - gen_type_die (qualified_type, context_die); > - return lookup_type_die (qualified_type); > + gen_type_die (dtype, context_die); > + return lookup_type_die (dtype); > } > else > { > diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/pr125421.c > b/gcc/testsuite/gcc.dg/debug/dwarf2/pr125421.c > new file mode 100644 > index 000000000000..810afeae7394 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/pr125421.c > @@ -0,0 +1,18 @@ > +/* Verify the typedef chains are preserved. > + DW_TAG_const_type for "const u16" chains to u16 typedef and doesnot > + skip to underlying __u16 typedef */ > + > +/* { dg-do compile } */ > +/* { dg-options "-gdwarf -dA" } */ > + > +typedef short __u16; > +typedef __u16 u16; > +__attribute__((btf_type_tag(""))) u16 __softirq_pending; > +const u16 perm; > + > +/* The exact failing pattern is hard to encode with TCL regex machinery, > + so resort to an indirect way: in the buggy output, const_type points > + directly to __u16, and only one u16 typedef DIE is emitted (for the > + btf_type_tag use). In the fixed output, const_type points to a u16 > + typedef, creating a second u16 typedef DIE. > +/* { dg-final { scan-assembler-times "(DW_AT_name: > \"u16\"|\"u16..\"\[^\\r\\n\]*DW_AT_name)" 2 } } */ > -- > 2.54.0 >
