Skip to content

Commit 1664891

Browse files
josefbacikqmonnet
authored andcommitted
bpftool: Support merging multiple module BTFs in btf dump
Add support for specifying multiple file sources in 'bpftool btf dump' to generate a single C header containing types from vmlinux plus multiple kernel modules: bpftool btf dump file /sys/kernel/btf/mod1 file /sys/kernel/btf/mod2 format c This is useful for BPF programs that need to access types defined in kernel modules. Previously this required a separate bpftool invocation for each module, producing separate headers that could not be combined due to overlapping vmlinux type definitions. The implementation collects all file paths, then for the multi-file case creates an empty split BTF on the vmlinux base and iteratively merges each module's types into it via btf__add_btf(). The single-file code path is preserved exactly to avoid any regression risk. Auto-detection of vmlinux as the base BTF from sysfs paths works as before. If vmlinux itself appears in the file list it is skipped with a warning since its types are already provided by the base. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Reviewed-by: Alan Maguire <alan.maguire@oracle.com> Link: https://lore.kernel.org/bpf/b19c2760ffe48cec546dd3810d237f8cad20d606.1772657690.git.josef@toxicpanda.com
1 parent f38edf1 commit 1664891

3 files changed

Lines changed: 121 additions & 17 deletions

File tree

bash-completion/bpftool

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,10 +961,14 @@ _bpftool()
961961
*)
962962
# emit extra options
963963
case ${words[3]} in
964-
id|file)
964+
id)
965965
COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
966966
_bpftool_once_attr 'format'
967967
;;
968+
file)
969+
COMPREPLY=( $( compgen -W "root_id file" -- "$cur" ) )
970+
_bpftool_once_attr 'format'
971+
;;
968972
map|prog)
969973
if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
970974
COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )

docs/bpftool-btf.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ BTF COMMANDS
2727
| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
2828
| **bpftool** **btf help**
2929
|
30-
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
30+
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* [**file** *FILE*]... }
3131
| *FORMAT* := { **raw** | **c** [**unsorted**] }
3232
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
3333
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
@@ -58,9 +58,12 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
5858
When **prog** is provided, it's expected that program has associated BTF
5959
object with BTF types.
6060

61-
When specifying *FILE*, an ELF file is expected, containing .BTF section
62-
with well-defined BTF binary format data, typically produced by clang or
63-
pahole.
61+
When specifying *FILE*, an ELF file or a raw BTF file (e.g. from
62+
``/sys/kernel/btf/``) is expected. Multiple **file** arguments may be
63+
given to merge BTF from several kernel modules into a single output.
64+
When sysfs paths are used, vmlinux BTF is loaded automatically as the
65+
base; if vmlinux itself appears in the file list it is skipped.
66+
A base BTF can also be specified explicitly with **-B**.
6467

6568
**format** option can be used to override default (raw) output format. Raw
6669
(**raw**) or C-syntax (**c**) output formats are supported. With C-style

src/btf.c

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define FASTCALL_DECL_TAG "bpf_fastcall"
2929

3030
#define MAX_ROOT_IDS 16
31+
#define MAX_BTF_FILES 64
3132

3233
static const char * const btf_kind_str[NR_BTF_KINDS] = {
3334
[BTF_KIND_UNKN] = "UNKNOWN",
@@ -878,6 +879,45 @@ static bool btf_is_kernel_module(__u32 btf_id)
878879
return btf_info.kernel_btf && strncmp(btf_name, "vmlinux", sizeof(btf_name)) != 0;
879880
}
880881

882+
static struct btf *merge_btf_files(const char **files, int nr_files,
883+
struct btf *vmlinux_base)
884+
{
885+
struct btf *combined, *mod;
886+
int ret;
887+
888+
combined = btf__new_empty_split(vmlinux_base);
889+
if (!combined) {
890+
p_err("failed to create combined BTF: %s", strerror(errno));
891+
return NULL;
892+
}
893+
894+
for (int j = 0; j < nr_files; j++) {
895+
mod = btf__parse_split(files[j], vmlinux_base);
896+
if (!mod) {
897+
p_err("failed to load BTF from %s: %s", files[j], strerror(errno));
898+
btf__free(combined);
899+
return NULL;
900+
}
901+
902+
ret = btf__add_btf(combined, mod);
903+
btf__free(mod);
904+
if (ret < 0) {
905+
p_err("failed to merge BTF from %s: %s", files[j], strerror(-ret));
906+
btf__free(combined);
907+
return NULL;
908+
}
909+
}
910+
911+
ret = btf__dedup(combined, NULL);
912+
if (ret) {
913+
p_err("failed to dedup combined BTF: %s", strerror(-ret));
914+
btf__free(combined);
915+
return NULL;
916+
}
917+
918+
return combined;
919+
}
920+
881921
static int do_dump(int argc, char **argv)
882922
{
883923
bool dump_c = false, sort_dump_c = true;
@@ -958,20 +998,76 @@ static int do_dump(int argc, char **argv)
958998
NEXT_ARG();
959999
} else if (is_prefix(src, "file")) {
9601000
const char sysfs_prefix[] = "/sys/kernel/btf/";
1001+
struct btf *vmlinux_base = base_btf;
1002+
const char *files[MAX_BTF_FILES];
1003+
int nr_files = 0;
9611004

962-
if (!base_btf &&
963-
strncmp(*argv, sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0 &&
964-
strcmp(*argv, sysfs_vmlinux) != 0)
965-
base = get_vmlinux_btf_from_sysfs();
966-
967-
btf = btf__parse_split(*argv, base ?: base_btf);
968-
if (!btf) {
969-
err = -errno;
970-
p_err("failed to load BTF from %s: %s",
971-
*argv, strerror(errno));
972-
goto done;
1005+
/* First grab our argument, filtering out the sysfs_vmlinux. */
1006+
if (strcmp(*argv, sysfs_vmlinux) != 0) {
1007+
files[nr_files++] = *argv;
1008+
} else {
1009+
p_info("skipping %s (will be loaded as base)", *argv);
9731010
}
9741011
NEXT_ARG();
1012+
1013+
while (argc && is_prefix(*argv, "file")) {
1014+
NEXT_ARG();
1015+
if (!REQ_ARGS(1)) {
1016+
err = -EINVAL;
1017+
goto done;
1018+
}
1019+
/* Filter out any sysfs vmlinux entries. */
1020+
if (strcmp(*argv, sysfs_vmlinux) == 0) {
1021+
p_info("skipping %s (will be loaded as base)", *argv);
1022+
NEXT_ARG();
1023+
continue;
1024+
}
1025+
if (nr_files >= MAX_BTF_FILES) {
1026+
p_err("too many BTF files (max %d)", MAX_BTF_FILES);
1027+
err = -E2BIG;
1028+
goto done;
1029+
}
1030+
files[nr_files++] = *argv;
1031+
NEXT_ARG();
1032+
}
1033+
1034+
/* Auto-detect vmlinux base if any file is from sysfs */
1035+
if (!vmlinux_base) {
1036+
for (int j = 0; j < nr_files; j++) {
1037+
if (strncmp(files[j], sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0) {
1038+
base = get_vmlinux_btf_from_sysfs();
1039+
vmlinux_base = base;
1040+
break;
1041+
}
1042+
}
1043+
}
1044+
1045+
/* All files were the sysfs_vmlinux, handle it like we used to */
1046+
if (nr_files == 0) {
1047+
nr_files = 1;
1048+
files[0] = sysfs_vmlinux;
1049+
}
1050+
1051+
if (nr_files == 1) {
1052+
btf = btf__parse_split(files[0], base ?: base_btf);
1053+
if (!btf) {
1054+
err = -errno;
1055+
p_err("failed to load BTF from %s: %s", files[0], strerror(errno));
1056+
goto done;
1057+
}
1058+
} else {
1059+
if (!vmlinux_base) {
1060+
p_err("base BTF is required when merging multiple BTF files; use -B/--base-btf or use sysfs paths");
1061+
err = -EINVAL;
1062+
goto done;
1063+
}
1064+
1065+
btf = merge_btf_files(files, nr_files, vmlinux_base);
1066+
if (!btf) {
1067+
err = -errno;
1068+
goto done;
1069+
}
1070+
}
9751071
} else {
9761072
err = -1;
9771073
p_err("unrecognized BTF source specifier: '%s'", src);
@@ -1445,7 +1541,8 @@ static int do_help(int argc, char **argv)
14451541
" %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n"
14461542
" %1$s %2$s help\n"
14471543
"\n"
1448-
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
1544+
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] |\n"
1545+
" file FILE [file FILE]... }\n"
14491546
" FORMAT := { raw | c [unsorted] }\n"
14501547
" " HELP_SPEC_MAP "\n"
14511548
" " HELP_SPEC_PROGRAM "\n"

0 commit comments

Comments
 (0)