mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 03:11:11 -04:00
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
This commit is contained in:
committed by
Andrii Nakryiko
parent
be872ccf37
commit
d8d5c01511
@@ -27,7 +27,7 @@ BTF COMMANDS
|
||||
| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
|
||||
| **bpftool** **btf help**
|
||||
|
|
||||
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
|
||||
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* [**file** *FILE*]... }
|
||||
| *FORMAT* := { **raw** | **c** [**unsorted**] }
|
||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
||||
| *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*]
|
||||
When **prog** is provided, it's expected that program has associated BTF
|
||||
object with BTF types.
|
||||
|
||||
When specifying *FILE*, an ELF file is expected, containing .BTF section
|
||||
with well-defined BTF binary format data, typically produced by clang or
|
||||
pahole.
|
||||
When specifying *FILE*, an ELF file or a raw BTF file (e.g. from
|
||||
``/sys/kernel/btf/``) is expected. Multiple **file** arguments may be
|
||||
given to merge BTF from several kernel modules into a single output.
|
||||
When sysfs paths are used, vmlinux BTF is loaded automatically as the
|
||||
base; if vmlinux itself appears in the file list it is skipped.
|
||||
A base BTF can also be specified explicitly with **-B**.
|
||||
|
||||
**format** option can be used to override default (raw) output format. Raw
|
||||
(**raw**) or C-syntax (**c**) output formats are supported. With C-style
|
||||
|
||||
@@ -961,10 +961,14 @@ _bpftool()
|
||||
*)
|
||||
# emit extra options
|
||||
case ${words[3]} in
|
||||
id|file)
|
||||
id)
|
||||
COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
|
||||
_bpftool_once_attr 'format'
|
||||
;;
|
||||
file)
|
||||
COMPREPLY=( $( compgen -W "root_id file" -- "$cur" ) )
|
||||
_bpftool_once_attr 'format'
|
||||
;;
|
||||
map|prog)
|
||||
if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
|
||||
COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define FASTCALL_DECL_TAG "bpf_fastcall"
|
||||
|
||||
#define MAX_ROOT_IDS 16
|
||||
#define MAX_BTF_FILES 64
|
||||
|
||||
static const char * const btf_kind_str[NR_BTF_KINDS] = {
|
||||
[BTF_KIND_UNKN] = "UNKNOWN",
|
||||
@@ -878,6 +879,45 @@ static bool btf_is_kernel_module(__u32 btf_id)
|
||||
return btf_info.kernel_btf && strncmp(btf_name, "vmlinux", sizeof(btf_name)) != 0;
|
||||
}
|
||||
|
||||
static struct btf *merge_btf_files(const char **files, int nr_files,
|
||||
struct btf *vmlinux_base)
|
||||
{
|
||||
struct btf *combined, *mod;
|
||||
int ret;
|
||||
|
||||
combined = btf__new_empty_split(vmlinux_base);
|
||||
if (!combined) {
|
||||
p_err("failed to create combined BTF: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int j = 0; j < nr_files; j++) {
|
||||
mod = btf__parse_split(files[j], vmlinux_base);
|
||||
if (!mod) {
|
||||
p_err("failed to load BTF from %s: %s", files[j], strerror(errno));
|
||||
btf__free(combined);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = btf__add_btf(combined, mod);
|
||||
btf__free(mod);
|
||||
if (ret < 0) {
|
||||
p_err("failed to merge BTF from %s: %s", files[j], strerror(-ret));
|
||||
btf__free(combined);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = btf__dedup(combined, NULL);
|
||||
if (ret) {
|
||||
p_err("failed to dedup combined BTF: %s", strerror(-ret));
|
||||
btf__free(combined);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
static int do_dump(int argc, char **argv)
|
||||
{
|
||||
bool dump_c = false, sort_dump_c = true;
|
||||
@@ -958,20 +998,76 @@ static int do_dump(int argc, char **argv)
|
||||
NEXT_ARG();
|
||||
} else if (is_prefix(src, "file")) {
|
||||
const char sysfs_prefix[] = "/sys/kernel/btf/";
|
||||
struct btf *vmlinux_base = base_btf;
|
||||
const char *files[MAX_BTF_FILES];
|
||||
int nr_files = 0;
|
||||
|
||||
if (!base_btf &&
|
||||
strncmp(*argv, sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0 &&
|
||||
strcmp(*argv, sysfs_vmlinux) != 0)
|
||||
base = get_vmlinux_btf_from_sysfs();
|
||||
|
||||
btf = btf__parse_split(*argv, base ?: base_btf);
|
||||
if (!btf) {
|
||||
err = -errno;
|
||||
p_err("failed to load BTF from %s: %s",
|
||||
*argv, strerror(errno));
|
||||
goto done;
|
||||
/* First grab our argument, filtering out the sysfs_vmlinux. */
|
||||
if (strcmp(*argv, sysfs_vmlinux) != 0) {
|
||||
files[nr_files++] = *argv;
|
||||
} else {
|
||||
p_info("skipping %s (will be loaded as base)", *argv);
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
||||
while (argc && is_prefix(*argv, "file")) {
|
||||
NEXT_ARG();
|
||||
if (!REQ_ARGS(1)) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
/* Filter out any sysfs vmlinux entries. */
|
||||
if (strcmp(*argv, sysfs_vmlinux) == 0) {
|
||||
p_info("skipping %s (will be loaded as base)", *argv);
|
||||
NEXT_ARG();
|
||||
continue;
|
||||
}
|
||||
if (nr_files >= MAX_BTF_FILES) {
|
||||
p_err("too many BTF files (max %d)", MAX_BTF_FILES);
|
||||
err = -E2BIG;
|
||||
goto done;
|
||||
}
|
||||
files[nr_files++] = *argv;
|
||||
NEXT_ARG();
|
||||
}
|
||||
|
||||
/* Auto-detect vmlinux base if any file is from sysfs */
|
||||
if (!vmlinux_base) {
|
||||
for (int j = 0; j < nr_files; j++) {
|
||||
if (strncmp(files[j], sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0) {
|
||||
base = get_vmlinux_btf_from_sysfs();
|
||||
vmlinux_base = base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All files were the sysfs_vmlinux, handle it like we used to */
|
||||
if (nr_files == 0) {
|
||||
nr_files = 1;
|
||||
files[0] = sysfs_vmlinux;
|
||||
}
|
||||
|
||||
if (nr_files == 1) {
|
||||
btf = btf__parse_split(files[0], base ?: base_btf);
|
||||
if (!btf) {
|
||||
err = -errno;
|
||||
p_err("failed to load BTF from %s: %s", files[0], strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
if (!vmlinux_base) {
|
||||
p_err("base BTF is required when merging multiple BTF files; use -B/--base-btf or use sysfs paths");
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
btf = merge_btf_files(files, nr_files, vmlinux_base);
|
||||
if (!btf) {
|
||||
err = -errno;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = -1;
|
||||
p_err("unrecognized BTF source specifier: '%s'", src);
|
||||
@@ -1445,7 +1541,8 @@ static int do_help(int argc, char **argv)
|
||||
" %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n"
|
||||
" %1$s %2$s help\n"
|
||||
"\n"
|
||||
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
|
||||
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] |\n"
|
||||
" file FILE [file FILE]... }\n"
|
||||
" FORMAT := { raw | c [unsorted] }\n"
|
||||
" " HELP_SPEC_MAP "\n"
|
||||
" " HELP_SPEC_PROGRAM "\n"
|
||||
|
||||
Reference in New Issue
Block a user