On Wed, Jun 17, 2026 at 7:56 PM Vineet Gupta <[email protected]> wrote:
>
> Ping !
>
> FWIW this also fixes (dup) PR/110439 originally reported in 2023.
>
> Thx,
> -Vineet
>
> On 5/26/26 9:51 AM, Vineet Gupta wrote:
> > 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.
> >
> > 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 thi issue cause combinatiorial explosion
> > in pahole dedup processing trying to match and failing repeatedly in what
> > seems like infinite recursion. The problem showed up and was excerbated
> > when trying to enable attr btf_type_tag in kernel with gcc for the first
> > time as that is the key ingredient to trigger the latent issue.
> >
> > Fix is to normalizing qualified_type to dtype when they are different
> > variant nodes of the same underlying typedef.
> >
> > Bootstrapped and regtested on x86 and aarch64.
> >
> >       PR debug/125421
> >
> > gcc/ChangeLog:
> >
> >       * dwarf2out.cc (modified_type_die): Normalize qualified_type to
> >       dtype if it is different variant of same typedef.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * gcc.dg/debug/dwarf2/pr125421.c: New Test.
> >
> > Signed-off-by: Vineet Gupta <[email protected]>
> > ---
> >   gcc/dwarf2out.cc                             | 12 ++++++++++++
> >   gcc/testsuite/gcc.dg/debug/dwarf2/pr125421.c | 18 ++++++++++++++++++
> >   2 files changed, 30 insertions(+)
> >   create mode 100644 gcc/testsuite/gcc.dg/debug/dwarf2/pr125421.c
> >
> > diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> > index bf342e468bf9..7b5197f270c2 100644
> > --- a/gcc/dwarf2out.cc
> > +++ b/gcc/dwarf2out.cc
> > @@ -13996,6 +13996,18 @@ modified_type_die (tree type, int cv_quals, tree 
> > type_attrs, bool reverse,
> >       {
> >         tree dtype = TREE_TYPE (name);
> >
> > +      /* If qualified_type is a different variant node of the same typedef,
> > +      use dtype to preserve the typedef identity in DWARF output.
> > +      Without this, get_qualified_type may return a variant that fails
> > +      the pointer comparison below, causing the typedef to be stripped.
> > +      Skip this when btf_type_tag attributes are present, as those need
> > +      to be handled in the else branch below.  */
> > +      if (qualified_type != dtype
> > +       && TYPE_NAME (qualified_type) == name
> > +       && TYPE_QUALS (qualified_type) == TYPE_QUALS (dtype)
> > +       && !lookup_attribute ("btf_type_tag", type_attrs))
> > +     qualified_type = dtype;
> > +
> >         /* Skip the typedef for base types with DW_AT_endianity, no big 
> > deal.  */
> >         if (qualified_type == dtype && !reverse_type)
> >       {

qualified_type is used in this comparison and then at the end of the
if (){} with

          /* For a named type, use the typedef.  */
          gen_type_die (qualified_type, context_die);
          return lookup_type_die (qualified_type);

is it important that on the path that does not return from the else {}
qualified_type is
adjusted as well?  I'd rather keep the changes in this fragile code
localized.  So how
about factoring the condition you make qualified_type = dtype into the if ()
condition and using 'dtype' in its body?

Thanks,
Richard.

> > 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 } } */
>

Reply via email to