From 7890a57356a2103f7cc79edcd87fb8ed7c23bdcc Mon Sep 17 00:00:00 2001 From: Alan Maguire Date: Thu, 24 Sep 2026 12:14:24 +0100 Subject: [PATCH] bpftool: Handle multi-split BTF by supporting multiple base BTFs For bpftool to be able to dump .BTF.inline data in /sys/kernel/btf/foo.inline for module foo, it needs to support multi-split BTF because the parent-child relationship of BTF inline data for modules is vmlinux BTF data module BTF data module BTF inline data So for example to dump BTF inline info for xfs we would run $ bpftool btf dump -B /sys/kernel/btf/vmlinux -B /sys/kernel/btf/xfs file /sys/kernel/btf/xfs.inline Multiple bases are specified with the vmlinux base BTF first (parent) followed by the xfs BTF (child), and finally the XFS BTF inline info. Update help text accordingly to reflect the ability to specify multiple in-order root-to-branch bases. Signed-off-by: Alan Maguire Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20260924111428.75957-8-alan.maguire@oracle.com --- tools/bpf/bpftool/btf.c | 2 +- tools/bpf/bpftool/main.c | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index bca0a3982f09..65e8a29277e6 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c @@ -1545,7 +1545,7 @@ static int do_help(int argc, char **argv) " " HELP_SPEC_MAP "\n" " " HELP_SPEC_PROGRAM "\n" " " HELP_SPEC_OPTIONS " |\n" - " {-B|--base-btf} }\n" + " {[{-B|--base-btf} FILE]... }}\n" "", bin_name, "btf"); diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 5ababd8f7d0a..83884b21e708 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -466,6 +466,7 @@ int main(int argc, char **argv) { "base-btf", required_argument, NULL, 'B' }, { 0 } }; + struct btf *new_base_btf = NULL, *root_base_btf = NULL; bool version_requested = false; int opt, ret; @@ -515,12 +516,16 @@ int main(int argc, char **argv) verifier_logs = true; break; case 'B': - base_btf = btf__parse(optarg, NULL); - if (!base_btf) { + /* handle multi-split BTF */ + new_base_btf = btf__parse_split(optarg, base_btf); + if (!new_base_btf) { p_err("failed to parse base BTF at '%s': %d\n", optarg, -errno); return -1; } + base_btf = new_base_btf; + if (!root_base_btf) + root_base_btf = base_btf; break; case 'L': use_loader = true; @@ -567,7 +572,20 @@ int main(int argc, char **argv) if (json_output) jsonw_destroy(&json_wtr); - btf__free(base_btf); + while (base_btf) { + bool is_root = base_btf == root_base_btf; + + new_base_btf = (struct btf *)btf__base_btf(base_btf); + btf__free(base_btf); + /* + * Do not free base BTF that is an owned .BTF.base ; leads + * to a double-free, so only free as far as the root base + * we explicitly read with -B above. + */ + if (is_root) + break; + base_btf = new_base_btf; + } return ret; }