From cc54fb8f732ae5199c247826ffc017bb0c1ecee0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 30 Jul 2026 10:41:42 +0200 Subject: [PATCH] bpf-util: add helper to probe the kernel for a kfunc Add bpf_kernel_has_kfunc() which checks whether the kernel's vmlinux BTF advertises a function of a given name. The three btf__* symbols it needs are optional in the dlopen table. btf__load_vmlinux_btf() only exists since libbpf 0.5 and a libbpf that lacks any of them should degrade this probe, not fail dlopen_bpf() and with it all of BPF. Note, libbpf < 1.0 returns error pointers instead of NULL, so the result is checked with bpf_get_error_translated(). Finding the name doesn't tell us all that much though. It tells us that the function exists, not that it is registered as a kfunc for the program type we care about. So callers still have to be prepared for the program load to fail. Signed-off-by: Christian Brauner (Amutable) --- src/shared/bpf-util.c | 41 +++++++++++++++++++++++++++++++++++++++-- src/shared/bpf-util.h | 12 ++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/shared/bpf-util.c b/src/shared/bpf-util.c index ebfc9b30bb..b1a8a25032 100644 --- a/src/shared/bpf-util.c +++ b/src/shared/bpf-util.c @@ -54,6 +54,9 @@ static int missing_bpf_token_create(int bpffs_fd, struct bpf_token_create_opts * return -ENOSYS; } DLSYM_PROTOTYPE(bpf_token_create) = missing_bpf_token_create; +DLSYM_PROTOTYPE(btf__find_by_name_kind) = NULL; +DLSYM_PROTOTYPE(btf__free) = NULL; +DLSYM_PROTOTYPE(btf__load_vmlinux_btf) = NULL; DLSYM_PROTOTYPE(libbpf_set_print) = NULL; DLSYM_PROTOTYPE(ring_buffer__epoll_fd) = NULL; DLSYM_PROTOTYPE(ring_buffer__free) = NULL; @@ -143,12 +146,18 @@ int dlopen_bpf(int log_level) { "Neither libbpf.so.1 nor libbpf.so.0 are installed, cgroup BPF features disabled."); /* Version-specific symbols: bpf_create_map exists only in libbpf < 1.0; bpf_map_create and - * bpf_object__next_map only in 0.7+. bpf_token_create only in 1.5+. Unresolved prototypes keep - * their initializers (NULL, or a fallback returning -ENOSYS for bpf_token_create). */ + * bpf_object__next_map only in 0.7+. bpf_token_create only in 1.5+. btf__load_vmlinux_btf only in + * 0.5+; its two btf__* companions are older, but keep the whole probe trio optional so a missing + * symbol degrades bpf_kernel_has_kfunc() instead of failing dlopen_bpf() entirely. Unresolved + * prototypes keep their initializers (NULL, or a fallback returning -ENOSYS for + * bpf_token_create). */ DLSYM_OPTIONAL(bpf_dl, bpf_create_map); DLSYM_OPTIONAL(bpf_dl, bpf_map_create); DLSYM_OPTIONAL(bpf_dl, bpf_object__next_map); DLSYM_OPTIONAL(bpf_dl, bpf_token_create); + DLSYM_OPTIONAL(bpf_dl, btf__find_by_name_kind); + DLSYM_OPTIONAL(bpf_dl, btf__free); + DLSYM_OPTIONAL(bpf_dl, btf__load_vmlinux_btf); /* We set the print helper unconditionally. Otherwise libbpf will emit not useful log messages. */ (void) sym_libbpf_set_print(bpf_print_func); @@ -177,4 +186,32 @@ int bpf_get_error_translated(const void *ptr) { return r; } } + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct btf *, sym_btf__free, NULL); + +bool bpf_kernel_has_kfunc(const char *name) { + _cleanup_(sym_btf__freep) struct btf *btf = NULL; + int id, r; + + assert(name); + + if (dlopen_bpf(LOG_DEBUG) < 0) + return false; + + if (!sym_btf__load_vmlinux_btf || !sym_btf__find_by_name_kind || !sym_btf__free) { + log_debug("libbpf too old to probe the kernel BTF for functions, assuming %s() is unavailable.", name); + return false; + } + + btf = sym_btf__load_vmlinux_btf(); + r = bpf_get_error_translated(btf); /* libbpf < 1.0 returns error pointers, not NULL; btf__free() copes with both */ + if (r != 0) { + log_debug_errno(r, "Failed to load vmlinux BTF, assuming kfunc %s() is unavailable: %m", name); + return false; + } + + id = sym_btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); + log_debug("Kernel BTF %s %s().", id > 0 ? "provides" : "lacks", name); + return id > 0; +} #endif diff --git a/src/shared/bpf-util.h b/src/shared/bpf-util.h index 45c264773f..42eb9332ae 100644 --- a/src/shared/bpf-util.h +++ b/src/shared/bpf-util.h @@ -12,6 +12,7 @@ #endif #include /* IWYU pragma: export */ +#include /* IWYU pragma: export */ #include /* IWYU pragma: export */ #include "dlfcn-util.h" @@ -21,6 +22,7 @@ * - bpf_map_create, bpf_object__next_map: available since libbpf 0.7 * - bpf_token_create: available since libbpf 1.5 * - bpf_create_map: removed in libbpf 1.0 + * - btf__load_vmlinux_btf: available since libbpf 0.5 */ DISABLE_WARNING_REDUNDANT_DECLS; /* NOLINTBEGIN(readability-redundant-declaration) */ @@ -30,6 +32,7 @@ extern int bpf_create_map(enum bpf_map_type, int key_size, int value_size, int m extern int bpf_map_create(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *); extern struct bpf_map* bpf_object__next_map(const struct bpf_object *obj, const struct bpf_map *map); extern int bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *opts); +extern struct btf* btf__load_vmlinux_btf(void); /* NOLINTEND(readability-redundant-declaration) */ REENABLE_WARNING; @@ -65,6 +68,9 @@ extern DLSYM_PROTOTYPE(bpf_program__attach_lsm); extern DLSYM_PROTOTYPE(bpf_program__name); extern DLSYM_PROTOTYPE(bpf_program__set_autoload); extern DLSYM_PROTOTYPE(bpf_token_create); +extern DLSYM_PROTOTYPE(btf__find_by_name_kind); +extern DLSYM_PROTOTYPE(btf__free); +extern DLSYM_PROTOTYPE(btf__load_vmlinux_btf); extern DLSYM_PROTOTYPE(libbpf_set_print); extern DLSYM_PROTOTYPE(ring_buffer__epoll_fd); extern DLSYM_PROTOTYPE(ring_buffer__free); @@ -92,6 +98,12 @@ static inline int compat_bpf_map_create( * this helper instead of libbpf_get_error() to ensure some of the known ones are translated into errnos * we understand. */ int bpf_get_error_translated(const void *ptr); + +/* Probe the kernel's vmlinux BTF for a function of the given name, to check whether a BPF kfunc is + * available. This probes vmlinux BTF only, not module BTFs; and BTF advertising the function only proves + * it exists, not that it is registered as a kfunc for a given program type — callers must still handle + * program load failure. */ +bool bpf_kernel_has_kfunc(const char *name); #endif int dlopen_bpf(int log_level) _dlopen_loader_;