Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 49 additions & 10 deletions tools/lib/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -3901,6 +3901,20 @@ static int btf_dedup_strings(struct btf_dedup *d)
return err;
}

/*
* Calculate type signature hash of TYPEDEF, ignoring referenced type IDs,
* as referenced type IDs equivalence is established separately during type
* graph equivalence check algorithm.
*/
static long btf_hash_typedef(struct btf_type *t)
{
long h;

h = hash_combine(0, t->name_off);
h = hash_combine(h, t->info);
return h;
}

static long btf_hash_common(struct btf_type *t)
{
long h;
Expand All @@ -3918,6 +3932,13 @@ static bool btf_equal_common(struct btf_type *t1, struct btf_type *t2)
t1->size == t2->size;
}

/* Check structural compatibility of two TYPEDEF. */
static bool btf_equal_typedef(struct btf_type *t1, struct btf_type *t2)
{
return t1->name_off == t2->name_off &&
t1->info == t2->info;
}

/* Calculate type signature hash of INT or TAG. */
static long btf_hash_int_decl_tag(struct btf_type *t)
{
Expand Down Expand Up @@ -4844,14 +4865,31 @@ static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
}
}

static inline long btf_hash_by_kind(struct btf_type *t, __u16 kind)
{
if (kind == BTF_KIND_TYPEDEF)
return btf_hash_typedef(t);
else
return btf_hash_struct(t);
}

static inline bool btf_equal_by_kind(struct btf_type *t1, struct btf_type *t2, __u16 kind)
{
if (kind == BTF_KIND_TYPEDEF)
return btf_equal_typedef(t1, t2);
else
return btf_shallow_equal_struct(t1, t2);
}

/*
* Deduplicate struct/union types.
* Deduplicate struct/union and typedef types.
*
* For each struct/union type its type signature hash is calculated, taking
* into account type's name, size, number, order and names of fields, but
* ignoring type ID's referenced from fields, because they might not be deduped
* completely until after reference types deduplication phase. This type hash
* is used to iterate over all potential canonical types, sharing same hash.
* completely until after reference types deduplication phase. For each typedef
* type, the hash is computed based on the type’s name and size. This type hash
* is used to iterate over all potential canonical types, sharingsame hash.
* For each canonical candidate we check whether type graphs that they form
* (through referenced types in fields and so on) are equivalent using algorithm
* implemented in `btf_dedup_is_equiv`. If such equivalence is found and
Expand Down Expand Up @@ -4882,26 +4920,28 @@ static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id)
t = btf_type_by_id(d->btf, type_id);
kind = btf_kind(t);

if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION)
if (kind != BTF_KIND_STRUCT &&
kind != BTF_KIND_UNION &&
kind != BTF_KIND_TYPEDEF)
return 0;

h = btf_hash_struct(t);
h = btf_hash_by_kind(t, kind);
for_each_dedup_cand(d, hash_entry, h) {
__u32 cand_id = hash_entry->value;
int eq;

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

btf_dedup_clear_hypot_map(d);
Expand Down Expand Up @@ -4939,7 +4979,7 @@ static int btf_dedup_struct_types(struct btf_dedup *d)
/*
* Deduplicate reference type.
*
* Once all primitive and struct/union types got deduplicated, we can easily
* Once all primitive, struct/union and typedef types got deduplicated, we can easily
* deduplicate all other (reference) BTF types. This is done in two steps:
*
* 1. Resolve all referenced type IDs into their canonical type IDs. This
Expand Down Expand Up @@ -4982,7 +5022,6 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
case BTF_KIND_PTR:
case BTF_KIND_TYPEDEF:
case BTF_KIND_FUNC:
case BTF_KIND_TYPE_TAG:
ref_type_id = btf_dedup_ref_type(d, t->type);
Expand Down
61 changes: 61 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7495,6 +7495,67 @@ static struct btf_dedup_test dedup_tests[] = {
BTF_STR_SEC("\0t\0m1\0m2\0tag1\0tag2\0tag3"),
},
},
{
.descr = "dedup: recursive typedef",
/*
* This test simulates a recursive typedef, which in GO is defined as such:
*
* type Foo func() Foo
*
* In BTF terms, this is represented as a TYPEDEF referencing
* a FUNC_PROTO that returns the same TYPEDEF.
*/
.input = {
.raw_types = {
/*
* [1] typedef Foo -> func() Foo
* [2] func_proto() -> Foo
*/
BTF_TYPEDEF_ENC(NAME_NTH(1), 2), /* [1] */
BTF_FUNC_PROTO_ENC(1, 0), /* [2] */
BTF_END_RAW,
},
BTF_STR_SEC("\0Foo"),
},
.expect = {
.raw_types = {
BTF_TYPEDEF_ENC(NAME_NTH(1), 2), /* [1] */
BTF_FUNC_PROTO_ENC(1, 0), /* [2] */
BTF_END_RAW,
},
BTF_STR_SEC("\0Foo"),
},
},
{
.descr = "dedup: typedef",
/*
* // CU 1:
* typedef int foo;
*
* // CU 2:
* typedef int foo;
*/
.input = {
.raw_types = {
/* CU 1 */
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
BTF_TYPEDEF_ENC(NAME_NTH(1), 1), /* [2] */
/* CU 2 */
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [3] */
BTF_TYPEDEF_ENC(NAME_NTH(1), 3), /* [4] */
BTF_END_RAW,
},
BTF_STR_SEC("\0foo"),
},
.expect = {
.raw_types = {
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
BTF_TYPEDEF_ENC(NAME_NTH(1), 1), /* [2] */
BTF_END_RAW,
},
BTF_STR_SEC("\0foo"),
},
},
{
.descr = "dedup: typedef tags",
.input = {
Expand Down
Loading