Skip to content

Commit ef2c0b2

Browse files
committed
Merge branch 'libbpf-fix-btf-dedup-to-support-recursive-typedef'
Paul Houssel says: ==================== libbpf: fix BTF dedup to support recursive typedef Pahole fails to encode BTF for some Go projects (e.g. Kubernetes and Podman) due to recursive type definitions that create reference loops not representable in C. These recursive typedefs trigger a failure in the BTF deduplication algorithm. This patch extends btf_dedup_struct_types() to properly handle potential recursion for BTF_KIND_TYPEDEF, similar to how recursion is already handled for BTF_KIND_STRUCT. This allows pahole to successfully generate BTF for Go binaries using recursive types without impacting existing C-based workflows. Changes in v4: fix typo found by Claude-based CI Changes in v3: 1. Patch 1: Adjusted the comment of btf_dedup_ref_type() to refer to typedef as well. 2. Patch 2: Update of the "dedup: recursive typedef" test to include a duplicated version of the types to make sure deduplication still happens in this case. Changes in v2: 1. Patch 1: Refactored code to prevent copying existing logic. Instead of adding a new function we modify the existing btf_dedup_struct_type() function to handle the BTF_KIND_TYPEDEF case. Calls to btf_hash_struct() and btf_shallow_equal_struct() are replaced with calls to functions that select btf_hash_struct() / btf_hash_typedef() based on the type. 2. Patch 2: Added tests v3: https://lore.kernel.org/lkml/[email protected]/ v2: https://lore.kernel.org/lkml/[email protected]/ v1: https://lore.kernel.org/lkml/[email protected]/ ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Andrii Nakryiko <[email protected]>
2 parents c133390 + a69e098 commit ef2c0b2

File tree

2 files changed

+120
-16
lines changed
  • tools

2 files changed

+120
-16
lines changed

tools/lib/bpf/btf.c

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3901,6 +3901,20 @@ static int btf_dedup_strings(struct btf_dedup *d)
39013901
return err;
39023902
}
39033903

3904+
/*
3905+
* Calculate type signature hash of TYPEDEF, ignoring referenced type IDs,
3906+
* as referenced type IDs equivalence is established separately during type
3907+
* graph equivalence check algorithm.
3908+
*/
3909+
static long btf_hash_typedef(struct btf_type *t)
3910+
{
3911+
long h;
3912+
3913+
h = hash_combine(0, t->name_off);
3914+
h = hash_combine(h, t->info);
3915+
return h;
3916+
}
3917+
39043918
static long btf_hash_common(struct btf_type *t)
39053919
{
39063920
long h;
@@ -3918,6 +3932,13 @@ static bool btf_equal_common(struct btf_type *t1, struct btf_type *t2)
39183932
t1->size == t2->size;
39193933
}
39203934

3935+
/* Check structural compatibility of two TYPEDEF. */
3936+
static bool btf_equal_typedef(struct btf_type *t1, struct btf_type *t2)
3937+
{
3938+
return t1->name_off == t2->name_off &&
3939+
t1->info == t2->info;
3940+
}
3941+
39213942
/* Calculate type signature hash of INT or TAG. */
39223943
static long btf_hash_int_decl_tag(struct btf_type *t)
39233944
{
@@ -4844,13 +4865,30 @@ static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
48444865
}
48454866
}
48464867

4868+
static inline long btf_hash_by_kind(struct btf_type *t, __u16 kind)
4869+
{
4870+
if (kind == BTF_KIND_TYPEDEF)
4871+
return btf_hash_typedef(t);
4872+
else
4873+
return btf_hash_struct(t);
4874+
}
4875+
4876+
static inline bool btf_equal_by_kind(struct btf_type *t1, struct btf_type *t2, __u16 kind)
4877+
{
4878+
if (kind == BTF_KIND_TYPEDEF)
4879+
return btf_equal_typedef(t1, t2);
4880+
else
4881+
return btf_shallow_equal_struct(t1, t2);
4882+
}
4883+
48474884
/*
4848-
* Deduplicate struct/union types.
4885+
* Deduplicate struct/union and typedef types.
48494886
*
48504887
* For each struct/union type its type signature hash is calculated, taking
48514888
* into account type's name, size, number, order and names of fields, but
48524889
* ignoring type ID's referenced from fields, because they might not be deduped
4853-
* completely until after reference types deduplication phase. This type hash
4890+
* completely until after reference types deduplication phase. For each typedef
4891+
* type, the hash is computed based on the type’s name and size. This type hash
48544892
* is used to iterate over all potential canonical types, sharing same hash.
48554893
* For each canonical candidate we check whether type graphs that they form
48564894
* (through referenced types in fields and so on) are equivalent using algorithm
@@ -4882,26 +4920,28 @@ static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id)
48824920
t = btf_type_by_id(d->btf, type_id);
48834921
kind = btf_kind(t);
48844922

4885-
if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION)
4923+
if (kind != BTF_KIND_STRUCT &&
4924+
kind != BTF_KIND_UNION &&
4925+
kind != BTF_KIND_TYPEDEF)
48864926
return 0;
48874927

4888-
h = btf_hash_struct(t);
4928+
h = btf_hash_by_kind(t, kind);
48894929
for_each_dedup_cand(d, hash_entry, h) {
48904930
__u32 cand_id = hash_entry->value;
48914931
int eq;
48924932

48934933
/*
48944934
* Even though btf_dedup_is_equiv() checks for
4895-
* btf_shallow_equal_struct() internally when checking two
4896-
* structs (unions) for equivalence, we need to guard here
4935+
* btf_equal_by_kind() internally when checking two
4936+
* structs (unions) or typedefs for equivalence, we need to guard here
48974937
* from picking matching FWD type as a dedup candidate.
48984938
* This can happen due to hash collision. In such case just
48994939
* relying on btf_dedup_is_equiv() would lead to potentially
49004940
* creating a loop (FWD -> STRUCT and STRUCT -> FWD), because
49014941
* FWD and compatible STRUCT/UNION are considered equivalent.
49024942
*/
49034943
cand_type = btf_type_by_id(d->btf, cand_id);
4904-
if (!btf_shallow_equal_struct(t, cand_type))
4944+
if (!btf_equal_by_kind(t, cand_type, kind))
49054945
continue;
49064946

49074947
btf_dedup_clear_hypot_map(d);
@@ -4939,18 +4979,18 @@ static int btf_dedup_struct_types(struct btf_dedup *d)
49394979
/*
49404980
* Deduplicate reference type.
49414981
*
4942-
* Once all primitive and struct/union types got deduplicated, we can easily
4982+
* Once all primitive, struct/union and typedef types got deduplicated, we can easily
49434983
* deduplicate all other (reference) BTF types. This is done in two steps:
49444984
*
49454985
* 1. Resolve all referenced type IDs into their canonical type IDs. This
4946-
* resolution can be done either immediately for primitive or struct/union types
4947-
* (because they were deduped in previous two phases) or recursively for
4986+
* resolution can be done either immediately for primitive, struct/union, and typedef
4987+
* types (because they were deduped in previous two phases) or recursively for
49484988
* reference types. Recursion will always terminate at either primitive or
4949-
* struct/union type, at which point we can "unwind" chain of reference types
4950-
* one by one. There is no danger of encountering cycles because in C type
4951-
* system the only way to form type cycle is through struct/union, so any chain
4952-
* of reference types, even those taking part in a type cycle, will inevitably
4953-
* reach struct/union at some point.
4989+
* struct/union and typedef types, at which point we can "unwind" chain of reference
4990+
* types one by one. There is no danger of encountering cycles in C, as the only way to
4991+
* form a type cycle is through struct or union types. Go can form such cycles through
4992+
* typedef. Thus, any chain of reference types, even those taking part in a type cycle,
4993+
* will inevitably reach a struct/union or typedef type at some point.
49544994
*
49554995
* 2. Once all referenced type IDs are resolved into canonical ones, BTF type
49564996
* becomes "stable", in the sense that no further deduplication will cause
@@ -4982,7 +5022,6 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
49825022
case BTF_KIND_VOLATILE:
49835023
case BTF_KIND_RESTRICT:
49845024
case BTF_KIND_PTR:
4985-
case BTF_KIND_TYPEDEF:
49865025
case BTF_KIND_FUNC:
49875026
case BTF_KIND_TYPE_TAG:
49885027
ref_type_id = btf_dedup_ref_type(d, t->type);

tools/testing/selftests/bpf/prog_tests/btf.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7495,6 +7495,71 @@ static struct btf_dedup_test dedup_tests[] = {
74957495
BTF_STR_SEC("\0t\0m1\0m2\0tag1\0tag2\0tag3"),
74967496
},
74977497
},
7498+
{
7499+
.descr = "dedup: recursive typedef",
7500+
/*
7501+
* This test simulates a recursive typedef, which in GO is defined as such:
7502+
*
7503+
* type Foo func() Foo
7504+
*
7505+
* In BTF terms, this is represented as a TYPEDEF referencing
7506+
* a FUNC_PROTO that returns the same TYPEDEF.
7507+
*/
7508+
.input = {
7509+
.raw_types = {
7510+
/*
7511+
* [1] typedef Foo -> func() Foo
7512+
* [2] func_proto() -> Foo
7513+
* [3] typedef Foo -> func() Foo
7514+
* [4] func_proto() -> Foo
7515+
*/
7516+
BTF_TYPEDEF_ENC(NAME_NTH(1), 2), /* [1] */
7517+
BTF_FUNC_PROTO_ENC(1, 0), /* [2] */
7518+
BTF_TYPEDEF_ENC(NAME_NTH(1), 4), /* [3] */
7519+
BTF_FUNC_PROTO_ENC(3, 0), /* [4] */
7520+
BTF_END_RAW,
7521+
},
7522+
BTF_STR_SEC("\0Foo"),
7523+
},
7524+
.expect = {
7525+
.raw_types = {
7526+
BTF_TYPEDEF_ENC(NAME_NTH(1), 2), /* [1] */
7527+
BTF_FUNC_PROTO_ENC(1, 0), /* [2] */
7528+
BTF_END_RAW,
7529+
},
7530+
BTF_STR_SEC("\0Foo"),
7531+
},
7532+
},
7533+
{
7534+
.descr = "dedup: typedef",
7535+
/*
7536+
* // CU 1:
7537+
* typedef int foo;
7538+
*
7539+
* // CU 2:
7540+
* typedef int foo;
7541+
*/
7542+
.input = {
7543+
.raw_types = {
7544+
/* CU 1 */
7545+
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
7546+
BTF_TYPEDEF_ENC(NAME_NTH(1), 1), /* [2] */
7547+
/* CU 2 */
7548+
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [3] */
7549+
BTF_TYPEDEF_ENC(NAME_NTH(1), 3), /* [4] */
7550+
BTF_END_RAW,
7551+
},
7552+
BTF_STR_SEC("\0foo"),
7553+
},
7554+
.expect = {
7555+
.raw_types = {
7556+
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
7557+
BTF_TYPEDEF_ENC(NAME_NTH(1), 1), /* [2] */
7558+
BTF_END_RAW,
7559+
},
7560+
BTF_STR_SEC("\0foo"),
7561+
},
7562+
},
74987563
{
74997564
.descr = "dedup: typedef tags",
75007565
.input = {

0 commit comments

Comments
 (0)