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.

Merge branch 'improve-the-performance-of-btf-type-lookups-with-binary-search'

Donglin Peng says:

====================
Improve the performance of BTF type lookups with binary search

From: Donglin Peng <pengdonglin@xiaomi.com>

The series addresses the performance limitations of linear search in large
BTFs by:
1. Adding BTF permutation support
2. Using resolve_btfids to sort BTF during the build phase
3. Checking BTF sorting
4. Using binary search when looking up types

Patch #1 introduces an interface for btf__permute in libbpf to relay out BTF.
Patch #2 adds test cases to validate the functionality of btf__permute in base
and split BTF scenarios.
Patch #3 introduces a new phase in the resolve_btfids tool to sort BTF by name
in ascending order.
Patches #4-#7 implement the sorting check and binary search.
Patches #8-#10 optimize type lookup performance of some functions by skipping
anonymous types or invoking btf_find_by_name_kind.
Patch #11 refactors the code by calling str_is_empty.

Here is a simple performance test result [1] for lookups to find 87,584 named
types in vmlinux BTF:

./vmtest.sh -- ./test_progs -t btf_permute/perf -v

Results:
| Condition | Lookup Time | Improvement |
|--------------------|-------------|--------------|
| Unsorted (Linear) | 36,534 ms | Baseline |
| Sorted (Binary) | 15 ms | 2437x faster |

The binary search implementation reduces lookup time from 36.5 seconds to 15
milliseconds, achieving a **2437x** speedup for large-scale type queries.

Changelog:
v12:
- Set the start_id to 1 instead of btf->start_id in the btf__find_by_name (AI)

v11:
- Link: https://lore.kernel.org/bpf/20260108031645.1350069-1-dolinux.peng@gmail.com/
- PATCH #1: Modify implementation of btf__permute: id_map[0] must be 0 for base BTF (Andrii)
- PATCH #3: Refactor the code (Andrii)
- PATCH #4~8:
- Revert to using the binary search in v7 to simplify the code (Andrii)
- Refactor the code of btf_check_sorted (Andrii, Eduard)
- Rename sorted_start_id to named_start_id
- Rename btf_sorted_start_id to btf_named_start_id, and add comments (Andrii, Eduard)

v10:
- Link: https://lore.kernel.org/all/20251218113051.455293-1-dolinux.peng@gmail.com/
- Improve btf__permute() documentation (Eduard)
- Fall back to linear search when locating anonymous types (Eduard)
- Remove redundant NULL name check in libbpf's linear search path (Eduard)
- Simplify btf_check_sorted() implementation (Eduard)
- Treat kernel modules as unsorted by default
- Introduce btf_is_sorted and btf_sorted_start_id for clarity (Eduard)
- Fix optimizations in btf_find_decl_tag_value() and btf_prepare_func_args()
to support split BTF
- Remove linear search branch in determine_ptr_size()
- Rebase onto Ihor's v4 patch series [4]

v9:
- Link: https://lore.kernel.org/bpf/20251208062353.1702672-1-dolinux.peng@gmail.com/
- Optimize the performance of the function determine_ptr_size by invoking
btf__find_by_name_kind
- Optimize the performance of btf_find_decl_tag_value/btf_prepare_func_args/
bpf_core_add_cands by skipping anonymous types
- Rebase the patch series onto Ihor's v3 patch series [3]

v8
- Link: https://lore.kernel.org/bpf/20251126085025.784288-1-dolinux.peng@gmail.com/
- Remove the type dropping feature of btf__permute (Andrii)
- Refactor the code of btf__permute (Andrii, Eduard)
- Make the self-test code cleaner (Eduard)
- Reconstruct the BTF sorting patch based on Ihor's patch series [2]
- Simplify the sorting logic and place anonymous types before named types
(Andrii, Eduard)
- Optimize type lookup performance of two kernel functions
- Refactoring the binary search and type lookup logic achieves a 4.2%
performance gain, reducing the average lookup time (via the perf test
code in [1] for 60,995 named types in vmlinux BTF) from 10,217 us (v7) to
9,783 us (v8).

v7:
- Link: https://lore.kernel.org/all/20251119031531.1817099-1-dolinux.peng@gmail.com/
- btf__permute API refinement: Adjusted id_map and id_map_cnt parameter
usage so that for base BTF, id_map[0] now contains the new id of original
type id 1 (instead of VOID type id 0), improving logical consistency
- Selftest updates: Modified test cases to align with the API usage changes
- Refactor the code of resolve_btfids

v6:
- Link: https://lore.kernel.org/all/20251117132623.3807094-1-dolinux.peng@gmail.com/
- ID Map-based reimplementation of btf__permute (Andrii)
- Build-time BTF sorting using resolve_btfids (Alexei, Eduard)
- Binary search method refactoring (Andrii)
- Enhanced selftest coverage

v5:
- Link: https://lore.kernel.org/all/20251106131956.1222864-1-dolinux.peng@gmail.com/
- Refactor binary search implementation for improved efficiency
(Thanks to Andrii and Eduard)
- Extend btf__permute interface with 'ids_sz' parameter to support
type dropping feature (suggested by Andrii). Plan subsequent reimplementation of
id_map version for comparative analysis with current sequence interface
- Add comprehensive test coverage for type dropping functionality
- Enhance function comment clarity and accuracy

v4:
- Link: https://lore.kernel.org/all/20251104134033.344807-1-dolinux.peng@gmail.com/
- Abstracted btf_dedup_remap_types logic into a helper function (suggested by Eduard).
- Removed btf_sort.c and implemented sorting separately for libbpf and kernel (suggested by Andrii).
- Added test cases for both base BTF and split BTF scenarios (suggested by Eduard).
- Added validation for name-only sorting of types (suggested by Andrii)
- Refactored btf__permute implementation to reduce complexity (suggested by Andrii)
- Add doc comments for btf__permute (suggested by Andrii)

v3:
- Link: https://lore.kernel.org/all/20251027135423.3098490-1-dolinux.peng@gmail.com/
- Remove sorting logic from libbpf and provide a generic btf__permute() interface (suggested
by Andrii)
- Omitted the search direction patch to avoid conflicts with base BTF (suggested by Eduard).
- Include btf_sort.c directly in btf.c to reduce function call overhead

v2:
- Link: https://lore.kernel.org/all/20251020093941.548058-1-dolinux.peng@gmail.com/
- Moved sorting to the build phase to reduce overhead (suggested by Alexei).
- Integrated sorting into btf_dedup_compact_and_sort_types (suggested by Eduard).
- Added sorting checks during BTF parsing.
- Consolidated common logic into btf_sort.c for sharing (suggested by Alan).

v1:
- Link: https://lore.kernel.org/all/20251013131537.1927035-1-dolinux.peng@gmail.com/

[1] https://github.com/pengdonglin137/btf_sort_test
[2] https://lore.kernel.org/bpf/20251126012656.3546071-1-ihor.solodrai@linux.dev/
[3] https://lore.kernel.org/bpf/20251205223046.4155870-1-ihor.solodrai@linux.dev/
[4] https://lore.kernel.org/bpf/20251218003314.260269-1-ihor.solodrai@linux.dev/
====================

Link: https://patch.msgid.link/20260109130003.3313716-1-dolinux.peng@gmail.com
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+746 -92
+1
include/linux/btf.h
··· 219 219 bool btf_is_vmlinux(const struct btf *btf); 220 220 struct module *btf_try_get_module(const struct btf *btf); 221 221 u32 btf_nr_types(const struct btf *btf); 222 + u32 btf_named_start_id(const struct btf *btf, bool own); 222 223 struct btf *btf_base_btf(const struct btf *btf); 223 224 bool btf_type_is_i32(const struct btf_type *t); 224 225 bool btf_type_is_i64(const struct btf_type *t);
+131 -14
kernel/bpf/btf.c
··· 259 259 void *nohdr_data; 260 260 struct btf_header hdr; 261 261 u32 nr_types; /* includes VOID for base BTF */ 262 + u32 named_start_id; 262 263 u32 types_size; 263 264 u32 data_size; 264 265 refcount_t refcnt; ··· 495 494 return false; 496 495 } 497 496 497 + static int btf_start_id(const struct btf *btf) 498 + { 499 + return btf->start_id + (btf->base_btf ? 0 : 1); 500 + } 501 + 498 502 bool btf_type_is_void(const struct btf_type *t) 499 503 { 500 504 return t == &btf_void; ··· 550 544 return total; 551 545 } 552 546 553 - s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind) 547 + /* 548 + * Note that vmlinux and kernel module BTFs are always sorted 549 + * during the building phase. 550 + */ 551 + static void btf_check_sorted(struct btf *btf) 552 + { 553 + u32 i, n, named_start_id = 0; 554 + 555 + n = btf_nr_types(btf); 556 + if (btf_is_vmlinux(btf)) { 557 + for (i = btf_start_id(btf); i < n; i++) { 558 + const struct btf_type *t = btf_type_by_id(btf, i); 559 + const char *n = btf_name_by_offset(btf, t->name_off); 560 + 561 + if (n[0] != '\0') { 562 + btf->named_start_id = i; 563 + return; 564 + } 565 + } 566 + return; 567 + } 568 + 569 + for (i = btf_start_id(btf) + 1; i < n; i++) { 570 + const struct btf_type *ta = btf_type_by_id(btf, i - 1); 571 + const struct btf_type *tb = btf_type_by_id(btf, i); 572 + const char *na = btf_name_by_offset(btf, ta->name_off); 573 + const char *nb = btf_name_by_offset(btf, tb->name_off); 574 + 575 + if (strcmp(na, nb) > 0) 576 + return; 577 + 578 + if (named_start_id == 0 && na[0] != '\0') 579 + named_start_id = i - 1; 580 + if (named_start_id == 0 && nb[0] != '\0') 581 + named_start_id = i; 582 + } 583 + 584 + if (named_start_id) 585 + btf->named_start_id = named_start_id; 586 + } 587 + 588 + /* 589 + * btf_named_start_id - Get the named starting ID for the BTF 590 + * @btf: Pointer to the target BTF object 591 + * @own: Flag indicating whether to query only the current BTF (true = current BTF only, 592 + * false = recursively traverse the base BTF chain) 593 + * 594 + * Return value rules: 595 + * 1. For a sorted btf, return its named_start_id 596 + * 2. Else for a split BTF, return its start_id 597 + * 3. Else for a base BTF, return 1 598 + */ 599 + u32 btf_named_start_id(const struct btf *btf, bool own) 600 + { 601 + const struct btf *base_btf = btf; 602 + 603 + while (!own && base_btf->base_btf) 604 + base_btf = base_btf->base_btf; 605 + 606 + return base_btf->named_start_id ?: (base_btf->start_id ?: 1); 607 + } 608 + 609 + static s32 btf_find_by_name_kind_bsearch(const struct btf *btf, const char *name) 554 610 { 555 611 const struct btf_type *t; 556 612 const char *tname; 557 - u32 i, total; 613 + s32 l, r, m; 614 + 615 + l = btf_named_start_id(btf, true); 616 + r = btf_nr_types(btf) - 1; 617 + while (l <= r) { 618 + m = l + (r - l) / 2; 619 + t = btf_type_by_id(btf, m); 620 + tname = btf_name_by_offset(btf, t->name_off); 621 + if (strcmp(tname, name) >= 0) { 622 + if (l == r) 623 + return r; 624 + r = m; 625 + } else { 626 + l = m + 1; 627 + } 628 + } 629 + 630 + return btf_nr_types(btf); 631 + } 632 + 633 + s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind) 634 + { 635 + const struct btf *base_btf = btf_base_btf(btf); 636 + const struct btf_type *t; 637 + const char *tname; 638 + s32 id, total; 639 + 640 + if (base_btf) { 641 + id = btf_find_by_name_kind(base_btf, name, kind); 642 + if (id > 0) 643 + return id; 644 + } 558 645 559 646 total = btf_nr_types(btf); 560 - for (i = 1; i < total; i++) { 561 - t = btf_type_by_id(btf, i); 562 - if (BTF_INFO_KIND(t->info) != kind) 563 - continue; 564 - 565 - tname = btf_name_by_offset(btf, t->name_off); 566 - if (!strcmp(tname, name)) 567 - return i; 647 + if (btf->named_start_id > 0 && name[0]) { 648 + id = btf_find_by_name_kind_bsearch(btf, name); 649 + for (; id < total; id++) { 650 + t = btf_type_by_id(btf, id); 651 + tname = btf_name_by_offset(btf, t->name_off); 652 + if (strcmp(tname, name) != 0) 653 + return -ENOENT; 654 + if (BTF_INFO_KIND(t->info) == kind) 655 + return id; 656 + } 657 + } else { 658 + for (id = btf_start_id(btf); id < total; id++) { 659 + t = btf_type_by_id(btf, id); 660 + if (BTF_INFO_KIND(t->info) != kind) 661 + continue; 662 + tname = btf_name_by_offset(btf, t->name_off); 663 + if (strcmp(tname, name) == 0) 664 + return id; 665 + } 568 666 } 569 667 570 668 return -ENOENT; ··· 3534 3424 const struct btf_type *t; 3535 3425 int len, id; 3536 3426 3537 - id = btf_find_next_decl_tag(btf, pt, comp_idx, tag_key, 0); 3427 + id = btf_find_next_decl_tag(btf, pt, comp_idx, tag_key, 3428 + btf_named_start_id(btf, false) - 1); 3538 3429 if (id < 0) 3539 3430 return ERR_PTR(id); 3540 3431 ··· 5902 5791 goto errout; 5903 5792 } 5904 5793 env->btf = btf; 5794 + btf->named_start_id = 0; 5905 5795 5906 5796 data = kvmalloc(attr->btf_size, GFP_KERNEL | __GFP_NOWARN); 5907 5797 if (!data) { ··· 6322 6210 btf->data = data; 6323 6211 btf->data_size = data_size; 6324 6212 btf->kernel_btf = true; 6213 + btf->named_start_id = 0; 6325 6214 snprintf(btf->name, sizeof(btf->name), "%s", name); 6326 6215 6327 6216 err = btf_parse_hdr(env); ··· 6343 6230 if (err) 6344 6231 goto errout; 6345 6232 6233 + btf_check_sorted(btf); 6346 6234 refcount_set(&btf->refcnt, 1); 6347 6235 6348 6236 return btf; ··· 6441 6327 btf->start_id = base_btf->nr_types; 6442 6328 btf->start_str_off = base_btf->hdr.str_len; 6443 6329 btf->kernel_btf = true; 6330 + btf->named_start_id = 0; 6444 6331 snprintf(btf->name, sizeof(btf->name), "%s", module_name); 6445 6332 6446 6333 btf->data = kvmemdup(data, data_size, GFP_KERNEL | __GFP_NOWARN); ··· 6478 6363 } 6479 6364 6480 6365 btf_verifier_env_free(env); 6366 + btf_check_sorted(btf); 6481 6367 refcount_set(&btf->refcnt, 1); 6482 6368 return btf; 6483 6369 ··· 7845 7729 tname); 7846 7730 return -EINVAL; 7847 7731 } 7732 + 7848 7733 /* Convert BTF function arguments into verifier types. 7849 7734 * Only PTR_TO_CTX and SCALAR are supported atm. 7850 7735 */ 7851 7736 for (i = 0; i < nargs; i++) { 7852 7737 u32 tags = 0; 7853 - int id = 0; 7738 + int id = btf_named_start_id(btf, false) - 1; 7854 7739 7855 7740 /* 'arg:<tag>' decl_tag takes precedence over derivation of 7856 7741 * register type from BTF type itself ··· 9340 9223 } 9341 9224 9342 9225 /* Attempt to find target candidates in vmlinux BTF first */ 9343 - cands = bpf_core_add_cands(cands, main_btf, 1); 9226 + cands = bpf_core_add_cands(cands, main_btf, btf_named_start_id(main_btf, true)); 9344 9227 if (IS_ERR(cands)) 9345 9228 return ERR_CAST(cands); 9346 9229 ··· 9372 9255 */ 9373 9256 btf_get(mod_btf); 9374 9257 spin_unlock_bh(&btf_idr_lock); 9375 - cands = bpf_core_add_cands(cands, mod_btf, btf_nr_types(main_btf)); 9258 + cands = bpf_core_add_cands(cands, mod_btf, btf_named_start_id(mod_btf, true)); 9376 9259 btf_put(mod_btf); 9377 9260 if (IS_ERR(cands)) 9378 9261 return ERR_CAST(cands);
+17 -25
kernel/bpf/inode.c
··· 600 600 601 601 static int find_bpffs_btf_enums(struct bpffs_btf_enums *info) 602 602 { 603 + struct { 604 + const struct btf_type **type; 605 + const char *name; 606 + } btf_enums[] = { 607 + {&info->cmd_t, "bpf_cmd"}, 608 + {&info->map_t, "bpf_map_type"}, 609 + {&info->prog_t, "bpf_prog_type"}, 610 + {&info->attach_t, "bpf_attach_type"}, 611 + }; 603 612 const struct btf *btf; 604 - const struct btf_type *t; 605 - const char *name; 606 - int i, n; 613 + int i, id; 607 614 608 615 memset(info, 0, sizeof(*info)); 609 616 ··· 622 615 623 616 info->btf = btf; 624 617 625 - for (i = 1, n = btf_nr_types(btf); i < n; i++) { 626 - t = btf_type_by_id(btf, i); 627 - if (!btf_type_is_enum(t)) 628 - continue; 618 + for (i = 0; i < ARRAY_SIZE(btf_enums); i++) { 619 + id = btf_find_by_name_kind(btf, btf_enums[i].name, 620 + BTF_KIND_ENUM); 621 + if (id < 0) 622 + return -ESRCH; 629 623 630 - name = btf_name_by_offset(btf, t->name_off); 631 - if (!name) 632 - continue; 633 - 634 - if (strcmp(name, "bpf_cmd") == 0) 635 - info->cmd_t = t; 636 - else if (strcmp(name, "bpf_map_type") == 0) 637 - info->map_t = t; 638 - else if (strcmp(name, "bpf_prog_type") == 0) 639 - info->prog_t = t; 640 - else if (strcmp(name, "bpf_attach_type") == 0) 641 - info->attach_t = t; 642 - else 643 - continue; 644 - 645 - if (info->cmd_t && info->map_t && info->prog_t && info->attach_t) 646 - return 0; 624 + *btf_enums[i].type = btf_type_by_id(btf, id); 647 625 } 648 626 649 - return -ESRCH; 627 + return 0; 650 628 } 651 629 652 630 static bool find_btf_enum_const(const struct btf *btf, const struct btf_type *enum_t,
+1 -6
kernel/bpf/verifier.c
··· 20687 20687 * types to look at only module's own BTF types. 20688 20688 */ 20689 20689 n = btf_nr_types(btf); 20690 - if (btf_is_module(btf)) 20691 - i = btf_nr_types(btf_vmlinux); 20692 - else 20693 - i = 1; 20694 - 20695 - for(; i < n; i++) { 20690 + for (i = btf_named_start_id(btf, true); i < n; i++) { 20696 20691 t = btf_type_by_id(btf, i); 20697 20692 if (BTF_INFO_KIND(t->info) != BTF_KIND_DATASEC) 20698 20693 continue;
+64
tools/bpf/resolve_btfids/main.c
··· 850 850 return 0; 851 851 } 852 852 853 + /* 854 + * Sort types by name in ascending order resulting in all 855 + * anonymous types being placed before named types. 856 + */ 857 + static int cmp_type_names(const void *a, const void *b, void *priv) 858 + { 859 + struct btf *btf = (struct btf *)priv; 860 + const struct btf_type *ta = btf__type_by_id(btf, *(__u32 *)a); 861 + const struct btf_type *tb = btf__type_by_id(btf, *(__u32 *)b); 862 + const char *na, *nb; 863 + 864 + na = btf__str_by_offset(btf, ta->name_off); 865 + nb = btf__str_by_offset(btf, tb->name_off); 866 + return strcmp(na, nb); 867 + } 868 + 869 + static int sort_btf_by_name(struct btf *btf) 870 + { 871 + __u32 *permute_ids = NULL, *id_map = NULL; 872 + int nr_types, i, err = 0; 873 + __u32 start_id = 0, start_offs = 1, id; 874 + 875 + if (btf__base_btf(btf)) { 876 + start_id = btf__type_cnt(btf__base_btf(btf)); 877 + start_offs = 0; 878 + } 879 + nr_types = btf__type_cnt(btf) - start_id; 880 + 881 + permute_ids = calloc(nr_types, sizeof(*permute_ids)); 882 + if (!permute_ids) { 883 + err = -ENOMEM; 884 + goto out; 885 + } 886 + 887 + id_map = calloc(nr_types, sizeof(*id_map)); 888 + if (!id_map) { 889 + err = -ENOMEM; 890 + goto out; 891 + } 892 + 893 + for (i = 0, id = start_id; i < nr_types; i++, id++) 894 + permute_ids[i] = id; 895 + 896 + qsort_r(permute_ids + start_offs, nr_types - start_offs, 897 + sizeof(*permute_ids), cmp_type_names, btf); 898 + 899 + for (i = 0; i < nr_types; i++) { 900 + id = permute_ids[i] - start_id; 901 + id_map[id] = i + start_id; 902 + } 903 + 904 + err = btf__permute(btf, id_map, nr_types, NULL); 905 + if (err) 906 + pr_err("FAILED: btf permute: %s\n", strerror(-err)); 907 + 908 + out: 909 + free(permute_ids); 910 + free(id_map); 911 + return err; 912 + } 913 + 853 914 static inline int make_out_path(char *buf, u32 buf_sz, const char *in_path, const char *suffix) 854 915 { 855 916 int len = snprintf(buf, buf_sz, "%s%s", in_path, suffix); ··· 1084 1023 return patch_btfids(btfids_path, obj.path); 1085 1024 1086 1025 if (load_btf(&obj)) 1026 + goto out; 1027 + 1028 + if (sort_btf_by_name(obj.btf)) 1087 1029 goto out; 1088 1030 1089 1031 if (elf_collect(&obj))
+243 -45
tools/lib/bpf/btf.c
··· 92 92 * - for split BTF counts number of types added on top of base BTF. 93 93 */ 94 94 __u32 nr_types; 95 + /* the start IDs of named types in sorted BTF */ 96 + int named_start_id; 95 97 /* if not NULL, points to the base BTF on top of which the current 96 98 * split BTF is based 97 99 */ ··· 899 897 return type_id; 900 898 } 901 899 902 - __s32 btf__find_by_name(const struct btf *btf, const char *type_name) 900 + static void btf_check_sorted(struct btf *btf) 903 901 { 904 - __u32 i, nr_types = btf__type_cnt(btf); 902 + __u32 i, n, named_start_id = 0; 905 903 906 - if (!strcmp(type_name, "void")) 904 + n = btf__type_cnt(btf); 905 + for (i = btf->start_id + 1; i < n; i++) { 906 + struct btf_type *ta = btf_type_by_id(btf, i - 1); 907 + struct btf_type *tb = btf_type_by_id(btf, i); 908 + const char *na = btf__str_by_offset(btf, ta->name_off); 909 + const char *nb = btf__str_by_offset(btf, tb->name_off); 910 + 911 + if (strcmp(na, nb) > 0) 912 + return; 913 + 914 + if (named_start_id == 0 && na[0] != '\0') 915 + named_start_id = i - 1; 916 + if (named_start_id == 0 && nb[0] != '\0') 917 + named_start_id = i; 918 + } 919 + 920 + if (named_start_id) 921 + btf->named_start_id = named_start_id; 922 + } 923 + 924 + static __s32 btf_find_type_by_name_bsearch(const struct btf *btf, const char *name, 925 + __s32 start_id) 926 + { 927 + const struct btf_type *t; 928 + const char *tname; 929 + __s32 l, r, m; 930 + 931 + l = start_id; 932 + r = btf__type_cnt(btf) - 1; 933 + while (l <= r) { 934 + m = l + (r - l) / 2; 935 + t = btf_type_by_id(btf, m); 936 + tname = btf__str_by_offset(btf, t->name_off); 937 + if (strcmp(tname, name) >= 0) { 938 + if (l == r) 939 + return r; 940 + r = m; 941 + } else { 942 + l = m + 1; 943 + } 944 + } 945 + 946 + return btf__type_cnt(btf); 947 + } 948 + 949 + static __s32 btf_find_by_name_kind(const struct btf *btf, int start_id, 950 + const char *type_name, __s32 kind) 951 + { 952 + __u32 nr_types = btf__type_cnt(btf); 953 + const struct btf_type *t; 954 + const char *tname; 955 + __s32 id; 956 + 957 + if (start_id < btf->start_id) { 958 + id = btf_find_by_name_kind(btf->base_btf, start_id, 959 + type_name, kind); 960 + if (id >= 0) 961 + return id; 962 + start_id = btf->start_id; 963 + } 964 + 965 + if (kind == BTF_KIND_UNKN || strcmp(type_name, "void") == 0) 907 966 return 0; 908 967 909 - for (i = 1; i < nr_types; i++) { 910 - const struct btf_type *t = btf__type_by_id(btf, i); 911 - const char *name = btf__name_by_offset(btf, t->name_off); 912 - 913 - if (name && !strcmp(type_name, name)) 914 - return i; 968 + if (btf->named_start_id > 0 && type_name[0]) { 969 + start_id = max(start_id, btf->named_start_id); 970 + id = btf_find_type_by_name_bsearch(btf, type_name, start_id); 971 + for (; id < nr_types; id++) { 972 + t = btf__type_by_id(btf, id); 973 + tname = btf__str_by_offset(btf, t->name_off); 974 + if (strcmp(tname, type_name) != 0) 975 + return libbpf_err(-ENOENT); 976 + if (kind < 0 || btf_kind(t) == kind) 977 + return id; 978 + } 979 + } else { 980 + for (id = start_id; id < nr_types; id++) { 981 + t = btf_type_by_id(btf, id); 982 + if (kind > 0 && btf_kind(t) != kind) 983 + continue; 984 + tname = btf__str_by_offset(btf, t->name_off); 985 + if (strcmp(tname, type_name) == 0) 986 + return id; 987 + } 915 988 } 916 989 917 990 return libbpf_err(-ENOENT); 918 991 } 919 992 920 - static __s32 btf_find_by_name_kind(const struct btf *btf, int start_id, 921 - const char *type_name, __u32 kind) 993 + /* the kind value of -1 indicates that kind matching should be skipped */ 994 + __s32 btf__find_by_name(const struct btf *btf, const char *type_name) 922 995 { 923 - __u32 i, nr_types = btf__type_cnt(btf); 924 - 925 - if (kind == BTF_KIND_UNKN || !strcmp(type_name, "void")) 926 - return 0; 927 - 928 - for (i = start_id; i < nr_types; i++) { 929 - const struct btf_type *t = btf__type_by_id(btf, i); 930 - const char *name; 931 - 932 - if (btf_kind(t) != kind) 933 - continue; 934 - name = btf__name_by_offset(btf, t->name_off); 935 - if (name && !strcmp(type_name, name)) 936 - return i; 937 - } 938 - 939 - return libbpf_err(-ENOENT); 996 + return btf_find_by_name_kind(btf, 1, type_name, -1); 940 997 } 941 998 942 999 __s32 btf__find_by_name_kind_own(const struct btf *btf, const char *type_name, ··· 1067 1006 btf->fd = -1; 1068 1007 btf->ptr_sz = sizeof(void *); 1069 1008 btf->swapped_endian = false; 1009 + btf->named_start_id = 0; 1070 1010 1071 1011 if (base_btf) { 1072 1012 btf->base_btf = base_btf; ··· 1119 1057 btf->start_id = 1; 1120 1058 btf->start_str_off = 0; 1121 1059 btf->fd = -1; 1060 + btf->named_start_id = 0; 1122 1061 1123 1062 if (base_btf) { 1124 1063 btf->base_btf = base_btf; ··· 1154 1091 err = err ?: btf_sanity_check(btf); 1155 1092 if (err) 1156 1093 goto done; 1094 + btf_check_sorted(btf); 1157 1095 1158 1096 done: 1159 1097 if (err) { ··· 1779 1715 free(btf->raw_data_swapped); 1780 1716 btf->raw_data_swapped = NULL; 1781 1717 } 1718 + btf->named_start_id = 0; 1782 1719 } 1783 1720 1784 1721 /* Ensure BTF is ready to be modified (by splitting into a three memory ··· 2134 2069 int sz, name_off; 2135 2070 2136 2071 /* non-empty name */ 2137 - if (!name || !name[0]) 2072 + if (str_is_empty(name)) 2138 2073 return libbpf_err(-EINVAL); 2139 2074 /* byte_sz must be power of 2 */ 2140 2075 if (!byte_sz || (byte_sz & (byte_sz - 1)) || byte_sz > 16) ··· 2182 2117 int sz, name_off; 2183 2118 2184 2119 /* non-empty name */ 2185 - if (!name || !name[0]) 2120 + if (str_is_empty(name)) 2186 2121 return libbpf_err(-EINVAL); 2187 2122 2188 2123 /* byte_sz must be one of the explicitly allowed values */ ··· 2237 2172 if (!t) 2238 2173 return libbpf_err(-ENOMEM); 2239 2174 2240 - if (name && name[0]) { 2175 + if (!str_is_empty(name)) { 2241 2176 name_off = btf__add_str(btf, name); 2242 2177 if (name_off < 0) 2243 2178 return name_off; ··· 2314 2249 if (!t) 2315 2250 return libbpf_err(-ENOMEM); 2316 2251 2317 - if (name && name[0]) { 2252 + if (!str_is_empty(name)) { 2318 2253 name_off = btf__add_str(btf, name); 2319 2254 if (name_off < 0) 2320 2255 return name_off; ··· 2415 2350 if (!m) 2416 2351 return libbpf_err(-ENOMEM); 2417 2352 2418 - if (name && name[0]) { 2353 + if (!str_is_empty(name)) { 2419 2354 name_off = btf__add_str(btf, name); 2420 2355 if (name_off < 0) 2421 2356 return name_off; ··· 2453 2388 if (!t) 2454 2389 return libbpf_err(-ENOMEM); 2455 2390 2456 - if (name && name[0]) { 2391 + if (!str_is_empty(name)) { 2457 2392 name_off = btf__add_str(btf, name); 2458 2393 if (name_off < 0) 2459 2394 return name_off; ··· 2511 2446 return libbpf_err(-EINVAL); 2512 2447 2513 2448 /* non-empty name */ 2514 - if (!name || !name[0]) 2449 + if (str_is_empty(name)) 2515 2450 return libbpf_err(-EINVAL); 2516 2451 if (value < INT_MIN || value > UINT_MAX) 2517 2452 return libbpf_err(-E2BIG); ··· 2588 2523 return libbpf_err(-EINVAL); 2589 2524 2590 2525 /* non-empty name */ 2591 - if (!name || !name[0]) 2526 + if (str_is_empty(name)) 2592 2527 return libbpf_err(-EINVAL); 2593 2528 2594 2529 /* decompose and invalidate raw data */ ··· 2628 2563 */ 2629 2564 int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind) 2630 2565 { 2631 - if (!name || !name[0]) 2566 + if (str_is_empty(name)) 2632 2567 return libbpf_err(-EINVAL); 2633 2568 2634 2569 switch (fwd_kind) { ··· 2664 2599 */ 2665 2600 int btf__add_typedef(struct btf *btf, const char *name, int ref_type_id) 2666 2601 { 2667 - if (!name || !name[0]) 2602 + if (str_is_empty(name)) 2668 2603 return libbpf_err(-EINVAL); 2669 2604 2670 2605 return btf_add_ref_kind(btf, BTF_KIND_TYPEDEF, name, ref_type_id, 0); ··· 2716 2651 */ 2717 2652 int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id) 2718 2653 { 2719 - if (!value || !value[0]) 2654 + if (str_is_empty(value)) 2720 2655 return libbpf_err(-EINVAL); 2721 2656 2722 2657 return btf_add_ref_kind(btf, BTF_KIND_TYPE_TAG, value, ref_type_id, 0); ··· 2733 2668 */ 2734 2669 int btf__add_type_attr(struct btf *btf, const char *value, int ref_type_id) 2735 2670 { 2736 - if (!value || !value[0]) 2671 + if (str_is_empty(value)) 2737 2672 return libbpf_err(-EINVAL); 2738 2673 2739 2674 return btf_add_ref_kind(btf, BTF_KIND_TYPE_TAG, value, ref_type_id, 1); ··· 2752 2687 { 2753 2688 int id; 2754 2689 2755 - if (!name || !name[0]) 2690 + if (str_is_empty(name)) 2756 2691 return libbpf_err(-EINVAL); 2757 2692 if (linkage != BTF_FUNC_STATIC && linkage != BTF_FUNC_GLOBAL && 2758 2693 linkage != BTF_FUNC_EXTERN) ··· 2838 2773 if (!p) 2839 2774 return libbpf_err(-ENOMEM); 2840 2775 2841 - if (name && name[0]) { 2776 + if (!str_is_empty(name)) { 2842 2777 name_off = btf__add_str(btf, name); 2843 2778 if (name_off < 0) 2844 2779 return name_off; ··· 2873 2808 int sz, name_off; 2874 2809 2875 2810 /* non-empty name */ 2876 - if (!name || !name[0]) 2811 + if (str_is_empty(name)) 2877 2812 return libbpf_err(-EINVAL); 2878 2813 if (linkage != BTF_VAR_STATIC && linkage != BTF_VAR_GLOBAL_ALLOCATED && 2879 2814 linkage != BTF_VAR_GLOBAL_EXTERN) ··· 2922 2857 int sz, name_off; 2923 2858 2924 2859 /* non-empty name */ 2925 - if (!name || !name[0]) 2860 + if (str_is_empty(name)) 2926 2861 return libbpf_err(-EINVAL); 2927 2862 2928 2863 if (btf_ensure_modifiable(btf)) ··· 2999 2934 struct btf_type *t; 3000 2935 int sz, value_off; 3001 2936 3002 - if (!value || !value[0] || component_idx < -1) 2937 + if (str_is_empty(value) || component_idx < -1) 3003 2938 return libbpf_err(-EINVAL); 3004 2939 3005 2940 if (validate_type_id(ref_type_id)) ··· 5950 5885 5951 5886 if (!err) 5952 5887 btf->owns_base = false; 5888 + return libbpf_err(err); 5889 + } 5890 + 5891 + struct btf_permute { 5892 + struct btf *btf; 5893 + __u32 *id_map; 5894 + __u32 start_offs; 5895 + }; 5896 + 5897 + /* Callback function to remap individual type ID references */ 5898 + static int btf_permute_remap_type_id(__u32 *type_id, void *ctx) 5899 + { 5900 + struct btf_permute *p = ctx; 5901 + __u32 new_id = *type_id; 5902 + 5903 + /* refer to the base BTF or VOID type */ 5904 + if (new_id < p->btf->start_id) 5905 + return 0; 5906 + 5907 + if (new_id >= btf__type_cnt(p->btf)) 5908 + return -EINVAL; 5909 + 5910 + *type_id = p->id_map[new_id - p->btf->start_id + p->start_offs]; 5911 + return 0; 5912 + } 5913 + 5914 + int btf__permute(struct btf *btf, __u32 *id_map, __u32 id_map_cnt, 5915 + const struct btf_permute_opts *opts) 5916 + { 5917 + struct btf_permute p; 5918 + struct btf_ext *btf_ext; 5919 + void *nt, *new_types = NULL; 5920 + __u32 *order_map = NULL; 5921 + int err = 0, i; 5922 + __u32 n, id, start_offs = 0; 5923 + 5924 + if (!OPTS_VALID(opts, btf_permute_opts)) 5925 + return libbpf_err(-EINVAL); 5926 + 5927 + if (btf__base_btf(btf)) { 5928 + n = btf->nr_types; 5929 + } else { 5930 + if (id_map[0] != 0) 5931 + return libbpf_err(-EINVAL); 5932 + n = btf__type_cnt(btf); 5933 + start_offs = 1; 5934 + } 5935 + 5936 + if (id_map_cnt != n) 5937 + return libbpf_err(-EINVAL); 5938 + 5939 + /* record the sequence of types */ 5940 + order_map = calloc(id_map_cnt, sizeof(*id_map)); 5941 + if (!order_map) { 5942 + err = -ENOMEM; 5943 + goto done; 5944 + } 5945 + 5946 + new_types = calloc(btf->hdr->type_len, 1); 5947 + if (!new_types) { 5948 + err = -ENOMEM; 5949 + goto done; 5950 + } 5951 + 5952 + if (btf_ensure_modifiable(btf)) { 5953 + err = -ENOMEM; 5954 + goto done; 5955 + } 5956 + 5957 + for (i = start_offs; i < id_map_cnt; i++) { 5958 + id = id_map[i]; 5959 + if (id < btf->start_id || id >= btf__type_cnt(btf)) { 5960 + err = -EINVAL; 5961 + goto done; 5962 + } 5963 + id -= btf->start_id - start_offs; 5964 + /* cannot be mapped to the same ID */ 5965 + if (order_map[id]) { 5966 + err = -EINVAL; 5967 + goto done; 5968 + } 5969 + order_map[id] = i + btf->start_id - start_offs; 5970 + } 5971 + 5972 + p.btf = btf; 5973 + p.id_map = id_map; 5974 + p.start_offs = start_offs; 5975 + nt = new_types; 5976 + for (i = start_offs; i < id_map_cnt; i++) { 5977 + struct btf_field_iter it; 5978 + const struct btf_type *t; 5979 + __u32 *type_id; 5980 + int type_size; 5981 + 5982 + id = order_map[i]; 5983 + t = btf__type_by_id(btf, id); 5984 + type_size = btf_type_size(t); 5985 + memcpy(nt, t, type_size); 5986 + 5987 + /* fix up referenced IDs for BTF */ 5988 + err = btf_field_iter_init(&it, nt, BTF_FIELD_ITER_IDS); 5989 + if (err) 5990 + goto done; 5991 + while ((type_id = btf_field_iter_next(&it))) { 5992 + err = btf_permute_remap_type_id(type_id, &p); 5993 + if (err) 5994 + goto done; 5995 + } 5996 + 5997 + nt += type_size; 5998 + } 5999 + 6000 + /* fix up referenced IDs for btf_ext */ 6001 + btf_ext = OPTS_GET(opts, btf_ext, NULL); 6002 + if (btf_ext) { 6003 + err = btf_ext_visit_type_ids(btf_ext, btf_permute_remap_type_id, &p); 6004 + if (err) 6005 + goto done; 6006 + } 6007 + 6008 + for (nt = new_types, i = 0; i < id_map_cnt - start_offs; i++) { 6009 + btf->type_offs[i] = nt - new_types; 6010 + nt += btf_type_size(nt); 6011 + } 6012 + 6013 + free(order_map); 6014 + free(btf->types_data); 6015 + btf->types_data = new_types; 6016 + return 0; 6017 + 6018 + done: 6019 + free(order_map); 6020 + free(new_types); 5953 6021 return libbpf_err(err); 5954 6022 }
+42
tools/lib/bpf/btf.h
··· 281 281 */ 282 282 LIBBPF_API int btf__relocate(struct btf *btf, const struct btf *base_btf); 283 283 284 + struct btf_permute_opts { 285 + size_t sz; 286 + /* optional .BTF.ext info along the main BTF info */ 287 + struct btf_ext *btf_ext; 288 + size_t :0; 289 + }; 290 + #define btf_permute_opts__last_field btf_ext 291 + 292 + /** 293 + * @brief **btf__permute()** rearranges BTF types in-place according to a specified ID mapping 294 + * @param btf BTF object to permute 295 + * @param id_map Array mapping original type IDs to new IDs 296 + * @param id_map_cnt Number of elements in @id_map 297 + * @param opts Optional parameters, including BTF extension data for reference updates 298 + * @return 0 on success, negative error code on failure 299 + * 300 + * **btf__permute()** reorders BTF types based on the provided @id_map array, 301 + * updating all internal type references to maintain consistency. The function 302 + * operates in-place, modifying the BTF object directly. 303 + * 304 + * For **base BTF**: 305 + * - @id_map must include all types from ID 0 to `btf__type_cnt(btf) - 1` 306 + * - @id_map_cnt must be `btf__type_cnt(btf)` 307 + * - Mapping is defined as `id_map[original_id] = new_id` 308 + * - `id_map[0]` must be 0 (void type cannot be moved) 309 + * 310 + * For **split BTF**: 311 + * - @id_map must include only split types (types added on top of the base BTF) 312 + * - @id_map_cnt must be `btf__type_cnt(btf) - btf__type_cnt(btf__base_btf(btf))` 313 + * - Mapping is defined as `id_map[original_id - start_id] = new_id` 314 + * - `start_id` equals `btf__type_cnt(btf__base_btf(btf))` 315 + * 316 + * After permutation, all type references within the BTF data and optional 317 + * BTF extension (if provided via @opts) are updated automatically. 318 + * 319 + * On error, returns a negative error code and sets errno: 320 + * - `-EINVAL`: Invalid parameters or invalid ID mapping 321 + * - `-ENOMEM`: Memory allocation failure 322 + */ 323 + LIBBPF_API int btf__permute(struct btf *btf, __u32 *id_map, __u32 id_map_cnt, 324 + const struct btf_permute_opts *opts); 325 + 284 326 struct btf_dump; 285 327 286 328 struct btf_dump_opts {
+2 -2
tools/lib/bpf/libbpf.c
··· 2904 2904 var_extra = btf_var(var); 2905 2905 map_name = btf__name_by_offset(obj->btf, var->name_off); 2906 2906 2907 - if (map_name == NULL || map_name[0] == '\0') { 2907 + if (str_is_empty(map_name)) { 2908 2908 pr_warn("map #%d: empty name.\n", var_idx); 2909 2909 return -EINVAL; 2910 2910 } ··· 4281 4281 if (!sym_is_extern(sym)) 4282 4282 continue; 4283 4283 ext_name = elf_sym_str(obj, sym->st_name); 4284 - if (!ext_name || !ext_name[0]) 4284 + if (str_is_empty(ext_name)) 4285 4285 continue; 4286 4286 4287 4287 ext = obj->externs;
+1
tools/lib/bpf/libbpf.map
··· 453 453 bpf_map__exclusive_program; 454 454 bpf_prog_assoc_struct_ops; 455 455 bpf_program__assoc_struct_ops; 456 + btf__permute; 456 457 } LIBBPF_1.6.0;
+244
tools/testing/selftests/bpf/prog_tests/btf_permute.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2026 Xiaomi */ 3 + 4 + #include <test_progs.h> 5 + #include <bpf/btf.h> 6 + #include "btf_helpers.h" 7 + 8 + static void permute_base_check(struct btf *btf) 9 + { 10 + VALIDATE_RAW_BTF( 11 + btf, 12 + "[1] STRUCT 's2' size=4 vlen=1\n" 13 + "\t'm' type_id=4 bits_offset=0", 14 + "[2] FUNC 'f' type_id=6 linkage=static", 15 + "[3] PTR '(anon)' type_id=4", 16 + "[4] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", 17 + "[5] STRUCT 's1' size=4 vlen=1\n" 18 + "\t'm' type_id=4 bits_offset=0", 19 + "[6] FUNC_PROTO '(anon)' ret_type_id=4 vlen=1\n" 20 + "\t'p' type_id=3"); 21 + } 22 + 23 + /* Ensure btf__permute works as expected in the base-BTF scenario */ 24 + static void test_permute_base(void) 25 + { 26 + struct btf *btf; 27 + __u32 permute_ids[7]; 28 + int err; 29 + 30 + btf = btf__new_empty(); 31 + if (!ASSERT_OK_PTR(btf, "empty_main_btf")) 32 + return; 33 + 34 + btf__add_int(btf, "int", 4, BTF_INT_SIGNED); /* [1] int */ 35 + btf__add_ptr(btf, 1); /* [2] ptr to int */ 36 + btf__add_struct(btf, "s1", 4); /* [3] struct s1 { */ 37 + btf__add_field(btf, "m", 1, 0, 0); /* int m; */ 38 + /* } */ 39 + btf__add_struct(btf, "s2", 4); /* [4] struct s2 { */ 40 + btf__add_field(btf, "m", 1, 0, 0); /* int m; */ 41 + /* } */ 42 + btf__add_func_proto(btf, 1); /* [5] int (*)(int *p); */ 43 + btf__add_func_param(btf, "p", 2); 44 + btf__add_func(btf, "f", BTF_FUNC_STATIC, 5); /* [6] int f(int *p); */ 45 + 46 + VALIDATE_RAW_BTF( 47 + btf, 48 + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", 49 + "[2] PTR '(anon)' type_id=1", 50 + "[3] STRUCT 's1' size=4 vlen=1\n" 51 + "\t'm' type_id=1 bits_offset=0", 52 + "[4] STRUCT 's2' size=4 vlen=1\n" 53 + "\t'm' type_id=1 bits_offset=0", 54 + "[5] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" 55 + "\t'p' type_id=2", 56 + "[6] FUNC 'f' type_id=5 linkage=static"); 57 + 58 + permute_ids[0] = 0; /* [0] -> [0] */ 59 + permute_ids[1] = 4; /* [1] -> [4] */ 60 + permute_ids[2] = 3; /* [2] -> [3] */ 61 + permute_ids[3] = 5; /* [3] -> [5] */ 62 + permute_ids[4] = 1; /* [4] -> [1] */ 63 + permute_ids[5] = 6; /* [5] -> [6] */ 64 + permute_ids[6] = 2; /* [6] -> [2] */ 65 + err = btf__permute(btf, permute_ids, ARRAY_SIZE(permute_ids), NULL); 66 + if (!ASSERT_OK(err, "btf__permute_base")) 67 + goto done; 68 + permute_base_check(btf); 69 + 70 + /* ids[0] must be 0 for base BTF */ 71 + permute_ids[0] = 4; /* [0] -> [0] */ 72 + permute_ids[1] = 0; /* [1] -> [4] */ 73 + permute_ids[2] = 3; /* [2] -> [3] */ 74 + permute_ids[3] = 5; /* [3] -> [5] */ 75 + permute_ids[4] = 1; /* [4] -> [1] */ 76 + permute_ids[5] = 6; /* [5] -> [6] */ 77 + permute_ids[6] = 2; /* [6] -> [2] */ 78 + err = btf__permute(btf, permute_ids, ARRAY_SIZE(permute_ids), NULL); 79 + if (!ASSERT_ERR(err, "btf__permute_base")) 80 + goto done; 81 + /* BTF is not modified */ 82 + permute_base_check(btf); 83 + 84 + /* id_map_cnt is invalid */ 85 + permute_ids[0] = 0; /* [0] -> [0] */ 86 + permute_ids[1] = 4; /* [1] -> [4] */ 87 + permute_ids[2] = 3; /* [2] -> [3] */ 88 + permute_ids[3] = 5; /* [3] -> [5] */ 89 + permute_ids[4] = 1; /* [4] -> [1] */ 90 + permute_ids[5] = 6; /* [5] -> [6] */ 91 + permute_ids[6] = 2; /* [6] -> [2] */ 92 + err = btf__permute(btf, permute_ids, ARRAY_SIZE(permute_ids) - 1, NULL); 93 + if (!ASSERT_ERR(err, "btf__permute_base")) 94 + goto done; 95 + /* BTF is not modified */ 96 + permute_base_check(btf); 97 + 98 + /* Multiple types can not be mapped to the same ID */ 99 + permute_ids[0] = 0; 100 + permute_ids[1] = 4; 101 + permute_ids[2] = 4; 102 + permute_ids[3] = 5; 103 + permute_ids[4] = 1; 104 + permute_ids[5] = 6; 105 + permute_ids[6] = 2; 106 + err = btf__permute(btf, permute_ids, ARRAY_SIZE(permute_ids), NULL); 107 + if (!ASSERT_ERR(err, "btf__permute_base")) 108 + goto done; 109 + /* BTF is not modified */ 110 + permute_base_check(btf); 111 + 112 + /* Type ID must be valid */ 113 + permute_ids[0] = 0; 114 + permute_ids[1] = 4; 115 + permute_ids[2] = 3; 116 + permute_ids[3] = 5; 117 + permute_ids[4] = 1; 118 + permute_ids[5] = 7; 119 + permute_ids[6] = 2; 120 + err = btf__permute(btf, permute_ids, ARRAY_SIZE(permute_ids), NULL); 121 + if (!ASSERT_ERR(err, "btf__permute_base")) 122 + goto done; 123 + /* BTF is not modified */ 124 + permute_base_check(btf); 125 + 126 + done: 127 + btf__free(btf); 128 + } 129 + 130 + static void permute_split_check(struct btf *btf) 131 + { 132 + VALIDATE_RAW_BTF( 133 + btf, 134 + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", 135 + "[2] PTR '(anon)' type_id=1", 136 + "[3] STRUCT 's2' size=4 vlen=1\n" 137 + "\t'm' type_id=1 bits_offset=0", 138 + "[4] FUNC 'f' type_id=5 linkage=static", 139 + "[5] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" 140 + "\t'p' type_id=2", 141 + "[6] STRUCT 's1' size=4 vlen=1\n" 142 + "\t'm' type_id=1 bits_offset=0"); 143 + } 144 + 145 + /* Ensure btf__permute works as expected in the split-BTF scenario */ 146 + static void test_permute_split(void) 147 + { 148 + struct btf *split_btf = NULL, *base_btf = NULL; 149 + __u32 permute_ids[4]; 150 + int err, start_id; 151 + 152 + base_btf = btf__new_empty(); 153 + if (!ASSERT_OK_PTR(base_btf, "empty_main_btf")) 154 + return; 155 + 156 + btf__add_int(base_btf, "int", 4, BTF_INT_SIGNED); /* [1] int */ 157 + btf__add_ptr(base_btf, 1); /* [2] ptr to int */ 158 + VALIDATE_RAW_BTF( 159 + base_btf, 160 + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", 161 + "[2] PTR '(anon)' type_id=1"); 162 + split_btf = btf__new_empty_split(base_btf); 163 + if (!ASSERT_OK_PTR(split_btf, "empty_split_btf")) 164 + goto cleanup; 165 + btf__add_struct(split_btf, "s1", 4); /* [3] struct s1 { */ 166 + btf__add_field(split_btf, "m", 1, 0, 0); /* int m; */ 167 + /* } */ 168 + btf__add_struct(split_btf, "s2", 4); /* [4] struct s2 { */ 169 + btf__add_field(split_btf, "m", 1, 0, 0); /* int m; */ 170 + /* } */ 171 + btf__add_func_proto(split_btf, 1); /* [5] int (*)(int p); */ 172 + btf__add_func_param(split_btf, "p", 2); 173 + btf__add_func(split_btf, "f", BTF_FUNC_STATIC, 5); /* [6] int f(int *p); */ 174 + 175 + VALIDATE_RAW_BTF( 176 + split_btf, 177 + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", 178 + "[2] PTR '(anon)' type_id=1", 179 + "[3] STRUCT 's1' size=4 vlen=1\n" 180 + "\t'm' type_id=1 bits_offset=0", 181 + "[4] STRUCT 's2' size=4 vlen=1\n" 182 + "\t'm' type_id=1 bits_offset=0", 183 + "[5] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n" 184 + "\t'p' type_id=2", 185 + "[6] FUNC 'f' type_id=5 linkage=static"); 186 + 187 + start_id = btf__type_cnt(base_btf); 188 + permute_ids[3 - start_id] = 6; /* [3] -> [6] */ 189 + permute_ids[4 - start_id] = 3; /* [4] -> [3] */ 190 + permute_ids[5 - start_id] = 5; /* [5] -> [5] */ 191 + permute_ids[6 - start_id] = 4; /* [6] -> [4] */ 192 + err = btf__permute(split_btf, permute_ids, ARRAY_SIZE(permute_ids), NULL); 193 + if (!ASSERT_OK(err, "btf__permute_split")) 194 + goto cleanup; 195 + permute_split_check(split_btf); 196 + 197 + /* 198 + * For split BTF, id_map_cnt must equal to the number of types 199 + * added on top of base BTF 200 + */ 201 + permute_ids[3 - start_id] = 4; 202 + permute_ids[4 - start_id] = 3; 203 + permute_ids[5 - start_id] = 5; 204 + permute_ids[6 - start_id] = 6; 205 + err = btf__permute(split_btf, permute_ids, ARRAY_SIZE(permute_ids) - 1, NULL); 206 + if (!ASSERT_ERR(err, "btf__permute_split")) 207 + goto cleanup; 208 + /* BTF is not modified */ 209 + permute_split_check(split_btf); 210 + 211 + /* Multiple types can not be mapped to the same ID */ 212 + permute_ids[3 - start_id] = 4; 213 + permute_ids[4 - start_id] = 3; 214 + permute_ids[5 - start_id] = 3; 215 + permute_ids[6 - start_id] = 6; 216 + err = btf__permute(split_btf, permute_ids, ARRAY_SIZE(permute_ids), NULL); 217 + if (!ASSERT_ERR(err, "btf__permute_split")) 218 + goto cleanup; 219 + /* BTF is not modified */ 220 + permute_split_check(split_btf); 221 + 222 + /* Can not map to base ID */ 223 + permute_ids[3 - start_id] = 4; 224 + permute_ids[4 - start_id] = 2; 225 + permute_ids[5 - start_id] = 5; 226 + permute_ids[6 - start_id] = 6; 227 + err = btf__permute(split_btf, permute_ids, ARRAY_SIZE(permute_ids), NULL); 228 + if (!ASSERT_ERR(err, "btf__permute_split")) 229 + goto cleanup; 230 + /* BTF is not modified */ 231 + permute_split_check(split_btf); 232 + 233 + cleanup: 234 + btf__free(split_btf); 235 + btf__free(base_btf); 236 + } 237 + 238 + void test_btf_permute(void) 239 + { 240 + if (test__start_subtest("permute_base")) 241 + test_permute_base(); 242 + if (test__start_subtest("permute_split")) 243 + test_permute_split(); 244 + }