[PATCH] libbpf: resolve versioned kfuncs by exact name before essential-name lookup
From: zhidao su
Date: Thu Mar 26 2026 - 16:44:11 EST
Some kfuncs use KF_IMPLICIT_ARGS and export both a base name (where
the verifier strips the implicit aux parameter from the prototype) and
a versioned name with additional user-visible parameters (e.g.
scx_bpf_dsq_move_to_local vs scx_bpf_dsq_move_to_local___v2).
When a BPF program declares a weak __ksym with a flavor suffix such as
func___v2, libbpf strips the suffix to obtain the essential name and
looks up the base entry first. For KF_IMPLICIT_ARGS kfuncs the base
entry has fewer parameters (aux was stripped); the prototype
compatibility check against the versioned declaration (which has the
extra user parameter) then fails, and libbpf leaves the weak symbol
unresolved even though a perfectly-matching versioned kernel entry exists.
Fix by trying an exact-name lookup first when the symbol has an
essential name (i.e. it has a flavor suffix). If the exact name is
found in kernel BTF and its prototype is compatible, use it directly.
Only fall through to the essential-name lookup when the exact name is
absent or incompatible.
Signed-off-by: zhidao su <suzhidao@xxxxxxxxxx>
---
tools/lib/bpf/libbpf.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 0be7017800fe..80e495aebe68 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8630,6 +8630,40 @@ static int bpf_object__resolve_ksym_func_btf_id(struct bpf_object *obj,
local_func_proto_id = ext->ksym.type_id;
+ /*
+ * For kfuncs with a flavor suffix (e.g. "func___v2"), first try the
+ * exact name. If the exact name exists in kernel BTF and its
+ * prototype is compatible, use it directly. This is necessary for
+ * KF_IMPLICIT_ARGS kfuncs that export both a base name (with the
+ * implicit aux parameter stripped) and a versioned name with extra
+ * parameters: the essential-name lookup would find the base entry
+ * (fewer params) and fail the compat check, leaving the weak symbol
+ * unresolved even though the versioned kernel entry is a perfect
+ * match.
+ */
+ if (ext->essent_name) {
+ struct module_btf *exact_mod_btf = NULL;
+ struct btf *exact_btf = NULL;
+ int exact_id;
+
+ exact_id = find_ksym_btf_id(obj, ext->name, BTF_KIND_FUNC,
+ &exact_btf, &exact_mod_btf);
+ if (exact_id >= 0) {
+ kern_func = btf__type_by_id(exact_btf, exact_id);
+ kfunc_proto_id = kern_func->type;
+ ret = bpf_core_types_are_compat(obj->btf,
+ local_func_proto_id,
+ exact_btf,
+ kfunc_proto_id);
+ if (ret > 0) {
+ kern_btf = exact_btf;
+ mod_btf = exact_mod_btf;
+ kfunc_id = exact_id;
+ goto found;
+ }
+ }
+ }
+
kfunc_id = find_ksym_btf_id(obj, ext->essent_name ?: ext->name, BTF_KIND_FUNC, &kern_btf,
&mod_btf);
if (kfunc_id < 0) {
@@ -8655,6 +8689,7 @@ static int bpf_object__resolve_ksym_func_btf_id(struct bpf_object *obj,
return -EINVAL;
}
+found:
/* set index for module BTF fd in fd_array, if unset */
if (mod_btf && !mod_btf->fd_array_idx) {
/* insn->off is s16 */
--
2.43.0