tools/lib/bpf/libbpf.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+)
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@xiaomi.com>
---
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
On Thu, Mar 26, 2026 at 1:43 PM zhidao su <soolaugust@gmail.com> wrote:
>
> 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@xiaomi.com>
> ---
> tools/lib/bpf/libbpf.c | 35 +++++++++++++++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
>
libbpf doesn't support (yet) matching triple-underscore-suffixed
kfuncs on the kernel side. We plan to support that, but it should be
done differently.
For now, as a work around, if you need libbpf to match
scx_bpf_dsq_move_to_local___v2, you can add yet another triple
underscore: scx_bpf_dsq_move_to_local___v2___andimeanit
pw-bot: cr
> 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
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 1eaa7527d4da..85268406c0f5 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 (ext->essent_name) {
This commit fixes a bug introduced by the patch that added
triple-underscore flavor support for kfunc relocation. Should it
include a Fixes: tag?
Suggested:
Fixes: 5964a223f5e4 ("libbpf: Support triple-underscore flavors for kfunc relocation")
> + struct module_btf *exact_mod_btf = NULL;
[ ... ]
> + }
> +
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23617578044
© 2016 - 2026 Red Hat, Inc.