On Fri, Jan 26, 2024 at 2:04 PM Bryce Kahle <git@xxxxxxxxxxxxxx> wrote: > > From: Bryce Kahle <bryce.kahle@xxxxxxxxxxxxx> > > Enables a user to generate minimized kernel module BTF. > > If an eBPF program probes a function within a kernel module or uses > types that come from a kernel module, split BTF is required. The split > module BTF contains only the BTF types that are unique to the module. > It will reference the base/vmlinux BTF types and always starts its type > IDs at X+1 where X is the largest type ID in the base BTF. > > Minimization allows a user to ship only the types necessary to do > relocations for the program(s) in the provided eBPF object file(s). A > minimized module BTF will still not contain vmlinux BTF types, so you > should always minimize the vmlinux file first, and then minimize the > kernel module file. > > Example: > > bpftool gen min_core_btf vmlinux.btf vm-min.btf prog.bpf.o > bpftool -B vm-min.btf gen min_core_btf mod.btf mod-min.btf prog.bpf.o > > Signed-off-by: Bryce Kahle <bryce.kahle@xxxxxxxxxxxxx> > --- > .../bpf/bpftool/Documentation/bpftool-gen.rst | 18 +++++++++++++++++- > tools/bpf/bpftool/gen.c | 17 ++++++++++++----- > 2 files changed, 29 insertions(+), 6 deletions(-) > > diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst > index 5006e724d..e067d3b05 100644 > --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst > +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst > @@ -16,7 +16,7 @@ SYNOPSIS > > **bpftool** [*OPTIONS*] **gen** *COMMAND* > > - *OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } } > + *OPTIONS* := { |COMMON_OPTIONS| | { **-B** | **--base-btf** } | { **-L** | **--use-loader** } } > > *COMMAND* := { **object** | **skeleton** | **help** } > > @@ -202,6 +202,14 @@ OPTIONS > ======= > .. include:: common_options.rst > > + -B, --base-btf *FILE* > + Pass a base BTF object. Base BTF objects are typically used > + with BTF objects for kernel modules. To avoid duplicating > + all kernel symbols required by modules, BTF objects for > + modules are "split", they are built incrementally on top of > + the kernel (vmlinux) BTF object. So the base BTF reference > + should usually point to the kernel BTF. > + > -L, --use-loader > For skeletons, generate a "light" skeleton (also known as "loader" > skeleton). A light skeleton contains a loader eBPF program. It does > @@ -444,3 +452,11 @@ ones given to min_core_btf. > obj = bpf_object__open_file("one.bpf.o", &opts); > > ... > + > +Kernel module BTF may also be minimized by using the -B option: > + > +**$ bpftool -B 5.4.0-smaller.btf gen min_core_btf 5.4.0-module.btf 5.4.0-module-smaller.btf one.bpf.o** > + > +A minimized module BTF will still not contain vmlinux BTF types, so you > +should always minimize the vmlinux file first, and then minimize the > +kernel module file. > diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c > index ee3ce2b80..634c809a5 100644 > --- a/tools/bpf/bpftool/gen.c > +++ b/tools/bpf/bpftool/gen.c > @@ -1630,6 +1630,7 @@ static int do_help(int argc, char **argv) > " %1$s %2$s help\n" > "\n" > " " HELP_SPEC_OPTIONS " |\n" > + " {-B|--base-btf} |\n" > " {-L|--use-loader} }\n" > "", > bin_name, "gen"); > @@ -1695,14 +1696,14 @@ btfgen_new_info(const char *targ_btf_path) > if (!info) > return NULL; > > - info->src_btf = btf__parse(targ_btf_path, NULL); > + info->src_btf = btf__parse_split(targ_btf_path, base_btf); > if (!info->src_btf) { > err = -errno; > p_err("failed parsing '%s' BTF file: %s", targ_btf_path, strerror(errno)); > goto err_out; > } > > - info->marked_btf = btf__parse(targ_btf_path, NULL); > + info->marked_btf = btf__parse_split(targ_btf_path, base_btf); > if (!info->marked_btf) { > err = -errno; > p_err("failed parsing '%s' BTF file: %s", targ_btf_path, strerror(errno)); > @@ -2141,10 +2142,16 @@ static struct btf *btfgen_get_btf(struct btfgen_info *info) > { > struct btf *btf_new = NULL; > unsigned int *ids = NULL; > + const struct btf *base; > unsigned int i, n = btf__type_cnt(info->marked_btf); > + int start_id = 1; > int err = 0; > > - btf_new = btf__new_empty(); > + base = btf__base_btf(info->src_btf); > + if (base) > + start_id = btf__type_cnt(base); stylistic nit, I'd do: int start_id, err = 0; and then here start_id = base ? btf__type_cnt(base) : 1; I'd also name base as base_btf to make it clearer that it's a btf object > + > + btf_new = btf__new_empty_split((struct btf *)base); um... and this cast didn't trigger any warnings for you? It might work ok currently, but clearly the code expects that base_btf might be modified, so sharing the same base_btf between two split BTF instances is just a recipe for a hard-to-debug issues, long term. Let's create a copy of base BTF instead? You can use btf__raw_data() + btf__new() to make a simple clone pw-bot: cr > if (!btf_new) { > err = -errno; > goto err_out; > @@ -2157,7 +2164,7 @@ static struct btf *btfgen_get_btf(struct btfgen_info *info) > } > > /* first pass: add all marked types to btf_new and add their new ids to the ids map */ > - for (i = 1; i < n; i++) { > + for (i = start_id; i < n; i++) { > const struct btf_type *cloned_type, *type; > const char *name; > int new_id; > @@ -2213,7 +2220,7 @@ static struct btf *btfgen_get_btf(struct btfgen_info *info) > } > > /* second pass: fix up type ids */ > - for (i = 1; i < btf__type_cnt(btf_new); i++) { > + for (i = start_id; i < btf__type_cnt(btf_new); i++) { > struct btf_type *btf_type = (struct btf_type *) btf__type_by_id(btf_new, i); > > err = btf_type_visit_type_ids(btf_type, btfgen_remap_id, ids); > -- > 2.25.1 > >