[PATCH] dwarf: fix inconsistency in type chains traversal [PR125421]

Vineet Gupta vineet.gupta@linux.dev
Mon Jun 22 21:36:03 GMT 2026


On 6/22/26 5:19 AM, Richard Biener wrote:
> On Wed, Jun 17, 2026 at 7:56 PM Vineet Gupta <vineet.gupta@linux.dev> 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 <vineet.gupta@linux.dev>
>>> ---
>>>    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;
+
           /* 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 } } */



More information about the bpf mailing list