Re: [PATCH] libbpf: resolve versioned kfuncs by exact name before essential-name lookup
From: Andrii Nakryiko
Date: Thu Mar 26 2026 - 17:33:28 EST
On Thu, Mar 26, 2026 at 1:43 PM zhidao su <soolaugust@xxxxxxxxx> 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@xxxxxxxxxx>
> ---
> 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
>