[PATCH v2] dwarf: fix inconsistency in type chains traversal [PR125421]
Vineet Gupta
vineet.gupta@linux.dev
Wed Jun 24 16:21:01 GMT 2026
On 6/24/26 3:47 AM, Richard Biener wrote:
> On Wed, Jun 24, 2026 at 1:31 AM Vineet Gupta <vineet.gupta@linux.dev> 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. Pushed !
-Vineet
>
> 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 <vineet.gupta@linux.dev>
>> ---
>> 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
>>
More information about the bpf
mailing list