[PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS

Ihor Solodrai posted 10 patches 4 weeks, 1 day ago
There is a newer version of this series
[PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 4 weeks, 1 day ago
Implement BTF modifications in resolve_btfids to support BPF kernel
functions with implicit arguments.

For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
prototype is added to BTF that does not have implicit arguments. The
kfunc's prototype is then updated to a new one in BTF. This prototype
is the intended interface for the BPF programs.

A <func_name>_impl function is added to BTF to make the original kfunc
prototype searchable for the BPF verifier. If a <func_name>_impl
function already exists in BTF, its interpreted as a legacy case, and
this step is skipped.

Whether an argument is implicit is determined by its type:
currently only `struct bpf_prog_aux *` is supported.

As a result, the BTF associated with kfunc is changed from

    __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);

into

    bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
    __bpf_kfunc bpf_foo(int arg1);

For more context see previous discussions and patches [1][2].

[1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
[2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
 1 file changed, 282 insertions(+)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index df39982f51df..b361e726fa36 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -152,6 +152,18 @@ struct object {
 	int nr_typedefs;
 };
 
+#define KF_IMPLICIT_ARGS (1 << 16)
+#define KF_IMPL_SUFFIX "_impl"
+#define MAX_BPF_FUNC_REG_ARGS 5
+#define MAX_KFUNCS 256
+#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
+
+struct btf2btf_context {
+	struct btf *btf;
+	u32 nr_decl_tags;
+	s32 decl_tags[MAX_DECL_TAGS];
+};
+
 static int verbose;
 static int warnings;
 
@@ -972,6 +984,273 @@ static int patch_btfids(const char *btfids_path, const char *elf_path)
 	return err;
 }
 
+static s64 collect_kfunc_ids_by_flags(struct object *obj,
+				      u32 flags,
+				      s32 kfunc_ids[],
+				      const u32 kfunc_ids_sz)
+{
+	Elf_Data *data = obj->efile.idlist;
+	struct rb_node *next;
+	s64 nr_kfuncs = 0;
+	int i;
+
+	next = rb_first(&obj->sets);
+	while (next) {
+		struct btf_id_set8 *set8 = NULL;
+		unsigned long addr, off;
+		struct btf_id *id;
+
+		id = rb_entry(next, struct btf_id, rb_node);
+
+		if (id->kind != BTF_ID_KIND_SET8)
+			goto skip;
+
+		addr = id->addr[0];
+		off = addr - obj->efile.idlist_addr;
+		set8 = data->d_buf + off;
+
+		for (i = 0; i < set8->cnt; i++) {
+			if (set8->pairs[i].flags & flags) {
+				if (nr_kfuncs >= kfunc_ids_sz) {
+					pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
+					       flags, kfunc_ids_sz);
+					return -E2BIG;
+				}
+				kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;
+			}
+		}
+skip:
+		next = rb_next(next);
+	}
+
+	return nr_kfuncs;
+}
+
+static const struct btf_type *btf__unqualified_type_by_id(const struct btf *btf, s32 type_id)
+{
+	const struct btf_type *t = btf__type_by_id(btf, type_id);
+
+	while (btf_is_mod(t))
+		t = btf__type_by_id(btf, t->type);
+
+	return t;
+}
+
+/* Implicit BPF kfunc arguments can only be of particular types */
+static bool btf__is_kf_implicit_arg(const struct btf *btf, const struct btf_param *p)
+{
+	static const char *const kf_implicit_arg_types[] = {
+		"bpf_prog_aux",
+	};
+	const struct btf_type *t;
+	const char *name;
+
+	t = btf__unqualified_type_by_id(btf, p->type);
+	if (!btf_is_ptr(t))
+		return false;
+
+	t = btf__unqualified_type_by_id(btf, t->type);
+	if (!btf_is_struct(t))
+		return false;
+
+	name = btf__name_by_offset(btf, t->name_off);
+	if (!name)
+		return false;
+
+	for (int i = 0; i < ARRAY_SIZE(kf_implicit_arg_types); i++)
+		if (strcmp(name, kf_implicit_arg_types[i]) == 0)
+			return true;
+
+	return false;
+}
+
+/*
+ * For a kfunc with KF_IMPLICIT_ARGS we do the following:
+ *   1. Add a new function with _impl suffix in the name, with the prototype
+ *      of the original kfunc.
+ *   2. Add all decl tags except "bpf_kfunc" for the _impl func.
+ *   3. Add a new function prototype with modified list of arguments:
+ *      omitting implicit args.
+ *   4. Change the prototype of the original kfunc to the new one.
+ *
+ * This way we transform the BTF associated with the kfunc from
+ *	__bpf_kfunc bpf_foo(int arg1, void *implicit_arg);
+ * into
+ *	bpf_foo_impl(int arg1, void *implicit_arg);
+ *	__bpf_kfunc bpf_foo(int arg1);
+ *
+ * If a kfunc with KF_IMPLICIT_ARGS already has an _impl counterpart
+ * in BTF, then it's a legacy case: an _impl function is declared in the
+ * source code. In this case, we can skip adding an _impl function, but we
+ * still have to add a func prototype that omits implicit args.
+ */
+static s64 process_kfunc_with_implicit_args(struct btf2btf_context *ctx, s32 kfunc_id)
+{
+	struct btf_param new_params[MAX_BPF_FUNC_REG_ARGS];
+	const char *kfunc_name, *param_name, *tag_name;
+	s32 proto_id, new_proto_id, new_func_id;
+	int err, len, name_len, nr_params;
+	const struct btf_param *params;
+	enum btf_func_linkage linkage;
+	char tmp_name[KSYM_NAME_LEN];
+	struct btf *btf = ctx->btf;
+	struct btf_type *t;
+
+	t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
+	if (!t || !btf_is_func(t)) {
+		pr_err("WARN: resolve_btfids: btf id %d is not a function\n", kfunc_id);
+		return -EINVAL;
+	}
+
+	kfunc_name = btf__name_by_offset(btf, t->name_off);
+	name_len = strlen(kfunc_name);
+	linkage = btf_vlen(t);
+
+	proto_id = t->type;
+	t = (struct btf_type *)btf__type_by_id(btf, proto_id);
+	if (!t || !btf_is_func_proto(t)) {
+		pr_err("ERROR: resolve_btfids: btf id %d is not a function prototype\n", proto_id);
+		return -EINVAL;
+	}
+
+	len = snprintf(tmp_name, sizeof(tmp_name), "%s%s", kfunc_name, KF_IMPL_SUFFIX);
+	if (len < 0 || len >= sizeof(tmp_name)) {
+		pr_err("ERROR: function name is too long: %s%s\n", kfunc_name, KF_IMPL_SUFFIX);
+		return -E2BIG;
+	}
+
+	if (btf__find_by_name_kind(btf, tmp_name, BTF_KIND_FUNC) > 0) {
+		pr_debug("resolve_btfids: function %s already exists in BTF\n", tmp_name);
+		goto add_new_proto;
+	}
+
+	/* Add a new function with _impl suffix and original prototype */
+	new_func_id = btf__add_func(btf, tmp_name, linkage, proto_id);
+	if (new_func_id < 0) {
+		pr_err("ERROR: resolve_btfids: failed to add func %s to BTF\n", tmp_name);
+		return new_func_id;
+	}
+
+	/* Copy all decl tags except "bpf_kfunc" from the original kfunc to the new one */
+	for (int i = 0; i < ctx->nr_decl_tags; i++) {
+		t = (struct btf_type *)btf__type_by_id(btf, ctx->decl_tags[i]);
+		if (t->type != kfunc_id)
+			continue;
+
+		tag_name = btf__name_by_offset(btf, t->name_off);
+		if (strcmp(tag_name, "bpf_kfunc") == 0)
+			continue;
+
+		err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);
+		if (err < 0) {
+			pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
+			       tag_name, tmp_name);
+			return -EINVAL;
+		}
+	}
+
+add_new_proto:
+	/*
+	 * Drop the _impl suffix and point kfunc_name to the local buffer for later use.
+	 * When BTF is modified the original pointer is invalidated.
+	 */
+	tmp_name[name_len] = '\0';
+	kfunc_name = tmp_name;
+
+	/* Load non-implicit args from the original prototype */
+	t = (struct btf_type *)btf__type_by_id(btf, proto_id);
+	params = btf_params(t);
+	nr_params = 0;
+	for (int i = 0; i < btf_vlen(t); i++) {
+		if (btf__is_kf_implicit_arg(btf, &params[i]))
+			break;
+		new_params[nr_params++] = params[i];
+	}
+
+	new_proto_id = btf__add_func_proto(btf, t->type);
+	if (new_proto_id < 0) {
+		pr_err("ERROR: resolve_btfids: failed to add func proto for %s\n", kfunc_name);
+		return new_proto_id;
+	}
+
+	/* Add non-implicit args to the new prototype */
+	for (int i = 0; i < nr_params; i++) {
+		param_name = btf__name_by_offset(btf, new_params[i].name_off);
+		err = btf__add_func_param(btf, param_name, new_params[i].type);
+		if (err < 0) {
+			pr_err("ERROR: resolve_btfids: failed to add param %s for %s\n",
+			       param_name, kfunc_name);
+			return err;
+		}
+	}
+
+	/* Finally change the prototype of the original kfunc to the new one */
+	t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
+	t->type = new_proto_id;
+
+	pr_debug("resolve_btfids: updated BTF for kfunc with implicit args %s\n", kfunc_name);
+
+	return 0;
+}
+
+static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
+{
+	const u32 type_cnt = btf__type_cnt(btf);
+	const struct btf_type *t;
+	s64 nr_decl_tags = 0;
+
+	for (u32 id = 1; id < type_cnt; id++) {
+		t = btf__type_by_id(btf, id);
+		if (!btf_is_decl_tag(t))
+			continue;
+		if (nr_decl_tags >= decl_tags_sz) {
+			pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
+				decl_tags_sz);
+			return -E2BIG;
+		}
+		decl_tags[nr_decl_tags++] = id;
+	}
+
+	return nr_decl_tags;
+}
+
+static s64 build_btf2btf_context(struct object *obj, struct btf2btf_context *ctx)
+{
+	s64 nr_decl_tags;
+
+	nr_decl_tags = btf__collect_decl_tags(obj->btf, ctx->decl_tags, ARRAY_SIZE(ctx->decl_tags));
+	if (nr_decl_tags < 0)
+		return nr_decl_tags;
+
+	ctx->btf = obj->btf;
+	ctx->nr_decl_tags = nr_decl_tags;
+
+	return 0;
+}
+
+static s64 finalize_btf(struct object *obj)
+{
+	struct btf2btf_context ctx = {};
+	s32 kfuncs[MAX_KFUNCS];
+	s64 err, nr_kfuncs;
+
+	err = build_btf2btf_context(obj, &ctx);
+	if (err < 0)
+		return err;
+
+	nr_kfuncs = collect_kfunc_ids_by_flags(obj, KF_IMPLICIT_ARGS, kfuncs, ARRAY_SIZE(kfuncs));
+	if (nr_kfuncs < 0)
+		return nr_kfuncs;
+
+	for (u32 i = 0; i < nr_kfuncs; i++) {
+		err = process_kfunc_with_implicit_args(&ctx, kfuncs[i]);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
 static const char * const resolve_btfids_usage[] = {
 	"resolve_btfids [<options>] <ELF object>",
 	"resolve_btfids --patch_btfids <.BTF_ids file> <ELF object>",
@@ -1047,6 +1326,9 @@ int main(int argc, const char **argv)
 	if (symbols_patch(&obj))
 		goto out;
 
+	if (finalize_btf(&obj))
+		goto out;
+
 	err = make_out_path(out_path, sizeof(out_path), obj.path, BTF_IDS_SECTION);
 	err = err ?: dump_raw_btf_ids(&obj, out_path);
 	if (err)
-- 
2.52.0
Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by Andrii Nakryiko 4 weeks, 1 day ago
On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Implement BTF modifications in resolve_btfids to support BPF kernel
> functions with implicit arguments.
>
> For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
> prototype is added to BTF that does not have implicit arguments. The
> kfunc's prototype is then updated to a new one in BTF. This prototype
> is the intended interface for the BPF programs.
>
> A <func_name>_impl function is added to BTF to make the original kfunc
> prototype searchable for the BPF verifier. If a <func_name>_impl
> function already exists in BTF, its interpreted as a legacy case, and
> this step is skipped.
>
> Whether an argument is implicit is determined by its type:
> currently only `struct bpf_prog_aux *` is supported.
>
> As a result, the BTF associated with kfunc is changed from
>
>     __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);
>
> into
>
>     bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
>     __bpf_kfunc bpf_foo(int arg1);
>
> For more context see previous discussions and patches [1][2].
>
> [1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
> [2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
>  1 file changed, 282 insertions(+)
>
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index df39982f51df..b361e726fa36 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -152,6 +152,18 @@ struct object {
>         int nr_typedefs;
>  };
>
> +#define KF_IMPLICIT_ARGS (1 << 16)
> +#define KF_IMPL_SUFFIX "_impl"
> +#define MAX_BPF_FUNC_REG_ARGS 5
> +#define MAX_KFUNCS 256
> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)

can't we get that from include/linux/bpf.h? seems like
resolve_btfids's main.c include internal headers just fine, so why
duplicate definitions?

> +
> +struct btf2btf_context {
> +       struct btf *btf;
> +       u32 nr_decl_tags;
> +       s32 decl_tags[MAX_DECL_TAGS];
> +};
> +
>  static int verbose;
>  static int warnings;
>
> @@ -972,6 +984,273 @@ static int patch_btfids(const char *btfids_path, const char *elf_path)
>         return err;
>  }
>
> +static s64 collect_kfunc_ids_by_flags(struct object *obj,
> +                                     u32 flags,
> +                                     s32 kfunc_ids[],
> +                                     const u32 kfunc_ids_sz)
> +{
> +       Elf_Data *data = obj->efile.idlist;
> +       struct rb_node *next;
> +       s64 nr_kfuncs = 0;
> +       int i;
> +
> +       next = rb_first(&obj->sets);
> +       while (next) {
> +               struct btf_id_set8 *set8 = NULL;
> +               unsigned long addr, off;
> +               struct btf_id *id;
> +
> +               id = rb_entry(next, struct btf_id, rb_node);
> +
> +               if (id->kind != BTF_ID_KIND_SET8)
> +                       goto skip;
> +
> +               addr = id->addr[0];
> +               off = addr - obj->efile.idlist_addr;
> +               set8 = data->d_buf + off;
> +
> +               for (i = 0; i < set8->cnt; i++) {
> +                       if (set8->pairs[i].flags & flags) {

invert condition and continue, reduce nesting?

> +                               if (nr_kfuncs >= kfunc_ids_sz) {

it's silly to set static limits like this: we are not in NMI, you have
memory allocator, use it

> +                                       pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
> +                                              flags, kfunc_ids_sz);
> +                                       return -E2BIG;
> +                               }
> +                               kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;
> +                       }
> +               }
> +skip:
> +               next = rb_next(next);
> +       }
> +
> +       return nr_kfuncs;
> +}
> +
> +static const struct btf_type *btf__unqualified_type_by_id(const struct btf *btf, s32 type_id)
> +{
> +       const struct btf_type *t = btf__type_by_id(btf, type_id);
> +
> +       while (btf_is_mod(t))
> +               t = btf__type_by_id(btf, t->type);
> +
> +       return t;
> +}
> +
> +/* Implicit BPF kfunc arguments can only be of particular types */
> +static bool btf__is_kf_implicit_arg(const struct btf *btf, const struct btf_param *p)

nit: not sure why you use double underscore convention, this is not
really an "API" of btf, it's just a helper... I'd just call it
"is_kf_implicit_arg", btf is just and input argument

> +{
> +       static const char *const kf_implicit_arg_types[] = {
> +               "bpf_prog_aux",
> +       };
> +       const struct btf_type *t;
> +       const char *name;
> +

[...]

> +       /* Copy all decl tags except "bpf_kfunc" from the original kfunc to the new one */
> +       for (int i = 0; i < ctx->nr_decl_tags; i++) {
> +               t = (struct btf_type *)btf__type_by_id(btf, ctx->decl_tags[i]);
> +               if (t->type != kfunc_id)
> +                       continue;
> +
> +               tag_name = btf__name_by_offset(btf, t->name_off);
> +               if (strcmp(tag_name, "bpf_kfunc") == 0)
> +                       continue;
> +
> +               err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);

decl_tag can apply to arguments as well (that -1 will be actually >=
0), we should copy those as well, no?

> +               if (err < 0) {
> +                       pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
> +                              tag_name, tmp_name);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +add_new_proto:
> +       /*
> +        * Drop the _impl suffix and point kfunc_name to the local buffer for later use.
> +        * When BTF is modified the original pointer is invalidated.
> +        */
> +       tmp_name[name_len] = '\0';
> +       kfunc_name = tmp_name;
> +
> +       /* Load non-implicit args from the original prototype */
> +       t = (struct btf_type *)btf__type_by_id(btf, proto_id);
> +       params = btf_params(t);
> +       nr_params = 0;
> +       for (int i = 0; i < btf_vlen(t); i++) {
> +               if (btf__is_kf_implicit_arg(btf, &params[i]))
> +                       break;
> +               new_params[nr_params++] = params[i];
> +       }

this is unnecessary, just when you working with original prototype and
its arguments, just re-fetch it back from IDs, all those pointers will
be valid until next btf__add_xxx() invocation, no need for this
awkward local copies

> +
> +       new_proto_id = btf__add_func_proto(btf, t->type);
> +       if (new_proto_id < 0) {
> +               pr_err("ERROR: resolve_btfids: failed to add func proto for %s\n", kfunc_name);
> +               return new_proto_id;
> +       }
> +
> +       /* Add non-implicit args to the new prototype */
> +       for (int i = 0; i < nr_params; i++) {
> +               param_name = btf__name_by_offset(btf, new_params[i].name_off);
> +               err = btf__add_func_param(btf, param_name, new_params[i].type);
> +               if (err < 0) {
> +                       pr_err("ERROR: resolve_btfids: failed to add param %s for %s\n",
> +                              param_name, kfunc_name);
> +                       return err;
> +               }
> +       }
> +
> +       /* Finally change the prototype of the original kfunc to the new one */
> +       t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
> +       t->type = new_proto_id;
> +
> +       pr_debug("resolve_btfids: updated BTF for kfunc with implicit args %s\n", kfunc_name);
> +
> +       return 0;
> +}
> +
> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
> +{
> +       const u32 type_cnt = btf__type_cnt(btf);
> +       const struct btf_type *t;
> +       s64 nr_decl_tags = 0;
> +
> +       for (u32 id = 1; id < type_cnt; id++) {
> +               t = btf__type_by_id(btf, id);
> +               if (!btf_is_decl_tag(t))
> +                       continue;
> +               if (nr_decl_tags >= decl_tags_sz) {
> +                       pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",

realloc is your friend, no fixed-sized arrays

> +                               decl_tags_sz);
> +                       return -E2BIG;
> +               }
> +               decl_tags[nr_decl_tags++] = id;
> +       }
> +
> +       return nr_decl_tags;
> +}
> +

[...]
Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 4 weeks, 1 day ago
On 1/9/26 3:25 PM, Andrii Nakryiko wrote:
> On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> Implement BTF modifications in resolve_btfids to support BPF kernel
>> functions with implicit arguments.
>>
>> For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
>> prototype is added to BTF that does not have implicit arguments. The
>> kfunc's prototype is then updated to a new one in BTF. This prototype
>> is the intended interface for the BPF programs.
>>
>> A <func_name>_impl function is added to BTF to make the original kfunc
>> prototype searchable for the BPF verifier. If a <func_name>_impl
>> function already exists in BTF, its interpreted as a legacy case, and
>> this step is skipped.
>>
>> Whether an argument is implicit is determined by its type:
>> currently only `struct bpf_prog_aux *` is supported.
>>
>> As a result, the BTF associated with kfunc is changed from
>>
>>     __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);
>>
>> into
>>
>>     bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
>>     __bpf_kfunc bpf_foo(int arg1);
>>
>> For more context see previous discussions and patches [1][2].
>>
>> [1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
>> [2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/
>>
>> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
>> ---
>>  tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
>>  1 file changed, 282 insertions(+)
>>
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index df39982f51df..b361e726fa36 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
>> @@ -152,6 +152,18 @@ struct object {
>>         int nr_typedefs;
>>  };
>>
>> +#define KF_IMPLICIT_ARGS (1 << 16)
>> +#define KF_IMPL_SUFFIX "_impl"
>> +#define MAX_BPF_FUNC_REG_ARGS 5
>> +#define MAX_KFUNCS 256
>> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
> 
> can't we get that from include/linux/bpf.h? seems like
> resolve_btfids's main.c include internal headers just fine, so why
> duplicate definitions?

Hi Andrii, thank you for a quick review.

Including internal include/linux/btf.h directly doesn't work, which is
probably expected.

resolve_btfids is currently built with:

HOSTCFLAGS_resolve_btfids += -g \
          -I$(srctree)/tools/include \
          -I$(srctree)/tools/include/uapi \
          -I$(LIBBPF_INCLUDE) \
          -I$(SUBCMD_INCLUDE) \
          $(LIBELF_FLAGS) \
          -Wall -Werror

If I add -I$(srctree)/include option and then

    #include <linux/btf.h>

A bunch of build errors happen.

AFAIU we'd have to create a stripped copy of relevant headers in
tools/include first.  Is that what you're suggesting?

> 
>> +
>> +struct btf2btf_context {
>> +       struct btf *btf;
>> +       u32 nr_decl_tags;
>> +       s32 decl_tags[MAX_DECL_TAGS];
>> +};
>> +
>>  static int verbose;
>>  static int warnings;
>>
>> @@ -972,6 +984,273 @@ static int patch_btfids(const char *btfids_path, const char *elf_path)
>>         return err;
>>  }
>>
>> +static s64 collect_kfunc_ids_by_flags(struct object *obj,
>> +                                     u32 flags,
>> +                                     s32 kfunc_ids[],
>> +                                     const u32 kfunc_ids_sz)
>> +{
>> +       Elf_Data *data = obj->efile.idlist;
>> +       struct rb_node *next;
>> +       s64 nr_kfuncs = 0;
>> +       int i;
>> +
>> +       next = rb_first(&obj->sets);
>> +       while (next) {
>> +               struct btf_id_set8 *set8 = NULL;
>> +               unsigned long addr, off;
>> +               struct btf_id *id;
>> +
>> +               id = rb_entry(next, struct btf_id, rb_node);
>> +
>> +               if (id->kind != BTF_ID_KIND_SET8)
>> +                       goto skip;
>> +
>> +               addr = id->addr[0];
>> +               off = addr - obj->efile.idlist_addr;
>> +               set8 = data->d_buf + off;
>> +
>> +               for (i = 0; i < set8->cnt; i++) {
>> +                       if (set8->pairs[i].flags & flags) {
> 
> invert condition and continue, reduce nesting?
> 
>> +                               if (nr_kfuncs >= kfunc_ids_sz) {
> 
> it's silly to set static limits like this: we are not in NMI, you have
> memory allocator, use it

I kinda like that btf2btf_context is stack allocated, but I see your
point. It's not necessary to set hard limits in resolve_btfids.

> 
>> +                                       pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
>> +                                              flags, kfunc_ids_sz);
>> +                                       return -E2BIG;
>> +                               }
>> +                               kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;
>> +                       }
>> +               }
>> +skip:
>> +               next = rb_next(next);
>> +       }
>> +
>> +       return nr_kfuncs;
>> +}
>> +
>> +static const struct btf_type *btf__unqualified_type_by_id(const struct btf *btf, s32 type_id)
>> +{
>> +       const struct btf_type *t = btf__type_by_id(btf, type_id);
>> +
>> +       while (btf_is_mod(t))
>> +               t = btf__type_by_id(btf, t->type);
>> +
>> +       return t;
>> +}
>> +
>> +/* Implicit BPF kfunc arguments can only be of particular types */
>> +static bool btf__is_kf_implicit_arg(const struct btf *btf, const struct btf_param *p)
> 
> nit: not sure why you use double underscore convention, this is not
> really an "API" of btf, it's just a helper... I'd just call it
> "is_kf_implicit_arg", btf is just and input argument
> 
>> +{
>> +       static const char *const kf_implicit_arg_types[] = {
>> +               "bpf_prog_aux",
>> +       };
>> +       const struct btf_type *t;
>> +       const char *name;
>> +
> 
> [...]
> 
>> +       /* Copy all decl tags except "bpf_kfunc" from the original kfunc to the new one */
>> +       for (int i = 0; i < ctx->nr_decl_tags; i++) {
>> +               t = (struct btf_type *)btf__type_by_id(btf, ctx->decl_tags[i]);
>> +               if (t->type != kfunc_id)
>> +                       continue;
>> +
>> +               tag_name = btf__name_by_offset(btf, t->name_off);
>> +               if (strcmp(tag_name, "bpf_kfunc") == 0)
>> +                       continue;
>> +
>> +               err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);
> 
> decl_tag can apply to arguments as well (that -1 will be actually >=
> 0), we should copy those as well, no?

I think you're right. Technically decl_tags can point to parameters as
well.  Is this actually used in kernel BTF?

For the type tags we don't have to do anything though, because the
param type should point to the top type tag, right?

> 
>> +               if (err < 0) {
>> +                       pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
>> +                              tag_name, tmp_name);
>> +                       return -EINVAL;
>> +               }
>> +       }
>> +
>> +add_new_proto:
>> +       /*
>> +        * Drop the _impl suffix and point kfunc_name to the local buffer for later use.
>> +        * When BTF is modified the original pointer is invalidated.
>> +        */
>> +       tmp_name[name_len] = '\0';
>> +       kfunc_name = tmp_name;
>> +
>> +       /* Load non-implicit args from the original prototype */
>> +       t = (struct btf_type *)btf__type_by_id(btf, proto_id);
>> +       params = btf_params(t);
>> +       nr_params = 0;
>> +       for (int i = 0; i < btf_vlen(t); i++) {
>> +               if (btf__is_kf_implicit_arg(btf, &params[i]))
>> +                       break;
>> +               new_params[nr_params++] = params[i];
>> +       }
> 
> this is unnecessary, just when you working with original prototype and
> its arguments, just re-fetch it back from IDs, all those pointers will
> be valid until next btf__add_xxx() invocation, no need for this
> awkward local copies
> 
>> +
>> +       new_proto_id = btf__add_func_proto(btf, t->type);
>> +       if (new_proto_id < 0) {
>> +               pr_err("ERROR: resolve_btfids: failed to add func proto for %s\n", kfunc_name);
>> +               return new_proto_id;
>> +       }
>> +
>> +       /* Add non-implicit args to the new prototype */
>> +       for (int i = 0; i < nr_params; i++) {
>> +               param_name = btf__name_by_offset(btf, new_params[i].name_off);
>> +               err = btf__add_func_param(btf, param_name, new_params[i].type);
>> +               if (err < 0) {
>> +                       pr_err("ERROR: resolve_btfids: failed to add param %s for %s\n",
>> +                              param_name, kfunc_name);
>> +                       return err;
>> +               }
>> +       }
>> +
>> +       /* Finally change the prototype of the original kfunc to the new one */
>> +       t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
>> +       t->type = new_proto_id;
>> +
>> +       pr_debug("resolve_btfids: updated BTF for kfunc with implicit args %s\n", kfunc_name);
>> +
>> +       return 0;
>> +}
>> +
>> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
>> +{
>> +       const u32 type_cnt = btf__type_cnt(btf);
>> +       const struct btf_type *t;
>> +       s64 nr_decl_tags = 0;
>> +
>> +       for (u32 id = 1; id < type_cnt; id++) {
>> +               t = btf__type_by_id(btf, id);
>> +               if (!btf_is_decl_tag(t))
>> +                       continue;
>> +               if (nr_decl_tags >= decl_tags_sz) {
>> +                       pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
> 
> realloc is your friend, no fixed-sized arrays
> 
>> +                               decl_tags_sz);
>> +                       return -E2BIG;
>> +               }
>> +               decl_tags[nr_decl_tags++] = id;
>> +       }
>> +
>> +       return nr_decl_tags;
>> +}
>> +
> 
> [...]

Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by Andrii Nakryiko 3 weeks, 5 days ago
On Fri, Jan 9, 2026 at 5:15 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/9/26 3:25 PM, Andrii Nakryiko wrote:
> > On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>
> >> Implement BTF modifications in resolve_btfids to support BPF kernel
> >> functions with implicit arguments.
> >>
> >> For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
> >> prototype is added to BTF that does not have implicit arguments. The
> >> kfunc's prototype is then updated to a new one in BTF. This prototype
> >> is the intended interface for the BPF programs.
> >>
> >> A <func_name>_impl function is added to BTF to make the original kfunc
> >> prototype searchable for the BPF verifier. If a <func_name>_impl
> >> function already exists in BTF, its interpreted as a legacy case, and
> >> this step is skipped.
> >>
> >> Whether an argument is implicit is determined by its type:
> >> currently only `struct bpf_prog_aux *` is supported.
> >>
> >> As a result, the BTF associated with kfunc is changed from
> >>
> >>     __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);
> >>
> >> into
> >>
> >>     bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
> >>     __bpf_kfunc bpf_foo(int arg1);
> >>
> >> For more context see previous discussions and patches [1][2].
> >>
> >> [1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
> >> [2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/
> >>
> >> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> >> ---
> >>  tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
> >>  1 file changed, 282 insertions(+)
> >>
> >> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> >> index df39982f51df..b361e726fa36 100644
> >> --- a/tools/bpf/resolve_btfids/main.c
> >> +++ b/tools/bpf/resolve_btfids/main.c
> >> @@ -152,6 +152,18 @@ struct object {
> >>         int nr_typedefs;
> >>  };
> >>
> >> +#define KF_IMPLICIT_ARGS (1 << 16)
> >> +#define KF_IMPL_SUFFIX "_impl"
> >> +#define MAX_BPF_FUNC_REG_ARGS 5
> >> +#define MAX_KFUNCS 256
> >> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
> >
> > can't we get that from include/linux/bpf.h? seems like
> > resolve_btfids's main.c include internal headers just fine, so why
> > duplicate definitions?
>
> Hi Andrii, thank you for a quick review.
>
> Including internal include/linux/btf.h directly doesn't work, which is
> probably expected.
>
> resolve_btfids is currently built with:
>
> HOSTCFLAGS_resolve_btfids += -g \
>           -I$(srctree)/tools/include \
>           -I$(srctree)/tools/include/uapi \

so I don't know if that will solve the issue, but I don't think it
makes sense to build resolve_btfids using tools' version of includes.
tools/include is mostly for perf's benefit (maybe so that they don't
accidentally take some kernel-internal dependency, not sure). But
resolve_btfids is built for the kernel during the kernel build, we
should have access to full kernel headers. Try changing this and see
if build errors go away?

>           -I$(LIBBPF_INCLUDE) \
>           -I$(SUBCMD_INCLUDE) \
>           $(LIBELF_FLAGS) \
>           -Wall -Werror
>
> If I add -I$(srctree)/include option and then
>
>     #include <linux/btf.h>
>
> A bunch of build errors happen.
>
> AFAIU we'd have to create a stripped copy of relevant headers in
> tools/include first.  Is that what you're suggesting?

see above, the opposite -- just use -I$(srctree)/include directly

[...]

> >> +               addr = id->addr[0];
> >> +               off = addr - obj->efile.idlist_addr;
> >> +               set8 = data->d_buf + off;
> >> +
> >> +               for (i = 0; i < set8->cnt; i++) {
> >> +                       if (set8->pairs[i].flags & flags) {
> >
> > invert condition and continue, reduce nesting?
> >
> >> +                               if (nr_kfuncs >= kfunc_ids_sz) {
> >
> > it's silly to set static limits like this: we are not in NMI, you have
> > memory allocator, use it
>
> I kinda like that btf2btf_context is stack allocated, but I see your
> point. It's not necessary to set hard limits in resolve_btfids.
>

I don't think we'll notice the performance difference. We had similar
statically-sized things for BTF processing in pahole, and very quickly
ran into limitations and had to change them to dynamically allocated.
Let's not do this again. realloc() is a bit more annoying to use, but
no big deal, it's C after all.

> >
> >> +                                       pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
> >> +                                              flags, kfunc_ids_sz);
> >> +                                       return -E2BIG;
> >> +                               }
> >> +                               kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;

[...]

> >> +               err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);
> >
> > decl_tag can apply to arguments as well (that -1 will be actually >=
> > 0), we should copy those as well, no?
>
> I think you're right. Technically decl_tags can point to parameters as
> well.  Is this actually used in kernel BTF?

I don't remember, but it doesn't matter. We have to clone them as well.

>
> For the type tags we don't have to do anything though, because the
> param type should point to the top type tag, right?

Yeah, type tags are part of type chains, if we reuse types, we'll be
reusing type_tags.

>
> >
> >> +               if (err < 0) {
> >> +                       pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
> >> +                              tag_name, tmp_name);
> >> +                       return -EINVAL;
> >> +               }
> >> +       }
> >> +

[...]
Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 3 weeks, 5 days ago
On 1/12/26 8:51 AM, Andrii Nakryiko wrote:
> On Fri, Jan 9, 2026 at 5:15 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> [...]
>>>>
>>>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>>>> index df39982f51df..b361e726fa36 100644
>>>> --- a/tools/bpf/resolve_btfids/main.c
>>>> +++ b/tools/bpf/resolve_btfids/main.c
>>>> @@ -152,6 +152,18 @@ struct object {
>>>>         int nr_typedefs;
>>>>  };
>>>>
>>>> +#define KF_IMPLICIT_ARGS (1 << 16)
>>>> +#define KF_IMPL_SUFFIX "_impl"
>>>> +#define MAX_BPF_FUNC_REG_ARGS 5
>>>> +#define MAX_KFUNCS 256
>>>> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
>>>
>>> can't we get that from include/linux/bpf.h? seems like
>>> resolve_btfids's main.c include internal headers just fine, so why
>>> duplicate definitions?
>>
>> Hi Andrii, thank you for a quick review.
>>
>> Including internal include/linux/btf.h directly doesn't work, which is
>> probably expected.
>>
>> resolve_btfids is currently built with:
>>
>> HOSTCFLAGS_resolve_btfids += -g \
>>           -I$(srctree)/tools/include \
>>           -I$(srctree)/tools/include/uapi \
> 
> so I don't know if that will solve the issue, but I don't think it
> makes sense to build resolve_btfids using tools' version of includes.
> tools/include is mostly for perf's benefit (maybe so that they don't
> accidentally take some kernel-internal dependency, not sure). But
> resolve_btfids is built for the kernel during the kernel build, we
> should have access to full kernel headers. Try changing this and see
> if build errors go away?
> 
>>           -I$(LIBBPF_INCLUDE) \
>>           -I$(SUBCMD_INCLUDE) \
>>           $(LIBELF_FLAGS) \
>>           -Wall -Werror
>>
>> If I add -I$(srctree)/include option and then
>>
>>     #include <linux/btf.h>
>>
>> A bunch of build errors happen.
>>
>> AFAIU we'd have to create a stripped copy of relevant headers in
>> tools/include first.  Is that what you're suggesting?
> 
> see above, the opposite -- just use -I$(srctree)/include directly

Andrii,

I made a low-effort attempt to switch away from tools/include and it
looks like too much trouble. See a sample splat below.

I think the issue is that resolve_btfids uses a couple of inherently
user-space things (stdlib, libelf), which themselves may include
system headers. And there is actually a difference between the kernel
and tools/include headers. For example, check

  ./include/linux/rbtree.h
vs
  ./tools/include/linux/rbtree.h

Maybe we can make it work (with our own local tools/include?), but it
doesn't look worth it for just a couple of constant #define-s.

Let me know if I am missing something.


$ make
  INSTALL libsubcmd_headers
  HOSTCC  /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:20:33: error: conflicting types for ‘fd_set’; have ‘__kernel_fd_set’
   20 | typedef __kernel_fd_set         fd_set;
      |                                 ^~~~~~
In file included from /usr/include/sys/types.h:179,
                 from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/select.h:70:5: note: previous declaration of ‘fd_set’ with type ‘fd_set’
   70 |   } fd_set;
      |     ^~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:21:33: error: conflicting types for ‘dev_t’; have ‘__kernel_dev_t’ {aka ‘unsigned int’}
   21 | typedef __kernel_dev_t          dev_t;
      |                                 ^~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:59:17: note: previous declaration of ‘dev_t’ with type ‘dev_t’ {aka ‘long unsigned int’}
   59 | typedef __dev_t dev_t;
      |                 ^~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:25:33: error: conflicting types for ‘nlink_t’; have ‘u32’ {aka ‘unsigned int’}
   25 | typedef u32                     nlink_t;
      |                                 ^~~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:74:19: note: previous declaration of ‘nlink_t’ with type ‘nlink_t’ {aka ‘long unsigned int’}
   74 | typedef __nlink_t nlink_t;
      |                   ^~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:31:33: error: conflicting types for ‘timer_t’; have ‘__kernel_timer_t’ {aka ‘int’}
   31 | typedef __kernel_timer_t        timer_t;
      |                                 ^~~~~~~
In file included from /usr/include/sys/types.h:130,
                 from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/bits/types/timer_t.h:7:19: note: previous declaration of ‘timer_t’ with type ‘timer_t’ {aka ‘void *’}
    7 | typedef __timer_t timer_t;
      |                   ^~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:52:33: error: conflicting types for ‘loff_t’; have ‘__kernel_loff_t’ {aka ‘long long int’}
   52 | typedef __kernel_loff_t         loff_t;
      |                                 ^~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:42:18: note: previous declaration of ‘loff_t’ with type ‘loff_t’ {aka ‘long int’}
   42 | typedef __loff_t loff_t;
      |                  ^~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:53:9: error: unknown type name ‘__kernel_uoff_t’
   53 | typedef __kernel_uoff_t         uoff_t;
      |         ^~~~~~~~~~~~~~~
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:115:33: error: conflicting types for ‘uint64_t’; have ‘u64’ {aka ‘long long unsigned int’}
  115 | typedef u64                     uint64_t;
      |                                 ^~~~~~~~
In file included from /usr/include/stdint.h:37,
                 from /usr/lib/gcc/x86_64-redhat-linux/11/include/stdint.h:9,
                 from /usr/include/libelf.h:32,
                 from main.c:68:
/usr/include/bits/stdint-uintn.h:27:20: note: previous declaration of ‘uint64_t’ with type ‘uint64_t’ {aka ‘long unsigned int’}
   27 | typedef __uint64_t uint64_t;
      |                    ^~~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:116:33: error: conflicting types for ‘u_int64_t’; have ‘u64’ {aka ‘long long unsigned int’}
  116 | typedef u64                     u_int64_t;
      |                                 ^~~~~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:161:20: note: previous declaration of ‘u_int64_t’ with type ‘u_int64_t’ {aka ‘long unsigned int’}
  161 | typedef __uint64_t u_int64_t;
      |                    ^~~~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:117:33: error: conflicting types for ‘int64_t’; have ‘s64’ {aka ‘long long int’}
  117 | typedef s64                     int64_t;
      |                                 ^~~~~~~
In file included from /usr/include/sys/types.h:155,
                 from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/bits/stdint-intn.h:27:19: note: previous declaration of ‘int64_t’ with type ‘int64_t’ {aka ‘long int’}
   27 | typedef __int64_t int64_t;
      |                   ^~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:138:13: error: conflicting types for ‘blkcnt_t’; have ‘u64’ {aka ‘long long unsigned int’}
  138 | typedef u64 blkcnt_t;
      |             ^~~~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:192:20: note: previous declaration of ‘blkcnt_t’ with type ‘blkcnt_t’ {aka ‘long int’}
  192 | typedef __blkcnt_t blkcnt_t;     /* Type to count number of disk blocks.  */
      |                    ^~~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:266:34: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘*’ token
  266 |         struct task_struct __rcu *task;
      |                                  ^
In file included from /home/isolodrai/workspace/prog-aux/linux/include/linux/cache.h:6,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/time.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:19,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/vdso/cache.h:5:10: fatal error: asm/cache.h: No such file or directory
    5 | #include <asm/cache.h>
      |          ^~~~~~~~~~~~~
compilation terminated.
make[1]: *** [/home/isolodrai/workspace/prog-aux/linux/tools/build/Makefile.build:86: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o] Error 1
make: *** [Makefile:81: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids//resolve_btfids-in.o] Error 2


> 
> [...]
> 
Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by Andrii Nakryiko 3 weeks, 4 days ago
On Mon, Jan 12, 2026 at 5:49 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/12/26 8:51 AM, Andrii Nakryiko wrote:
> > On Fri, Jan 9, 2026 at 5:15 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>
> >> [...]
> >>>>
> >>>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> >>>> index df39982f51df..b361e726fa36 100644
> >>>> --- a/tools/bpf/resolve_btfids/main.c
> >>>> +++ b/tools/bpf/resolve_btfids/main.c
> >>>> @@ -152,6 +152,18 @@ struct object {
> >>>>         int nr_typedefs;
> >>>>  };
> >>>>
> >>>> +#define KF_IMPLICIT_ARGS (1 << 16)
> >>>> +#define KF_IMPL_SUFFIX "_impl"
> >>>> +#define MAX_BPF_FUNC_REG_ARGS 5
> >>>> +#define MAX_KFUNCS 256
> >>>> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
> >>>
> >>> can't we get that from include/linux/bpf.h? seems like
> >>> resolve_btfids's main.c include internal headers just fine, so why
> >>> duplicate definitions?
> >>
> >> Hi Andrii, thank you for a quick review.
> >>
> >> Including internal include/linux/btf.h directly doesn't work, which is
> >> probably expected.
> >>
> >> resolve_btfids is currently built with:
> >>
> >> HOSTCFLAGS_resolve_btfids += -g \
> >>           -I$(srctree)/tools/include \
> >>           -I$(srctree)/tools/include/uapi \
> >
> > so I don't know if that will solve the issue, but I don't think it
> > makes sense to build resolve_btfids using tools' version of includes.
> > tools/include is mostly for perf's benefit (maybe so that they don't
> > accidentally take some kernel-internal dependency, not sure). But
> > resolve_btfids is built for the kernel during the kernel build, we
> > should have access to full kernel headers. Try changing this and see
> > if build errors go away?
> >
> >>           -I$(LIBBPF_INCLUDE) \
> >>           -I$(SUBCMD_INCLUDE) \
> >>           $(LIBELF_FLAGS) \
> >>           -Wall -Werror
> >>
> >> If I add -I$(srctree)/include option and then
> >>
> >>     #include <linux/btf.h>
> >>
> >> A bunch of build errors happen.
> >>
> >> AFAIU we'd have to create a stripped copy of relevant headers in
> >> tools/include first.  Is that what you're suggesting?
> >
> > see above, the opposite -- just use -I$(srctree)/include directly
>
> Andrii,
>
> I made a low-effort attempt to switch away from tools/include and it
> looks like too much trouble. See a sample splat below.
>
> I think the issue is that resolve_btfids uses a couple of inherently
> user-space things (stdlib, libelf), which themselves may include
> system headers. And there is actually a difference between the kernel
> and tools/include headers. For example, check
>
>   ./include/linux/rbtree.h
> vs
>   ./tools/include/linux/rbtree.h
>
> Maybe we can make it work (with our own local tools/include?), but it
> doesn't look worth it for just a couple of constant #define-s.
>
> Let me know if I am missing something.

No, it's fine, no big deal, at least we know that it's not as simple.
Thanks for trying!

>
>
> $ make
>   INSTALL libsubcmd_headers
>   HOSTCC  /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:20:33: error: conflicting types for ‘fd_set’; have ‘__kernel_fd_set’
>    20 | typedef __kernel_fd_set         fd_set;
>       |                                 ^~~~~~
> In file included from /usr/include/sys/types.h:179,
>                  from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/select.h:70:5: note: previous declaration of ‘fd_set’ with type ‘fd_set’
>    70 |   } fd_set;
>       |     ^~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:21:33: error: conflicting types for ‘dev_t’; have ‘__kernel_dev_t’ {aka ‘unsigned int’}
>    21 | typedef __kernel_dev_t          dev_t;
>       |                                 ^~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:59:17: note: previous declaration of ‘dev_t’ with type ‘dev_t’ {aka ‘long unsigned int’}
>    59 | typedef __dev_t dev_t;
>       |                 ^~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:25:33: error: conflicting types for ‘nlink_t’; have ‘u32’ {aka ‘unsigned int’}
>    25 | typedef u32                     nlink_t;
>       |                                 ^~~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:74:19: note: previous declaration of ‘nlink_t’ with type ‘nlink_t’ {aka ‘long unsigned int’}
>    74 | typedef __nlink_t nlink_t;
>       |                   ^~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:31:33: error: conflicting types for ‘timer_t’; have ‘__kernel_timer_t’ {aka ‘int’}
>    31 | typedef __kernel_timer_t        timer_t;
>       |                                 ^~~~~~~
> In file included from /usr/include/sys/types.h:130,
>                  from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/bits/types/timer_t.h:7:19: note: previous declaration of ‘timer_t’ with type ‘timer_t’ {aka ‘void *’}
>     7 | typedef __timer_t timer_t;
>       |                   ^~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:52:33: error: conflicting types for ‘loff_t’; have ‘__kernel_loff_t’ {aka ‘long long int’}
>    52 | typedef __kernel_loff_t         loff_t;
>       |                                 ^~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:42:18: note: previous declaration of ‘loff_t’ with type ‘loff_t’ {aka ‘long int’}
>    42 | typedef __loff_t loff_t;
>       |                  ^~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:53:9: error: unknown type name ‘__kernel_uoff_t’
>    53 | typedef __kernel_uoff_t         uoff_t;
>       |         ^~~~~~~~~~~~~~~
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:115:33: error: conflicting types for ‘uint64_t’; have ‘u64’ {aka ‘long long unsigned int’}
>   115 | typedef u64                     uint64_t;
>       |                                 ^~~~~~~~
> In file included from /usr/include/stdint.h:37,
>                  from /usr/lib/gcc/x86_64-redhat-linux/11/include/stdint.h:9,
>                  from /usr/include/libelf.h:32,
>                  from main.c:68:
> /usr/include/bits/stdint-uintn.h:27:20: note: previous declaration of ‘uint64_t’ with type ‘uint64_t’ {aka ‘long unsigned int’}
>    27 | typedef __uint64_t uint64_t;
>       |                    ^~~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:116:33: error: conflicting types for ‘u_int64_t’; have ‘u64’ {aka ‘long long unsigned int’}
>   116 | typedef u64                     u_int64_t;
>       |                                 ^~~~~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:161:20: note: previous declaration of ‘u_int64_t’ with type ‘u_int64_t’ {aka ‘long unsigned int’}
>   161 | typedef __uint64_t u_int64_t;
>       |                    ^~~~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:117:33: error: conflicting types for ‘int64_t’; have ‘s64’ {aka ‘long long int’}
>   117 | typedef s64                     int64_t;
>       |                                 ^~~~~~~
> In file included from /usr/include/sys/types.h:155,
>                  from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/bits/stdint-intn.h:27:19: note: previous declaration of ‘int64_t’ with type ‘int64_t’ {aka ‘long int’}
>    27 | typedef __int64_t int64_t;
>       |                   ^~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:138:13: error: conflicting types for ‘blkcnt_t’; have ‘u64’ {aka ‘long long unsigned int’}
>   138 | typedef u64 blkcnt_t;
>       |             ^~~~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:192:20: note: previous declaration of ‘blkcnt_t’ with type ‘blkcnt_t’ {aka ‘long int’}
>   192 | typedef __blkcnt_t blkcnt_t;     /* Type to count number of disk blocks.  */
>       |                    ^~~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:266:34: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘*’ token
>   266 |         struct task_struct __rcu *task;
>       |                                  ^
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/linux/cache.h:6,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/time.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:19,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/vdso/cache.h:5:10: fatal error: asm/cache.h: No such file or directory
>     5 | #include <asm/cache.h>
>       |          ^~~~~~~~~~~~~
> compilation terminated.
> make[1]: *** [/home/isolodrai/workspace/prog-aux/linux/tools/build/Makefile.build:86: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o] Error 1
> make: *** [Makefile:81: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids//resolve_btfids-in.o] Error 2
>
>
> >
> > [...]
> >
Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by bot+bpf-ci@kernel.org 4 weeks, 1 day ago
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index df39982f51df..b361e726fa36 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c

[ ... ]

> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
> +{
> +	const u32 type_cnt = btf__type_cnt(btf);
> +	const struct btf_type *t;
> +	s64 nr_decl_tags = 0;
> +
> +	for (u32 id = 1; id < type_cnt; id++) {
> +		t = btf__type_by_id(btf, id);
> +		if (!btf_is_decl_tag(t))
> +			continue;
> +		if (nr_decl_tags >= decl_tags_sz) {
> +			pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
> +				decl_tags_sz);
                                ^^^^^^^^^^^^^^^^^

Should this be %u instead of %s? The decl_tags_sz parameter is a u32, but
the format specifier %s expects a string pointer. When the error path is
triggered (more than MAX_DECL_TAGS decl tags in BTF), this will cause
undefined behavior as printf tries to interpret the integer value as a
memory address to read a string from.

> +			return -E2BIG;
> +		}
> +		decl_tags[nr_decl_tags++] = id;
> +	}
> +
> +	return nr_decl_tags;
> +}

[ ... ]


---
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/20862592244
Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 4 weeks, 1 day ago
On 1/9/26 11:15 AM, bot+bpf-ci@kernel.org wrote:
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index df39982f51df..b361e726fa36 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
> 
> [ ... ]
> 
>> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
>> +{
>> +	const u32 type_cnt = btf__type_cnt(btf);
>> +	const struct btf_type *t;
>> +	s64 nr_decl_tags = 0;
>> +
>> +	for (u32 id = 1; id < type_cnt; id++) {
>> +		t = btf__type_by_id(btf, id);
>> +		if (!btf_is_decl_tag(t))
>> +			continue;
>> +		if (nr_decl_tags >= decl_tags_sz) {
>> +			pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
>> +				decl_tags_sz);
>                                 ^^^^^^^^^^^^^^^^^
> 
> Should this be %u instead of %s? The decl_tags_sz parameter is a u32, but
> the format specifier %s expects a string pointer. When the error path is
> triggered (more than MAX_DECL_TAGS decl tags in BTF), this will cause
> undefined behavior as printf tries to interpret the integer value as a
> memory address to read a string from.

Correct, it should be %u or %d.

> 
>> +			return -E2BIG;
>> +		}
>> +		decl_tags[nr_decl_tags++] = id;
>> +	}
>> +
>> +	return nr_decl_tags;
>> +}
> 
> [ ... ]
> 
> 
> ---
> 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/20862592244