[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