On Mon, Jun 22, 2026 at 11:36 PM Vineet Gupta <[email protected]> wrote:
>
> On 6/22/26 5:19 AM, Richard Biener wrote:
> > 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?
>
> And then duplicate the entire if block under that using dtype ; I would
> personally prefer non duplication and conditionalize the usage of
> qualified_type vs. dtype (untest but just to get your preference)
>
> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> index bf342e468bf9..0b04f009570c 100644
> --- a/gcc/dwarf2out.cc
> +++ b/gcc/dwarf2out.cc
> @@ -13996,8 +13996,14 @@ modified_type_die (tree type, int cv_quals,
> tree type_attrs, bool reverse,
>       {
>         tree dtype = TREE_TYPE (name);
>
> +      bool typedef_variant
> +       = qualified_type != dtype
> +         && TYPE_NAME (qualified_type) == name
> +         && TYPE_QUALS (qualified_type) == TYPE_QUALS (dtype)
> +         && !lookup_attribute ("btf_type_tag", type_attrs);
> +
>         /* Skip the typedef for base types with DW_AT_endianity, no big
> deal.  */
> -      if (qualified_type == dtype && !reverse_type)
> +      if ((qualified_type == dtype || typedef_variant) && !reverse_type)
>          {
>            tree origin = decl_ultimate_origin (name);
>
> @@ -14008,9 +14014,11 @@ modified_type_die (tree type, int cv_quals,
> tree type_attrs, bool reverse,
>              return modified_type_die (TREE_TYPE (origin), cv_quals,
> type_attrs,
>                                        reverse, context_die);
>
> +         tree qualified_type_alt = typedef_variant ? dtype :
> qualified_type;

just use 'dtype' unconditionally?  Iff !typedef_variant then
qualified_type == dtype.
I'd also fold the typedef_variant bool check into the if condition itself, so

   if (!reverse_type
       && (qualified_type == dtype
             /* Add comment here */
              || (TYPE_NAME (qualified_type) == name
             && TYPE_QUALS (qualified_type) == TYPE_QUALS (dtype)
             && !lookup_attribute ("btf_type_tag", type_attrs))))
     {
  ...
     }

The comment should explain why we exempt 'btf_type_tag' attribute qualified
types and only those.

Richard.

> +
>            /* For a named type, use the typedef.  */
> -         gen_type_die (qualified_type, context_die);
> -         return lookup_type_die (qualified_type);
> +         gen_type_die (qualified_type_alt, context_die);
> +         return lookup_type_die (qualified_type_alt);
>
> Thx,
> -Vineet
>
>
> >
> > 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