Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

libbpf: Improve BTF dedup handling of "identical" BTF types

BTF dedup has a strong assumption that compiler with deduplicate identical
types within any given compilation unit (i.e., .c file). This property
is used when establishing equilvalence of two subgraphs of types.

Unfortunately, this property doesn't always holds in practice. We've
seen cases of having truly identical structs, unions, array definitions,
and, most recently, even pointers to the same type being duplicated
within CU.

Previously, we mitigated this on a case-by-case basis, adding a few
simple heuristics for validating that two BTF types (having two
different type IDs) are structurally the same. But this approach scales
poorly, and we can have more weird cases come up in the future.

So let's take a half-step back, and implement a bit more generic
structural equivalence check, recursively. We still limit it to
reasonable depth to avoid long reference loops. Depth-wise limiting of
potentially cyclical graph isn't great, but as I mentioned below doesn't
seem to be detrimental performance-wise. We can always improve this in
the future with per-type visited markers, if necessary.

Performance-wise this doesn't seem too affect vmlinux BTF dedup, which
makes sense because this logic kicks in not so frequently and only if we
already established a canonical candidate type match, but suddenly find
a different (but probably identical) type.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Reviewed-by: Alan Maguire <alan.maguire@oracle.com>
Link: https://lore.kernel.org/r/20250501235231.1339822-1-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
62e23f18 41948afc

+92 -51
+92 -51
tools/lib/bpf/btf.c
··· 4356 4356 return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT; 4357 4357 } 4358 4358 4359 - /* Check if given two types are identical ARRAY definitions */ 4360 - static bool btf_dedup_identical_arrays(struct btf_dedup *d, __u32 id1, __u32 id2) 4359 + static bool btf_dedup_identical_types(struct btf_dedup *d, __u32 id1, __u32 id2, int depth) 4361 4360 { 4362 4361 struct btf_type *t1, *t2; 4363 - 4364 - t1 = btf_type_by_id(d->btf, id1); 4365 - t2 = btf_type_by_id(d->btf, id2); 4366 - if (!btf_is_array(t1) || !btf_is_array(t2)) 4362 + int k1, k2; 4363 + recur: 4364 + if (depth <= 0) 4367 4365 return false; 4368 - 4369 - return btf_equal_array(t1, t2); 4370 - } 4371 - 4372 - /* Check if given two types are identical STRUCT/UNION definitions */ 4373 - static bool btf_dedup_identical_structs(struct btf_dedup *d, __u32 id1, __u32 id2) 4374 - { 4375 - const struct btf_member *m1, *m2; 4376 - struct btf_type *t1, *t2; 4377 - int n, i; 4378 4366 4379 4367 t1 = btf_type_by_id(d->btf, id1); 4380 4368 t2 = btf_type_by_id(d->btf, id2); 4381 4369 4382 - if (!btf_is_composite(t1) || btf_kind(t1) != btf_kind(t2)) 4370 + k1 = btf_kind(t1); 4371 + k2 = btf_kind(t2); 4372 + if (k1 != k2) 4383 4373 return false; 4384 4374 4385 - if (!btf_shallow_equal_struct(t1, t2)) 4386 - return false; 4387 - 4388 - m1 = btf_members(t1); 4389 - m2 = btf_members(t2); 4390 - for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) { 4391 - if (m1->type != m2->type && 4392 - !btf_dedup_identical_arrays(d, m1->type, m2->type) && 4393 - !btf_dedup_identical_structs(d, m1->type, m2->type)) 4375 + switch (k1) { 4376 + case BTF_KIND_UNKN: /* VOID */ 4377 + return true; 4378 + case BTF_KIND_INT: 4379 + return btf_equal_int_tag(t1, t2); 4380 + case BTF_KIND_ENUM: 4381 + case BTF_KIND_ENUM64: 4382 + return btf_compat_enum(t1, t2); 4383 + case BTF_KIND_FWD: 4384 + case BTF_KIND_FLOAT: 4385 + return btf_equal_common(t1, t2); 4386 + case BTF_KIND_CONST: 4387 + case BTF_KIND_VOLATILE: 4388 + case BTF_KIND_RESTRICT: 4389 + case BTF_KIND_PTR: 4390 + case BTF_KIND_TYPEDEF: 4391 + case BTF_KIND_FUNC: 4392 + case BTF_KIND_TYPE_TAG: 4393 + if (t1->info != t2->info || t1->name_off != t2->name_off) 4394 4394 return false; 4395 + id1 = t1->type; 4396 + id2 = t2->type; 4397 + goto recur; 4398 + case BTF_KIND_ARRAY: { 4399 + struct btf_array *a1, *a2; 4400 + 4401 + if (!btf_compat_array(t1, t2)) 4402 + return false; 4403 + 4404 + a1 = btf_array(t1); 4405 + a2 = btf_array(t1); 4406 + 4407 + if (a1->index_type != a2->index_type && 4408 + !btf_dedup_identical_types(d, a1->index_type, a2->index_type, depth - 1)) 4409 + return false; 4410 + 4411 + if (a1->type != a2->type && 4412 + !btf_dedup_identical_types(d, a1->type, a2->type, depth - 1)) 4413 + return false; 4414 + 4415 + return true; 4395 4416 } 4396 - return true; 4397 - } 4417 + case BTF_KIND_STRUCT: 4418 + case BTF_KIND_UNION: { 4419 + const struct btf_member *m1, *m2; 4420 + int i, n; 4398 4421 4399 - static bool btf_dedup_identical_ptrs(struct btf_dedup *d, __u32 id1, __u32 id2) 4400 - { 4401 - struct btf_type *t1, *t2; 4422 + if (!btf_shallow_equal_struct(t1, t2)) 4423 + return false; 4402 4424 4403 - t1 = btf_type_by_id(d->btf, id1); 4404 - t2 = btf_type_by_id(d->btf, id2); 4425 + m1 = btf_members(t1); 4426 + m2 = btf_members(t2); 4427 + for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) { 4428 + if (m1->type == m2->type) 4429 + continue; 4430 + if (!btf_dedup_identical_types(d, m1->type, m2->type, depth - 1)) 4431 + return false; 4432 + } 4433 + return true; 4434 + } 4435 + case BTF_KIND_FUNC_PROTO: { 4436 + const struct btf_param *p1, *p2; 4437 + int i, n; 4405 4438 4406 - if (!btf_is_ptr(t1) || !btf_is_ptr(t2)) 4439 + if (!btf_compat_fnproto(t1, t2)) 4440 + return false; 4441 + 4442 + if (t1->type != t2->type && 4443 + !btf_dedup_identical_types(d, t1->type, t2->type, depth - 1)) 4444 + return false; 4445 + 4446 + p1 = btf_params(t1); 4447 + p2 = btf_params(t2); 4448 + for (i = 0, n = btf_vlen(t1); i < n; i++, p1++, p2++) { 4449 + if (p1->type == p2->type) 4450 + continue; 4451 + if (!btf_dedup_identical_types(d, p1->type, p2->type, depth - 1)) 4452 + return false; 4453 + } 4454 + return true; 4455 + } 4456 + default: 4407 4457 return false; 4408 - 4409 - return t1->type == t2->type; 4458 + } 4410 4459 } 4460 + 4411 4461 4412 4462 /* 4413 4463 * Check equivalence of BTF type graph formed by candidate struct/union (we'll ··· 4577 4527 * different fields within the *same* struct. This breaks type 4578 4528 * equivalence check, which makes an assumption that candidate 4579 4529 * types sub-graph has a consistent and deduped-by-compiler 4580 - * types within a single CU. So work around that by explicitly 4581 - * allowing identical array types here. 4530 + * types within a single CU. And similar situation can happen 4531 + * with struct/union sometimes, and event with pointers. 4532 + * So accommodate cases like this doing a structural 4533 + * comparison recursively, but avoiding being stuck in endless 4534 + * loops by limiting the depth up to which we check. 4582 4535 */ 4583 - if (btf_dedup_identical_arrays(d, hypot_type_id, cand_id)) 4584 - return 1; 4585 - /* It turns out that similar situation can happen with 4586 - * struct/union sometimes, sigh... Handle the case where 4587 - * structs/unions are exactly the same, down to the referenced 4588 - * type IDs. Anything more complicated (e.g., if referenced 4589 - * types are different, but equivalent) is *way more* 4590 - * complicated and requires a many-to-many equivalence mapping. 4591 - */ 4592 - if (btf_dedup_identical_structs(d, hypot_type_id, cand_id)) 4593 - return 1; 4594 - /* A similar case is again observed for PTRs. */ 4595 - if (btf_dedup_identical_ptrs(d, hypot_type_id, cand_id)) 4536 + if (btf_dedup_identical_types(d, hypot_type_id, cand_id, 16)) 4596 4537 return 1; 4597 4538 return 0; 4598 4539 }