[PATCH bpf-next v4 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
From: Fuyu Zhao
Date: Mon Aug 24 2026 - 08:54:10 EST
Add btf_module_names and nr_btf_module_names fields to
bpf_object_open_opts to support selective kernel module BTF loading.
When btf_module_names is provided, libbpf loads BTFs only for the
specified kernel modules and skips other module BTFs. If
btf_module_names is NULL, all module BTFs are loaded as before.
This avoids unnecessary module BTF loading and reduces BPF object
loading time when only a subset of kernel module BTFs is needed.
Suggested-by: Andrii Nakryiko <andrii.nakryiko@xxxxxxxxx>
Signed-off-by: Fuyu Zhao <zhaofuyu@xxxxxxxx>
---
tools/lib/bpf/libbpf.c | 105 +++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 24 +++++++++-
2 files changed, 128 insertions(+), 1 deletion(-)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 514e4e9daa82..6f5d3213b3e5 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -779,6 +779,9 @@ struct bpf_object {
char *token_path;
int token_fd;
+ char **btf_module_names;
+ size_t nr_btf_module_names;
+
char path[];
};
@@ -5803,6 +5806,94 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
return 0;
}
+static void bpf_object_free_btf_module_names(struct bpf_object *obj)
+{
+ size_t i;
+
+ if (!obj->btf_module_names)
+ return;
+
+ for (i = 0; i < obj->nr_btf_module_names; i++)
+ zfree(&obj->btf_module_names[i]);
+ zfree(&obj->btf_module_names);
+ obj->nr_btf_module_names = 0;
+}
+
+static int bpf_object_init_btf_module_names(struct bpf_object *obj,
+ const struct bpf_object_open_opts *opts)
+{
+ const char **names;
+ size_t i, j, cnt;
+ int err;
+
+ names = OPTS_GET(opts, btf_module_names, NULL);
+ if (!names)
+ return 0;
+
+ cnt = OPTS_GET(opts, nr_btf_module_names, 0);
+
+ /*
+ * Keep btf_module_names non-NULL to distinguish an empty filter from
+ * the default behavior of loading all module BTFs.
+ */
+ obj->btf_module_names = calloc(cnt ?: 1,
+ sizeof(*obj->btf_module_names));
+ if (!obj->btf_module_names)
+ return -ENOMEM;
+
+ for (i = 0; i < cnt; i++) {
+ if (!names[i] || !names[i][0]) {
+ pr_warn("invalid kernel module BTF name at index %zu\n", i);
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ for (j = 0; j < i; j++) {
+ if (strcmp(obj->btf_module_names[j], names[i]) == 0) {
+ pr_warn("duplicate kernel module BTF name '%s'\n",
+ names[i]);
+ err = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ obj->btf_module_names[i] = strdup(names[i]);
+ if (!obj->btf_module_names[i]) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ obj->nr_btf_module_names++;
+ }
+ return 0;
+
+err_out:
+ bpf_object_free_btf_module_names(obj);
+ return err;
+}
+
+static bool is_module_btf_needed(const struct bpf_object *obj, const char *name)
+{
+ size_t i;
+
+ if (!obj->btf_module_names)
+ return true;
+
+ for (i = 0; i < obj->nr_btf_module_names; i++) {
+ if (strcmp(obj->btf_module_names[i], name) == 0)
+ return true;
+ }
+
+ pr_debug("skipping module BTF '%s', not in btf_module_names\n", name);
+ return false;
+}
+
+static bool all_needed_module_btfs_loaded(const struct bpf_object *obj)
+{
+ return obj->btf_module_names &&
+ obj->nr_btf_module_names == obj->btf_module_cnt;
+}
+
static int load_module_btfs(struct bpf_object *obj)
{
struct bpf_btf_info info;
@@ -5867,6 +5958,11 @@ static int load_module_btfs(struct bpf_object *obj)
continue;
}
+ if (!is_module_btf_needed(obj, name)) {
+ close(fd);
+ continue;
+ }
+
btf = btf_get_from_fd(fd, obj->btf_vmlinux);
err = libbpf_get_error(btf);
if (err) {
@@ -5891,6 +5987,9 @@ static int load_module_btfs(struct bpf_object *obj)
break;
}
obj->btf_module_cnt++;
+
+ if (all_needed_module_btfs_loaded(obj))
+ break;
}
if (err) {
@@ -8508,6 +8607,10 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf,
}
}
+ err = bpf_object_init_btf_module_names(obj, opts);
+ if (err)
+ goto out;
+
err = bpf_object__elf_init(obj);
err = err ? : bpf_object__elf_collect(obj);
err = err ? : bpf_object__collect_externs(obj);
@@ -9629,6 +9732,8 @@ void bpf_object__close(struct bpf_object *obj)
close(obj->jumptable_maps[i].fd);
zfree(&obj->jumptable_maps);
+ bpf_object_free_btf_module_names(obj);
+
free(obj);
}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b965ad571540..838602319da8 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -224,10 +224,32 @@ struct bpf_object_open_opts {
* point (/sys/fs/bpf), in case this default behavior is undesirable.
*/
const char *bpf_token_path;
+ /*
+ * Optional list of kernel module names whose BTFs should be loaded.
+ * nr_btf_module_names specifies the number of entries in
+ * btf_module_names.
+ *
+ * If btf_module_names is NULL, all module BTFs are loaded,
+ * preserving the default behavior. Otherwise, only the BTFs of
+ * the listed modules are loaded. A non-NULL btf_module_names
+ * with nr_btf_module_names equal to zero means that no module
+ * BTFs are loaded.
+ *
+ * The list must not contain duplicate entries; otherwise
+ * -EINVAL is returned.
+ *
+ * This affects:
+ * - BPF CO-RE relocations against types defined in modules;
+ * - BTF-based resolution of function attach targets for
+ * fentry/fexit/fmod_ret/freplace/LSM programs;
+ * - extern (ksym) resolution for kernel symbols defined in modules.
+ */
+ const char **btf_module_names;
+ size_t nr_btf_module_names;
size_t :0;
};
-#define bpf_object_open_opts__last_field bpf_token_path
+#define bpf_object_open_opts__last_field nr_btf_module_names
/**
* @brief **bpf_object__open()** creates a bpf_object by opening
--
2.34.1