All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs
@ 2026-06-01 22:17 Ihor Solodrai
  2026-06-01 22:17 ` [PATCH bpf-next v1 01/14] tools/bpf: Sync btf_ids.h to tools Ihor Solodrai
                   ` (14 more replies)
  0 siblings, 15 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

BTF data for the kernel is generated through the following pipeline:
  * DWARF is emitted by the compiler
  * pahole reads in DWARF and produces BTF
  * resolve_btfids makes kernel-specific btf2btf transformation and
    patches .BTF_ids section

This is orchestrated by link-vmlinux.sh, gen-btf.sh and Makefile.btf
in ./scripts directory.

Historically kernel-specific BTF features were implemented in pahole,
and controlled by the feature flags. This requires kernel build
process to be aware of pahole version used for the build to set
correct runtime arguments for BTF encoding [1].

This is a burden which can be alleviated by splitting kernel/module
BTF generation in two stages:
  1. Generic BTF generation from the kernel source code.
  2. Kernel-specific BTF modifications to support various BPF features.

So far both stages were fused in pahole's BTF encoding. By moving
stage (2) in-tree, the dependency of kernel build on pahole can become
much more loose.

Recent work [2] made it possible to do btf2btf transformations in
resolve_btfids, and it is already responsible for a few important BTF
modifications for the kernel:
  * .BTF.base generation for modules [3]
  * BTF sorting [4]
  * KF_IMPLICIT_ARGS support [5]

This series continues the migration of kernel-specific BTF
transformations from pahole to resolve_btfids, implementing emission
of decl/type tags for the kfuncs and handling of the KF_ARENA_* flags.

The implementation is idempotent with respect to BTF modifications: if
input BTF already contains target tags, corresponding resolve_btfids
transformation is a noop. This allows for more flexibility in terms of
dependency on pahole. In particular, older pahole (without
decl_tag_kfuncs support, for example) can now be used in kbuild and
the resulting vmlinux BTF will contain properly tagged kfuncs
anyways. Conversely, if for whatever reason pahole emitted the same
tags, they will be properly skipped.

The series consists of the following patches:

  * patches #1-#4 refactor and fixup resolve_btfids selftest, adding
    kfunc checks
    * patch #3 is a fascinating rabbit hole with a two-line test fix
  * patch #5 refactors resolve_btfids introducing an address to
    elf-symbol index for efficient lookups
  * patch #6 changes how resolve_btfids discovers kfuncs: instead of
    relying on decl_tags emitted by pahole, identify kfuncs directly
    from BTF ID sets
  * patch #7 implements "bpf_kfunc" decl_tag emission, with the next
    patch testing it
  * patch #9 adds "bpf_fastcall" handling, with the next patch testing it
  * patch #11 implements KF_ARENA_* flags handling in resolve_btfids,
    with the next patch testing that
  * finally, patch #13 drops now unnecessary pahole runtime flags from
    the kbuild, and patch #14 adds a bit of documentation


[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/scripts/Makefile.btf?h=v7.1-rc5
[2] https://lore.kernel.org/bpf/20251219181321.1283664-1-ihor.solodrai@linux.dev/
[3] https://docs.kernel.org/bpf/btf.html#btf-base-section
[4] https://lore.kernel.org/bpf/20260109130003.3313716-4-dolinux.peng@gmail.com/
[5] https://lore.kernel.org/bpf/20260120222638.3976562-1-ihor.solodrai@linux.dev/

---

Ihor Solodrai (14):
  tools/bpf: Sync btf_ids.h to tools
  selftests/bpf: Modernize resolve_btfids test scaffolding
  selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE
    builds
  selftests/bpf: Add kfunc set test to resolve_btfids
  resolve_btfids: Index BTF ID symbols by address
  resolve_btfids: Discover kfuncs from BTF ID sets
  resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs
  selftests/bpf: Verify bpf_kfunc decl tag emission in resolve_btfids
  resolve_btfids: Emit a decl tag for kfuncs with KF_FASTCALL
  selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test
  resolve_btfids: Process KF_ARENA_* flags in resolve_btfids
  selftests/bpf: Verify arena type tags in resolve_btfids test
  kbuild: Drop decl_tag_kfuncs and attributes from pahole flags
  docs, resolve_btfids: Document kfunc BTF annotation emission

 Documentation/bpf/kfuncs.rst                  |   8 +
 scripts/Makefile.btf                          |   7 +-
 tools/bpf/resolve_btfids/main.c               | 391 ++++++++++++++----
 tools/include/linux/btf_ids.h                 |  78 +++-
 .../selftests/bpf/prog_tests/resolve_btfids.c | 218 ++++++++--
 tools/testing/selftests/bpf/progs/btf_data.c  |  20 +
 6 files changed, 602 insertions(+), 120 deletions(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 01/14] tools/bpf: Sync btf_ids.h to tools
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-01 22:17 ` [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding Ihor Solodrai
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Sync tools/include/linux/btf_ids.h with include/linux/btf_ids.h so
tools-side code can use BTF_ID_FLAGS(), BTF_SET8_START(), and
BTF_KFUNCS_START().

Keep the tools copy's existing compiler header dependency:
tools/include/linux/compiler.h already provides __maybe_unused and
tools/include/linux/compiler_attributes.h does not exist.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/include/linux/btf_ids.h | 78 ++++++++++++++++++++++++++++++++---
 1 file changed, 73 insertions(+), 5 deletions(-)

diff --git a/tools/include/linux/btf_ids.h b/tools/include/linux/btf_ids.h
index 72ea363d434d..4fe5c5f1558c 100644
--- a/tools/include/linux/btf_ids.h
+++ b/tools/include/linux/btf_ids.h
@@ -10,6 +10,9 @@ struct btf_id_set {
 	u32 ids[];
 };
 
+/* This flag implies BTF_SET8 holds kfunc(s) */
+#define BTF_SET8_KFUNCS		(1 << 0)
+
 struct btf_id_set8 {
 	u32 cnt;
 	u32 flags;
@@ -22,6 +25,7 @@ struct btf_id_set8 {
 #ifdef CONFIG_DEBUG_INFO_BTF
 
 #include <linux/compiler.h> /* for __PASTE */
+#include <linux/stringify.h>
 
 /*
  * Following macros help to define lists of BTF IDs placed
@@ -35,7 +39,7 @@ struct btf_id_set8 {
 
 #define BTF_IDS_SECTION ".BTF_ids"
 
-#define ____BTF_ID(symbol)				\
+#define ____BTF_ID(symbol, word)			\
 asm(							\
 ".pushsection " BTF_IDS_SECTION ",\"a\";       \n"	\
 ".local " #symbol " ;                          \n"	\
@@ -43,10 +47,11 @@ asm(							\
 ".size  " #symbol ", 4;                        \n"	\
 #symbol ":                                     \n"	\
 ".zero 4                                       \n"	\
+word							\
 ".popsection;                                  \n");
 
-#define __BTF_ID(symbol) \
-	____BTF_ID(symbol)
+#define __BTF_ID(symbol, word) \
+	____BTF_ID(symbol, word)
 
 #define __ID(prefix) \
 	__PASTE(__PASTE(prefix, __COUNTER__), __LINE__)
@@ -56,7 +61,14 @@ asm(							\
  * to 4 zero bytes.
  */
 #define BTF_ID(prefix, name) \
-	__BTF_ID(__ID(__BTF_ID__##prefix##__##name##__))
+	__BTF_ID(__ID(__BTF_ID__##prefix##__##name##__), "")
+
+#define ____BTF_ID_FLAGS(prefix, name, flags) \
+	__BTF_ID(__ID(__BTF_ID__##prefix##__##name##__), ".long " #flags "\n")
+#define __BTF_ID_FLAGS(prefix, name, flags, ...) \
+	____BTF_ID_FLAGS(prefix, name, flags)
+#define BTF_ID_FLAGS(prefix, name, ...) \
+	__BTF_ID_FLAGS(prefix, name, ##__VA_ARGS__, 0)
 
 /*
  * The BTF_ID_LIST macro defines pure (unsorted) list
@@ -155,10 +167,58 @@ asm(							\
 ".popsection;                                 \n");	\
 extern struct btf_id_set name;
 
+/*
+ * The BTF_SET8_START/END macros pair defines sorted list of
+ * BTF IDs and their flags plus its members count, with the
+ * following layout:
+ *
+ * BTF_SET8_START(list)
+ * BTF_ID_FLAGS(type1, name1, flags)
+ * BTF_ID_FLAGS(type2, name2, flags)
+ * BTF_SET8_END(list)
+ *
+ * __BTF_ID__set8__list:
+ * .zero 8
+ * list:
+ * __BTF_ID__type1__name1__3:
+ * .zero 4
+ * .word (1 << 0) | (1 << 2)
+ * __BTF_ID__type2__name2__5:
+ * .zero 4
+ * .word (1 << 3) | (1 << 1) | (1 << 2)
+ *
+ */
+#define __BTF_SET8_START(name, scope, flags)		\
+__BTF_ID_LIST(name, local)				\
+asm(							\
+".pushsection " BTF_IDS_SECTION ",\"a\";       \n"	\
+"." #scope " __BTF_ID__set8__" #name ";        \n"	\
+"__BTF_ID__set8__" #name ":;                   \n"	\
+".zero 4                                       \n"	\
+".long " __stringify(flags)                   "\n"	\
+".popsection;                                  \n");
+
+#define BTF_SET8_START(name)				\
+__BTF_SET8_START(name, local, 0)
+
+#define BTF_SET8_END(name)				\
+asm(							\
+".pushsection " BTF_IDS_SECTION ",\"a\";      \n"	\
+".size __BTF_ID__set8__" #name ", .-" #name "  \n"	\
+".popsection;                                 \n");	\
+extern struct btf_id_set8 name;
+
+#define BTF_KFUNCS_START(name)				\
+__BTF_SET8_START(name, local, BTF_SET8_KFUNCS)
+
+#define BTF_KFUNCS_END(name)				\
+BTF_SET8_END(name)
+
 #else
 
-#define BTF_ID_LIST(name) static u32 __maybe_unused name[5];
+#define BTF_ID_LIST(name) static u32 __maybe_unused name[128];
 #define BTF_ID(prefix, name)
+#define BTF_ID_FLAGS(prefix, name, ...)
 #define BTF_ID_UNUSED
 #define BTF_ID_LIST_GLOBAL(name, n) u32 __maybe_unused name[n];
 #define BTF_ID_LIST_SINGLE(name, prefix, typename) static u32 __maybe_unused name[1];
@@ -166,6 +226,10 @@ extern struct btf_id_set name;
 #define BTF_SET_START(name) static struct btf_id_set __maybe_unused name = { 0 };
 #define BTF_SET_START_GLOBAL(name) static struct btf_id_set __maybe_unused name = { 0 };
 #define BTF_SET_END(name)
+#define BTF_SET8_START(name) static struct btf_id_set8 __maybe_unused name = { 0 };
+#define BTF_SET8_END(name)
+#define BTF_KFUNCS_START(name) static struct btf_id_set8 __maybe_unused name = { .flags = BTF_SET8_KFUNCS };
+#define BTF_KFUNCS_END(name)
 
 #endif /* CONFIG_DEBUG_INFO_BTF */
 
@@ -215,5 +279,9 @@ MAX_BTF_TRACING_TYPE,
 };
 
 extern u32 btf_tracing_ids[];
+extern u32 bpf_cgroup_btf_id[];
+extern u32 bpf_local_storage_map_btf_id[];
+extern u32 btf_bpf_map_id[];
+extern u32 bpf_kmem_cache_btf_id[];
 
 #endif
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
  2026-06-01 22:17 ` [PATCH bpf-next v1 01/14] tools/bpf: Sync btf_ids.h to tools Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-02 13:02   ` Jiri Olsa
  2026-06-01 22:17 ` [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds Ihor Solodrai
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Refactor resolve_btfids test in order to:
  * use newer ASSERT_* macros instead of CHECK
  * extend the lifetime of loaded BTF to enable additional checks
  * cleanup unused/unnecessary code

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 .../selftests/bpf/prog_tests/resolve_btfids.c | 54 +++++++------------
 1 file changed, 18 insertions(+), 36 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
index 41dfaaabb73f..d742ecb2ca8e 100644
--- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
+++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
@@ -10,8 +10,6 @@
 #include <linux/btf_ids.h>
 #include "test_progs.h"
 
-static int duration;
-
 struct symbol {
 	const char	*name;
 	int		 type;
@@ -95,25 +93,15 @@ __resolve_symbol(struct btf *btf, int type_id)
 	return 0;
 }
 
-static int resolve_symbols(void)
+static int resolve_symbols(struct btf *btf)
 {
-	struct btf *btf;
+	__u32 nr = btf__type_cnt(btf);
 	int type_id;
-	__u32 nr;
-
-	btf = btf__parse_raw("resolve_btfids.test.o.BTF");
-	if (CHECK(libbpf_get_error(btf), "resolve",
-		  "Failed to load BTF from resolve_btfids.test.o.BTF\n"))
-		return -1;
-
-	nr = btf__type_cnt(btf);
 
 	for (type_id = 1; type_id < nr; type_id++) {
 		if (__resolve_symbol(btf, type_id))
-			break;
+			return -1;
 	}
-
-	btf__free(btf);
 	return 0;
 }
 
@@ -121,25 +109,22 @@ void test_resolve_btfids(void)
 {
 	__u32 *test_list, *test_lists[] = { test_list_local, test_list_global };
 	unsigned int i, j;
-	int ret = 0;
+	struct btf *btf;
 
-	if (resolve_symbols())
-		return;
+	btf = btf__parse_raw("resolve_btfids.test.o.BTF");
+	if (!ASSERT_OK_PTR(btf, "btf_parse"))
+		goto out;
+
+	if (resolve_symbols(btf))
+		goto out;
 
 	/* Check BTF_ID_LIST(test_list_local) and
 	 * BTF_ID_LIST_GLOBAL(test_list_global) IDs
 	 */
 	for (j = 0; j < ARRAY_SIZE(test_lists); j++) {
 		test_list = test_lists[j];
-		for (i = 0; i < ARRAY_SIZE(test_symbols); i++) {
-			ret = CHECK(test_list[i] != test_symbols[i].id,
-				    "id_check",
-				    "wrong ID for %s (%d != %d)\n",
-				    test_symbols[i].name,
-				    test_list[i], test_symbols[i].id);
-			if (ret)
-				return;
-		}
+		for (i = 0; i < ARRAY_SIZE(test_symbols); i++)
+			ASSERT_EQ(test_list[i], test_symbols[i].id, test_symbols[i].name);
 	}
 
 	/* Check BTF_SET_START(test_set) IDs */
@@ -153,15 +138,12 @@ void test_resolve_btfids(void)
 			break;
 		}
 
-		ret = CHECK(!found, "id_check",
-			    "ID %d not found in test_symbols\n",
-			    test_set.ids[i]);
-		if (ret)
-			break;
+		ASSERT_TRUE(found, "id_in_test_symbols");
 
-		if (i > 0) {
-			if (!ASSERT_LE(test_set.ids[i - 1], test_set.ids[i], "sort_check"))
-				return;
-		}
+		if (i > 0)
+			ASSERT_LE(test_set.ids[i - 1], test_set.ids[i], "sort_check");
 	}
+
+out:
+	btf__free(btf);
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
  2026-06-01 22:17 ` [PATCH bpf-next v1 01/14] tools/bpf: Sync btf_ids.h to tools Ihor Solodrai
  2026-06-01 22:17 ` [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-03 23:45   ` Andrii Nakryiko
  2026-06-01 22:17 ` [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids Ihor Solodrai
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

TL;DR

On aarch64 with gcc toolchain, when test_progs is linked as a PIE,
reads of BTF ID array by C name return garbage because the GNU
assembler on aarch64 unconditionally folds .local symbol references
into section+addend form, and GOT slots cannot carry an addend per the
AArch64 ELF spec.

Fix by marking the test's BTF ID objects with hidden visibility, which
makes gcc emit a direct access that bypasses the GOT entirely.

Details below.

The subsequent patches adding kfunc checks to resolve_btfids test may
cause test failures on aarch64 / gcc-15:

  test_resolve_btfids:FAIL:kfunc_set_flags  actual 13 != expected 1
  test_resolve_btfids:FAIL:kfunc_set_cnt    actual 0  != expected 4

The test defines its BTF ID sets with the same macros as the kernel
and reads them back directly by C name (in the same way as the kernel
code does).

test_kfunc_set is a .local symbol emitted into .BTF_ids by inline asm
and declared to the compiler as a plain default-visibility
extern, that is:
    extern struct btf_id_set8 test_kfunc_set;

Depending on the build environment, test_progs may be linked as a
position-independent executable (for example, gcc defaults to -fpie
[1]). In a PIE, taking the address of a default-visibility extern is
routed through the GOT (Global Offset Table) [2].

The GNU assembler's adjust_reloc_syms() pass (gas/write.c [3])
replaces references to local symbols with the corresponding section
symbol, folding the symbol's offset into the relocation addend.  On
aarch64 this conversion is unconditional: tc_fix_adjustable() is
defined to 1 for all fixups (gas/config/tc-aarch64.h [4]), so even
GOT-generating relocations are subject to it.  The resulting object
file therefore contains:

  R_AARCH64_ADR_GOT_PAGE      .BTF_ids + 0x54
  R_AARCH64_LD64_GOT_LO12_NC  .BTF_ids + 0x54

However, the AArch64 ELF specification mandates that GOT-generating
relocations must have a zero addend [5].  The +0x54 is therefore not
honored: the linker creates a GOT slot pointing at the .BTF_ids base,
and every access through that slot reads offset 0 instead of 0x54.

This is purely a read-side problem, specific to the PIE test binary on
aarch64 with gcc toolchain. resolve_btfids patches the set header
correctly and the .BTF_ids bytes in test_progs are correct. vmlinux is
unaffected because it is built with -fno-PIE [6] and reaches .BTF_ids
with direct, addend-preserving relocations rather than the GOT. clang
is unaffected because LLVM's assembler retains the original symbol for
GOT relocations instead of converting to section+addend [7].

To mitigate this issue, mark the test's BTF ID objects hidden with a
visibility pragma so that gcc treats them as non-interposable and
emits a direct access instead of a GOT load. This keeps the natural
by-name access, works in both PIE and non-PIE builds, and needs no
change to the BTF_ID macros or resolve_btfids.

[1] https://gcc.gnu.org/onlinedocs/gnat_ugn/Position-Independent-Executable-PIE-Enabled-by-Default-on-Linux.html
[2] https://gcc.gnu.org/wiki/Visibility
[3] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/write.c#l922
[4] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/config/tc-aarch64.h#l279
[5] https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#5733relocation-operations
[6] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Makefile?h=v7.1-rc6#n593
[7] https://github.com/llvm/llvm-project/blob/4b3bc46d1d794b8ed78b75ccd35a6cc30235bf31/llvm/lib/MC/ELFObjectWriter.cpp#L1213-L1224

---
---
 .../selftests/bpf/prog_tests/resolve_btfids.c  | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
index d742ecb2ca8e..d0e51979d455 100644
--- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
+++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
@@ -32,6 +32,23 @@ asm (
 ".balign 4, 0;                            \n"
 ".popsection;                             \n");
 
+/*
+ * The BTF ID arrays below are .local symbols placed in .BTF_ids by
+ * inline asm, and are read here directly by C name. To the compiler
+ * they are plain, default-visibility extern objects.
+ *
+ * When test_progs is linked as a position-independent executable (PIE),
+ * taking the address of such an extern is routed through the GOT. The
+ * GNU assembler on aarch64 unconditionally converts references to
+ * .local symbols into section + addend form (".BTF_ids + <offset>"),
+ * but a GOT slot cannot carry an addend (the AArch64 ELF spec mandates
+ * zero), so the linker resolves it to the .BTF_ids base.
+ *
+ * Mark these symbols hidden so the compiler treats them as
+ * non-interposable and emits a direct, addend-preserving PC-relative
+ * access instead of a GOT load, in both PIE and non-PIE builds.
+ */
+#pragma GCC visibility push(hidden)
 BTF_ID_LIST(test_list_local)
 BTF_ID_UNUSED
 BTF_ID(typedef, S)
@@ -60,6 +77,7 @@ BTF_ID(union,   U)
 BTF_ID(func,    func)
 BTF_SET_END(test_set)
 
+#pragma GCC visibility pop
 static int
 __resolve_symbol(struct btf *btf, int type_id)
 {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (2 preceding siblings ...)
  2026-06-01 22:17 ` [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-02 13:02   ` Jiri Olsa
  2026-06-03 23:45   ` Andrii Nakryiko
  2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
                   ` (10 subsequent siblings)
  14 siblings, 2 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Extend the resolve_btfids selftest to cover kfunc sets defined with
BTF_KFUNCS_START/BTF_KFUNCS_END.

The test verifies that resolve_btfids correctly processes BTF_ID_FLAGS,
resolves function IDs, and checks the kfunc set is sorted.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 .../selftests/bpf/prog_tests/resolve_btfids.c | 56 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/btf_data.c  | 10 ++++
 2 files changed, 66 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
index d0e51979d455..f6fd79b9dd23 100644
--- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
+++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
@@ -10,6 +10,10 @@
 #include <linux/btf_ids.h>
 #include "test_progs.h"
 
+#ifndef KF_FASTCALL
+#define KF_FASTCALL (1 << 12)
+#endif
+
 struct symbol {
 	const char	*name;
 	int		 type;
@@ -26,6 +30,17 @@ struct symbol test_symbols[] = {
 	{ "func",    BTF_KIND_FUNC,    -1 },
 };
 
+struct kfunc_symbol {
+	const char	*name;
+	s32		 id;
+	u32		 flags;
+};
+
+static struct kfunc_symbol kfunc_symbols[] = {
+	{ "kfunc_a", -1, 0 },
+	{ "kfunc_b", -1, KF_FASTCALL },
+};
+
 /* Align the .BTF_ids section to 4 bytes */
 asm (
 ".pushsection " BTF_IDS_SECTION " ,\"a\"; \n"
@@ -77,7 +92,13 @@ BTF_ID(union,   U)
 BTF_ID(func,    func)
 BTF_SET_END(test_set)
 
+BTF_KFUNCS_START(test_kfunc_set)
+BTF_ID_FLAGS(func, kfunc_a)
+BTF_ID_FLAGS(func, kfunc_b, KF_FASTCALL)
+BTF_KFUNCS_END(test_kfunc_set)
+
 #pragma GCC visibility pop
+
 static int
 __resolve_symbol(struct btf *btf, int type_id)
 {
@@ -108,6 +129,18 @@ __resolve_symbol(struct btf *btf, int type_id)
 			test_symbols[i].id = type_id;
 	}
 
+	if (BTF_INFO_KIND(type->info) == BTF_KIND_FUNC) {
+		str = btf__name_by_offset(btf, type->name_off);
+		if (str) {
+			for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++) {
+				if (kfunc_symbols[i].id >= 0)
+					continue;
+				if (!strcmp(str, kfunc_symbols[i].name))
+					kfunc_symbols[i].id = type_id;
+			}
+		}
+	}
+
 	return 0;
 }
 
@@ -162,6 +195,29 @@ void test_resolve_btfids(void)
 			ASSERT_LE(test_set.ids[i - 1], test_set.ids[i], "sort_check");
 	}
 
+	/* Check BTF_KFUNCS_START(test_kfunc_set) */
+	ASSERT_EQ(test_kfunc_set.flags, BTF_SET8_KFUNCS, "kfunc_set_flags");
+	ASSERT_EQ(test_kfunc_set.cnt, ARRAY_SIZE(kfunc_symbols), "kfunc_set_cnt");
+
+	for (i = 0; i < test_kfunc_set.cnt; i++) {
+		bool found = false;
+
+		for (j = 0; j < ARRAY_SIZE(kfunc_symbols); j++) {
+			if (kfunc_symbols[j].id != (s32)test_kfunc_set.pairs[i].id)
+				continue;
+			found = true;
+			ASSERT_EQ(test_kfunc_set.pairs[i].flags,
+				  kfunc_symbols[j].flags, "kfunc_flags_check");
+			break;
+		}
+
+		ASSERT_TRUE(found, "kfunc_id_found");
+
+		if (i > 0)
+			ASSERT_LE(test_kfunc_set.pairs[i - 1].id,
+				  test_kfunc_set.pairs[i].id, "kfunc_sort_check");
+	}
+
 out:
 	btf__free(btf);
 }
diff --git a/tools/testing/selftests/bpf/progs/btf_data.c b/tools/testing/selftests/bpf/progs/btf_data.c
index baa525275bde..8587658012c3 100644
--- a/tools/testing/selftests/bpf/progs/btf_data.c
+++ b/tools/testing/selftests/bpf/progs/btf_data.c
@@ -48,3 +48,13 @@ int func(struct root_struct *root)
 {
 	return 0;
 }
+
+int kfunc_a(struct root_struct *root)
+{
+	return 0;
+}
+
+int kfunc_b(struct root_struct *root)
+{
+	return 0;
+}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (3 preceding siblings ...)
  2026-06-01 22:17 ` [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-01 22:28   ` sashiko-bot
                     ` (3 more replies)
  2026-06-01 22:17 ` [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets Ihor Solodrai
                   ` (9 subsequent siblings)
  14 siblings, 4 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Keep an address-sorted index of parsed .BTF_ids symbols so code that
the original BTF_ID symbol name can be recovered from an entry
address.

Use the index in find_kfunc_flags() to scan BTF_SET8_KFUNCS entries
directly and match each entry back to the requested kfunc.

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

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index f8a91fa7584f..43512af13148 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -119,6 +119,11 @@ struct btf_id {
 	Elf64_Addr	 addr[ADDR_CNT];
 };
 
+struct addr_sym {
+	Elf64_Addr	 addr;
+	const char	*name;
+};
+
 struct object {
 	const char *path;
 	const char *btf_path;
@@ -150,6 +155,10 @@ struct object {
 	int nr_structs;
 	int nr_unions;
 	int nr_typedefs;
+
+	struct addr_sym *addr_syms;
+	int nr_addr_syms;
+	int max_addr_syms;
 };
 
 #define KF_IMPLICIT_ARGS (1 << 16)
@@ -480,6 +489,49 @@ static int elf_collect(struct object *obj)
 	return 0;
 }
 
+static int push_addr_sym(struct object *obj, Elf64_Addr addr, const char *name)
+{
+	struct addr_sym *arr = obj->addr_syms;
+	int cap = obj->max_addr_syms;
+
+	if (obj->nr_addr_syms + 1 > cap) {
+		cap = max(cap + 256, cap * 2);
+		arr = realloc(arr, sizeof(*arr) * cap);
+		if (!arr)
+			return -ENOMEM;
+		obj->max_addr_syms = cap;
+		obj->addr_syms = arr;
+	}
+
+	obj->addr_syms[obj->nr_addr_syms].addr = addr;
+	obj->addr_syms[obj->nr_addr_syms].name = name;
+	obj->nr_addr_syms++;
+
+	return 0;
+}
+
+static int cmp_addr_sym(const void *a, const void *b)
+{
+	Elf64_Addr aa = ((const struct addr_sym *)a)->addr;
+	Elf64_Addr ab = ((const struct addr_sym *)b)->addr;
+
+	if (aa < ab)
+		return -1;
+	if (aa > ab)
+		return 1;
+	return 0;
+}
+
+static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
+{
+	struct addr_sym key = { .addr = addr };
+	struct addr_sym *res;
+
+	res = bsearch(&key, obj->addr_syms, obj->nr_addr_syms,
+		      sizeof(*obj->addr_syms), cmp_addr_sym);
+	return res ? res->name : NULL;
+}
+
 static int symbols_collect(struct object *obj)
 {
 	Elf_Scn *scn = NULL;
@@ -573,8 +625,14 @@ static int symbols_collect(struct object *obj)
 			return -1;
 		}
 		id->addr[id->addr_cnt++] = sym.st_value;
+
+		if (push_addr_sym(obj, sym.st_value, id->name))
+			return -1;
 	}
 
+	qsort(obj->addr_syms, obj->nr_addr_syms, sizeof(*obj->addr_syms),
+	      cmp_addr_sym);
+
 	return 0;
 }
 
@@ -946,43 +1004,41 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
 }
 
 /*
- * To find the kfunc flags having its struct btf_id (with ELF addresses)
- * we need to find the address that is in range of a set8.
- * If a set8 is found, then the flags are located at addr + 4 bytes.
+ * To find kfunc flags, scan BTF_SET8_KFUNCS entries and use the entry
+ * address to recover the corresponding BTF_ID symbol name.
  * Return 0 (no flags!) if not found.
  */
 static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
 {
 	const u32 *elf_data_ptr = obj->efile.idlist->d_buf;
-	u64 set_lower_addr, set_upper_addr, addr;
 	struct btf_id *set_id;
 	struct rb_node *next;
-	u32 flags;
-	u64 idx;
+	u64 idx, set_addr;
+	u32 set_flags;
 
 	for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
 		set_id = rb_entry(next, struct btf_id, rb_node);
 		if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
 			continue;
 
-		set_lower_addr = set_id->addr[0];
-		set_upper_addr = set_lower_addr + set_id->cnt * sizeof(u64);
+		set_addr = set_id->addr[0];
+		idx = (set_addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
+		set_flags = elf_data_ptr[idx];
+		if (!(set_flags & BTF_SET8_KFUNCS))
+			continue;
 
-		for (u32 i = 0; i < kfunc_id->addr_cnt; i++) {
-			addr = kfunc_id->addr[i];
-			/*
-			 * Lower bound is exclusive to skip the 8-byte header of the set.
-			 * Upper bound is inclusive to capture the last entry at offset 8*cnt.
-			 */
-			if (set_lower_addr < addr && addr <= set_upper_addr) {
-				pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
-					 kfunc_id->name, set_id->name);
-				idx = addr - obj->efile.idlist_addr;
-				idx = idx / sizeof(u32) + 1;
-				flags = elf_data_ptr[idx];
-
-				return flags;
-			}
+		for (u32 i = 0; i < set_id->cnt; i++) {
+			Elf64_Addr addr = set_addr + sizeof(u64) * (i + 1);
+			const char *name = find_name_by_addr(obj, addr);
+
+			if (!name || strcmp(name, kfunc_id->name) != 0)
+				continue;
+
+			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
+				 kfunc_id->name, set_id->name);
+
+			idx = (addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
+			return elf_data_ptr[idx];
 		}
 	}
 
@@ -1575,6 +1631,7 @@ int main(int argc, const char **argv)
 	btf_id__free_all(&obj.typedefs);
 	btf_id__free_all(&obj.funcs);
 	btf_id__free_all(&obj.sets);
+	free(obj.addr_syms);
 	if (obj.efile.elf) {
 		elf_end(obj.efile.elf);
 		close(obj.efile.fd);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (4 preceding siblings ...)
  2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-01 22:33   ` sashiko-bot
                     ` (2 more replies)
  2026-06-01 22:17 ` [PATCH bpf-next v1 07/14] resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs Ihor Solodrai
                   ` (8 subsequent siblings)
  14 siblings, 3 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
list of kfuncs. The decl tags are generated by pahole, which makes
current implementation implicitly rely on those tags being generated.

The authoritative source, used by the the BPF verifier for kfunc
registration, of functions being BPF kfuncs are
BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
hood. Currently resolve_btfids reads kfunc flags from these sets, and
populates them with BTF IDs.

Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
removing the dependency on pahole's emmission of decl tags.

Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
recording the paired flags directly. This makes find_kfunc_flags()
helper unnecessary, so it's removed.

Kernel functions can appear in more than one set, which is legitimate,
since kfunc sets are prog-type dependent in the kernel. So for btf2btf
processing deduplicate kfuncs by BTF ID, accumulate (OR) the flags,
and warn on flags mismatch to catch inconsistent declarations.

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

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 43512af13148..d35a7b2460e8 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -970,6 +970,23 @@ static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
 	struct kfunc *arr = ctx->kfuncs;
 	u32 cap = ctx->max_kfuncs;
 
+	/*
+	 * A kfunc can be listed in multiple BTF ID sets.
+	 * In this case, dedup by btf_id and accumulate kfunc flags.
+	 */
+	for (u32 i = 0; i < ctx->nr_kfuncs; i++) {
+		if (ctx->kfuncs[i].btf_id != kfunc->btf_id)
+			continue;
+
+		if (ctx->kfuncs[i].flags != kfunc->flags) {
+			pr_err("WARN: resolve_btfids: inconsistent flags for kfunc %s: 0x%x != 0x%x\n",
+			       kfunc->name, ctx->kfuncs[i].flags, kfunc->flags);
+			warnings++;
+		}
+		ctx->kfuncs[i].flags |= kfunc->flags;
+		return 0;
+	}
+
 	if (ctx->nr_kfuncs + 1 > cap) {
 		cap = max(cap + 256, cap * 2);
 		arr = realloc(arr, sizeof(struct kfunc) * cap);
@@ -1003,92 +1020,63 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
 	return 0;
 }
 
-/*
- * To find kfunc flags, scan BTF_SET8_KFUNCS entries and use the entry
- * address to recover the corresponding BTF_ID symbol name.
- * Return 0 (no flags!) if not found.
- */
-static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
+static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
 {
-	const u32 *elf_data_ptr = obj->efile.idlist->d_buf;
-	struct btf_id *set_id;
+	Elf_Data *idlist = obj->efile.idlist;
+	struct btf *btf = ctx->btf;
 	struct rb_node *next;
-	u64 idx, set_addr;
-	u32 set_flags;
+
+	if (!idlist || !idlist->d_buf)
+		return 0;
 
 	for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
+		struct btf_id_set8 *set8;
+		struct btf_id *set_id;
+		unsigned long off;
+		u64 set_addr;
+		int err;
+
 		set_id = rb_entry(next, struct btf_id, rb_node);
 		if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
 			continue;
 
 		set_addr = set_id->addr[0];
-		idx = (set_addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
-		set_flags = elf_data_ptr[idx];
-		if (!(set_flags & BTF_SET8_KFUNCS))
+		off = set_addr - obj->efile.idlist_addr;
+		set8 = idlist->d_buf + off;
+		if (!(set8->flags & BTF_SET8_KFUNCS))
 			continue;
 
 		for (u32 i = 0; i < set_id->cnt; i++) {
 			Elf64_Addr addr = set_addr + sizeof(u64) * (i + 1);
 			const char *name = find_name_by_addr(obj, addr);
+			struct kfunc kfunc;
+			s32 func_id;
 
-			if (!name || strcmp(name, kfunc_id->name) != 0)
+			if (!name) {
+				pr_err("WARN: resolve_btfids: no kfunc symbol for set %s entry %u\n",
+				       set_id->name, i);
+				warnings++;
 				continue;
+			}
 
-			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
-				 kfunc_id->name, set_id->name);
-
-			idx = (addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
-			return elf_data_ptr[idx];
-		}
-	}
-
-	return 0;
-}
-
-static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
-{
-	const char *tag_name, *func_name;
-	struct btf *btf = ctx->btf;
-	const struct btf_type *t;
-	u32 flags, func_id;
-	struct kfunc kfunc;
-	struct btf_id *id;
-	int err;
-
-	if (ctx->nr_decl_tags == 0)
-		return 0;
-
-	for (u32 i = 0; i < ctx->nr_decl_tags; i++) {
-		t = btf__type_by_id(btf, ctx->decl_tags[i]);
-		if (btf_kflag(t) || btf_decl_tag(t)->component_idx != -1)
-			continue;
-
-		tag_name = btf__name_by_offset(btf, t->name_off);
-		if (strcmp(tag_name, "bpf_kfunc") != 0)
-			continue;
-
-		func_id = t->type;
-		t = btf__type_by_id(btf, func_id);
-		if (!btf_is_func(t))
-			continue;
-
-		func_name = btf__name_by_offset(btf, t->name_off);
-		if (!func_name)
-			continue;
-
-		id = btf_id__find(&obj->funcs, func_name);
-		if (!id || id->kind != BTF_ID_KIND_SYM)
-			continue;
+			func_id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
+			if (func_id < 0) {
+				pr_err("WARN: resolve_btfids: no BTF FUNC for kfunc %s in set %s\n",
+				       name, set_id->name);
+				warnings++;
+				continue;
+			}
 
-		flags = find_kfunc_flags(obj, id);
+			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n", name, set_id->name);
 
-		kfunc.name = id->name;
-		kfunc.btf_id = func_id;
-		kfunc.flags = flags;
+			kfunc.name = name;
+			kfunc.btf_id = func_id;
+			kfunc.flags = set8->pairs[i].flags;
 
-		err = push_kfunc(ctx, &kfunc);
-		if (err)
-			return err;
+			err = push_kfunc(ctx, &kfunc);
+			if (err)
+				return err;
+		}
 	}
 
 	return 0;
@@ -1108,7 +1096,7 @@ static int build_btf2btf_context(struct object *obj, struct btf2btf_context *ctx
 
 	err = collect_kfuncs(obj, ctx);
 	if (err) {
-		pr_err("ERROR: resolve_btfids: failed to collect kfuncs from BTF\n");
+		pr_err("ERROR: resolve_btfids: failed to collect kfuncs from BTF ID sets\n");
 		return err;
 	}
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 07/14] resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (5 preceding siblings ...)
  2026-06-01 22:17 ` [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-03 23:45   ` Andrii Nakryiko
  2026-06-01 22:17 ` [PATCH bpf-next v1 08/14] selftests/bpf: Verify bpf_kfunc decl tag emission in resolve_btfids Ihor Solodrai
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Now that collect_kfuncs() discovers kfuncs from BTF_SET8_KFUNCS sets,
resolve_btfids can also be the source of truth for the bpf_kfunc decl
tag in BTF.

Today pahole emits it under --btf_features=decl_tag_kfuncs; moving the
emission in-tree lets that flag be dropped from the kernel build once
the rest of the kfunc annotations follow.

The implementation in resolve_btfids is idempotent: if a decl_tag
exists in the input BTF, no new tag will be emitted. This allows to be
more independent of pahole's version and runtime flags used to
generate the BTF.

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

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index d35a7b2460e8..30a07ec4ebb6 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -1255,6 +1255,44 @@ static int process_kfunc_with_implicit_args(struct btf2btf_context *ctx, struct
 	return 0;
 }
 
+static bool btf_has_decl_tag(struct btf2btf_context *ctx, const char *tag_name,
+			     u32 target_btf_id, int component_idx)
+{
+	const struct btf_type *t;
+	const char *name;
+
+	for (u32 i = 0; i < ctx->nr_decl_tags; i++) {
+		t = btf__type_by_id(ctx->btf, ctx->decl_tags[i]);
+		if (t->type != target_btf_id)
+			continue;
+		if (btf_decl_tag(t)->component_idx != component_idx)
+			continue;
+		name = btf__name_by_offset(ctx->btf, t->name_off);
+		if (name && strcmp(name, tag_name) == 0)
+			return true;
+	}
+	return false;
+}
+
+/* Add a decl tag if an identical one is not already present. */
+static int ensure_decl_tag(struct btf2btf_context *ctx, const char *tag_name,
+			   u32 target_btf_id, int component_idx)
+{
+	int new_id;
+
+	if (btf_has_decl_tag(ctx, tag_name, target_btf_id, component_idx))
+		return 0;
+
+	new_id = btf__add_decl_tag(ctx->btf, tag_name, target_btf_id, component_idx);
+	if (new_id < 0) {
+		pr_err("ERROR: resolve_btfids: failed to add '%s' decl tag for BTF id %u: %d\n",
+		       tag_name, target_btf_id, new_id);
+		return new_id;
+	}
+
+	return push_decl_tag_id(ctx, new_id);
+}
+
 static int btf2btf(struct object *obj)
 {
 	struct btf2btf_context ctx = {};
@@ -1267,12 +1305,15 @@ static int btf2btf(struct object *obj)
 	for (u32 i = 0; i < ctx.nr_kfuncs; i++) {
 		struct kfunc *kfunc = &ctx.kfuncs[i];
 
-		if (!(kfunc->flags & KF_IMPLICIT_ARGS))
-			continue;
-
-		err = process_kfunc_with_implicit_args(&ctx, kfunc);
+		err = ensure_decl_tag(&ctx, "bpf_kfunc", kfunc->btf_id, -1);
 		if (err)
 			goto out;
+
+		if (kfunc->flags & KF_IMPLICIT_ARGS) {
+			err = process_kfunc_with_implicit_args(&ctx, kfunc);
+			if (err)
+				goto out;
+		}
 	}
 
 	err = 0;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 08/14] selftests/bpf: Verify bpf_kfunc decl tag emission in resolve_btfids
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (6 preceding siblings ...)
  2026-06-01 22:17 ` [PATCH bpf-next v1 07/14] resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs Ihor Solodrai
@ 2026-06-01 22:17 ` Ihor Solodrai
  2026-06-01 22:18 ` [PATCH bpf-next v1 09/14] resolve_btfids: Emit a decl tag for kfuncs with KF_FASTCALL Ihor Solodrai
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:17 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Extend prog_tests/resolve_btfids.c to assert that resolve_btfids emits a
BTF_KIND_DECL_TAG of name "bpf_kfunc" for each kfunc declared in the
test BTF ID set. Add a small btf_has_decl_tag() helper that walks the
output BTF looking for a matching decl tag (name + target FUNC +
component_idx == -1).

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

diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
index f6fd79b9dd23..7d9c3460cbed 100644
--- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
+++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
@@ -156,6 +156,28 @@ static int resolve_symbols(struct btf *btf)
 	return 0;
 }
 
+static bool btf_has_decl_tag(struct btf *btf, const char *tag_name, s32 target_id)
+{
+	const struct btf_type *t;
+	const char *name;
+	int nr, id;
+
+	nr = btf__type_cnt(btf);
+	for (id = 1; id < nr; id++) {
+		t = btf__type_by_id(btf, id);
+		if (!btf_is_decl_tag(t))
+			continue;
+		if (t->type != (__u32)target_id)
+			continue;
+		if (btf_decl_tag(t)->component_idx != -1)
+			continue;
+		name = btf__name_by_offset(btf, t->name_off);
+		if (name && strcmp(name, tag_name) == 0)
+			return true;
+	}
+	return false;
+}
+
 void test_resolve_btfids(void)
 {
 	__u32 *test_list, *test_lists[] = { test_list_local, test_list_global };
@@ -218,6 +240,12 @@ void test_resolve_btfids(void)
 				  test_kfunc_set.pairs[i].id, "kfunc_sort_check");
 	}
 
+	/* Check resolve_btfids emitted bpf_kfunc decl_tag for each kfunc */
+	for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++)
+		ASSERT_TRUE(btf_has_decl_tag(btf, "bpf_kfunc",
+					     kfunc_symbols[i].id),
+			    kfunc_symbols[i].name);
+
 out:
 	btf__free(btf);
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 09/14] resolve_btfids: Emit a decl tag for kfuncs with KF_FASTCALL
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (7 preceding siblings ...)
  2026-06-01 22:17 ` [PATCH bpf-next v1 08/14] selftests/bpf: Verify bpf_kfunc decl tag emission in resolve_btfids Ihor Solodrai
@ 2026-06-01 22:18 ` Ihor Solodrai
  2026-06-01 22:18 ` [PATCH bpf-next v1 10/14] selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test Ihor Solodrai
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:18 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Following bpf_kfunc, also emit the bpf_fastcall decl tag for kfuncs
marked with KF_FASTCALL, so resolve_btfids becomes the source of truth
for this annotation too.

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

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 30a07ec4ebb6..f276200b1a68 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -161,6 +161,7 @@ struct object {
 	int max_addr_syms;
 };
 
+#define KF_FASTCALL (1 << 12)
 #define KF_IMPLICIT_ARGS (1 << 16)
 #define KF_IMPL_SUFFIX "_impl"
 
@@ -1309,6 +1310,12 @@ static int btf2btf(struct object *obj)
 		if (err)
 			goto out;
 
+		if (kfunc->flags & KF_FASTCALL) {
+			err = ensure_decl_tag(&ctx, "bpf_fastcall", kfunc->btf_id, -1);
+			if (err)
+				goto out;
+		}
+
 		if (kfunc->flags & KF_IMPLICIT_ARGS) {
 			err = process_kfunc_with_implicit_args(&ctx, kfunc);
 			if (err)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 10/14] selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (8 preceding siblings ...)
  2026-06-01 22:18 ` [PATCH bpf-next v1 09/14] resolve_btfids: Emit a decl tag for kfuncs with KF_FASTCALL Ihor Solodrai
@ 2026-06-01 22:18 ` Ihor Solodrai
  2026-06-03 23:47   ` Andrii Nakryiko
  2026-06-01 22:18 ` [PATCH bpf-next v1 11/14] resolve_btfids: Process KF_ARENA_* flags in resolve_btfids Ihor Solodrai
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:18 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Extend test_resolve_btfids() to assert that resolve_btfids emits a
BTF_KIND_DECL_TAG of name "bpf_fastcall" for each kfunc marked with
KF_FASTCALL flag.

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

diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
index 7d9c3460cbed..eeda4e3b6a7f 100644
--- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
+++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
@@ -246,6 +246,13 @@ void test_resolve_btfids(void)
 					     kfunc_symbols[i].id),
 			    kfunc_symbols[i].name);
 
+	/* Check resolve_btfids emitted bpf_fastcall decl_tag for fastcall kfuncs */
+	for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++)
+		if (kfunc_symbols[i].flags & KF_FASTCALL)
+			ASSERT_TRUE(btf_has_decl_tag(btf, "bpf_fastcall",
+						     kfunc_symbols[i].id),
+				    kfunc_symbols[i].name);
+
 out:
 	btf__free(btf);
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 11/14] resolve_btfids: Process KF_ARENA_* flags in resolve_btfids
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (9 preceding siblings ...)
  2026-06-01 22:18 ` [PATCH bpf-next v1 10/14] selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test Ihor Solodrai
@ 2026-06-01 22:18 ` Ihor Solodrai
  2026-06-03 23:47   ` Andrii Nakryiko
  2026-06-01 22:18 ` [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test Ihor Solodrai
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:18 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

For kfuncs flagged KF_ARENA_RET, KF_ARENA_ARG1 or KF_ARENA_ARG2,
address_space(1) attribute (type tag with kflag=1) needs to be emitted
to BTF for the return type or arg type respectively.

So far this has been done by pahole [1].

Implement the emission of the arena attributes in resolve_btfids: for
flagged kfuncs create a new function prototype with updated BTF types,
adding tags as necessary.

Similar to decl tags emission, the transformation is idempotent: if
the types are already tagged with address_space(1), it's a noop.

[1] https://lore.kernel.org/dwarves/20250228194654.1022535-1-ihor.solodrai@linux.dev/

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

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index f276200b1a68..aafbcfec755b 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -162,6 +162,9 @@ struct object {
 };
 
 #define KF_FASTCALL (1 << 12)
+#define KF_ARENA_RET (1 << 13)
+#define KF_ARENA_ARG1 (1 << 14)
+#define KF_ARENA_ARG2 (1 << 15)
 #define KF_IMPLICIT_ARGS (1 << 16)
 #define KF_IMPL_SUFFIX "_impl"
 
@@ -1294,6 +1297,128 @@ static int ensure_decl_tag(struct btf2btf_context *ctx, const char *tag_name,
 	return push_decl_tag_id(ctx, new_id);
 }
 
+static bool is_arena_type_attr(struct btf *btf, u32 id)
+{
+	const struct btf_type *t = btf__type_by_id(btf, id);
+	const char *name;
+
+	if (!t || !btf_is_type_tag(t) || !btf_kflag(t))
+		return false;
+	name = btf__name_by_offset(btf, t->name_off);
+	return name && strcmp(name, "address_space(1)") == 0;
+}
+
+static s32 ensure_arena_tagged_ptr(struct btf *btf, u32 ptr_id)
+{
+	const struct btf_type *ptr = btf__type_by_id(btf, ptr_id);
+	s32 tag_id;
+
+	if (!ptr || !btf_is_ptr(ptr))
+		return -EINVAL;
+
+	if (is_arena_type_attr(btf, ptr->type))
+		return ptr_id;
+
+	tag_id = btf__add_type_attr(btf, "address_space(1)", ptr->type);
+	if (tag_id < 0)
+		return tag_id;
+
+	return btf__add_ptr(btf, tag_id);
+}
+
+/*
+ * Build a FUNC_PROTO for @kfunc with each arena-flagged return/parameter
+ * pointer tagged with address_space(1). Pointers already tagged are kept as is.
+ *
+ * If nothing needs tagging, the original proto id is returned unchanged.
+ * Otherwise a new FUNC_PROTO is created and its id returned. The original
+ * proto may be shared with sibling FUNCs, so it must not be modified in place.
+ */
+static s32 ensure_arena_tagged_proto(struct btf *btf, struct kfunc *kfunc)
+{
+	const struct btf_type *func = btf__type_by_id(btf, kfunc->btf_id);
+	u32 proto_id = func->type;
+	const struct btf_type *proto = btf__type_by_id(btf, proto_id);
+	const struct btf_param *params = btf_params(proto);
+	u32 nr_params = btf_vlen(proto);
+	s32 arg0_type_id = nr_params > 0 ? (s32)params[0].type : -1;
+	s32 arg1_type_id = nr_params > 1 ? (s32)params[1].type : -1;
+	s32 ret_type_id = proto->type;
+	s32 new_proto_id, id;
+	bool changed = false;
+	int err;
+
+	if (kfunc->flags & KF_ARENA_RET) {
+		id = ensure_arena_tagged_ptr(btf, ret_type_id);
+		if (id < 0)
+			return id;
+		changed |= id != ret_type_id;
+		ret_type_id = id;
+	}
+
+	if (nr_params > 0 && (kfunc->flags & KF_ARENA_ARG1)) {
+		id = ensure_arena_tagged_ptr(btf, arg0_type_id);
+		if (id < 0)
+			return id;
+		changed |= id != arg0_type_id;
+		arg0_type_id = id;
+	}
+
+	if (nr_params > 1 && (kfunc->flags & KF_ARENA_ARG2)) {
+		id = ensure_arena_tagged_ptr(btf, arg1_type_id);
+		if (id < 0)
+			return id;
+		changed |= id != arg1_type_id;
+		arg1_type_id = id;
+	}
+
+	if (!changed)
+		return proto_id;
+
+	new_proto_id = btf__add_func_proto(btf, ret_type_id);
+	if (new_proto_id < 0)
+		return new_proto_id;
+
+	for (int i = 0; i < nr_params; i++) {
+		s32 param_type_id;
+		const char *name;
+
+		proto = btf__type_by_id(btf, proto_id);
+		params = btf_params(proto);
+		name = btf__name_by_offset(btf, params[i].name_off);
+
+		if (i == 0)
+			param_type_id = arg0_type_id;
+		else if (i == 1)
+			param_type_id = arg1_type_id;
+		else
+			param_type_id = params[i].type;
+
+		err = btf__add_func_param(btf, name ?: "", param_type_id);
+		if (err < 0)
+			return err;
+	}
+
+	pr_debug("resolve_btfids: added arena-tagged proto for kfunc %s: %d\n", kfunc->name, new_proto_id);
+
+	return new_proto_id;
+}
+
+static int process_kfunc_with_arena_flags(struct btf2btf_context *ctx, struct kfunc *kfunc)
+{
+	struct btf_type *t;
+	s32 proto_id;
+
+	proto_id = ensure_arena_tagged_proto(ctx->btf, kfunc);
+	if (proto_id < 0)
+		return proto_id;
+
+	t = (struct btf_type *)btf__type_by_id(ctx->btf, kfunc->btf_id);
+	t->type = proto_id;
+
+	return 0;
+}
+
 static int btf2btf(struct object *obj)
 {
 	struct btf2btf_context ctx = {};
@@ -1321,6 +1446,12 @@ static int btf2btf(struct object *obj)
 			if (err)
 				goto out;
 		}
+
+		if (kfunc->flags & (KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2)) {
+			err = process_kfunc_with_arena_flags(&ctx, kfunc);
+			if (err)
+				goto out;
+		}
 	}
 
 	err = 0;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (10 preceding siblings ...)
  2026-06-01 22:18 ` [PATCH bpf-next v1 11/14] resolve_btfids: Process KF_ARENA_* flags in resolve_btfids Ihor Solodrai
@ 2026-06-01 22:18 ` Ihor Solodrai
  2026-06-01 22:29   ` sashiko-bot
  2026-06-03 23:46   ` Andrii Nakryiko
  2026-06-01 22:18 ` [PATCH bpf-next v1 13/14] kbuild: Drop decl_tag_kfuncs and attributes from pahole flags Ihor Solodrai
                   ` (2 subsequent siblings)
  14 siblings, 2 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:18 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

Extend test_resolve_btfids() to assert that resolve_btfids emits the
address_space(1) type attribute (a BTF_KIND_TYPE_TAG with kflag=1) on
the return type and/or arguments of kfuncs marked with KF_ARENA_RET,
KF_ARENA_ARG1 or KF_ARENA_ARG2.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 .../selftests/bpf/prog_tests/resolve_btfids.c | 59 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/btf_data.c  | 10 ++++
 2 files changed, 69 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
index eeda4e3b6a7f..6449b551fde1 100644
--- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
+++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
@@ -13,6 +13,15 @@
 #ifndef KF_FASTCALL
 #define KF_FASTCALL (1 << 12)
 #endif
+#ifndef KF_ARENA_RET
+#define KF_ARENA_RET  (1 << 13)
+#endif
+#ifndef KF_ARENA_ARG1
+#define KF_ARENA_ARG1 (1 << 14)
+#endif
+#ifndef KF_ARENA_ARG2
+#define KF_ARENA_ARG2 (1 << 15)
+#endif
 
 struct symbol {
 	const char	*name;
@@ -39,6 +48,8 @@ struct kfunc_symbol {
 static struct kfunc_symbol kfunc_symbols[] = {
 	{ "kfunc_a", -1, 0 },
 	{ "kfunc_b", -1, KF_FASTCALL },
+	{ "kfunc_c", -1, KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2 },
+	{ "kfunc_d", -1, KF_ARENA_ARG2 },
 };
 
 /* Align the .BTF_ids section to 4 bytes */
@@ -95,6 +106,8 @@ BTF_SET_END(test_set)
 BTF_KFUNCS_START(test_kfunc_set)
 BTF_ID_FLAGS(func, kfunc_a)
 BTF_ID_FLAGS(func, kfunc_b, KF_FASTCALL)
+BTF_ID_FLAGS(func, kfunc_c, KF_ARENA_RET | KF_ARENA_ARG1 | KF_ARENA_ARG2)
+BTF_ID_FLAGS(func, kfunc_d, KF_ARENA_ARG2)
 BTF_KFUNCS_END(test_kfunc_set)
 
 #pragma GCC visibility pop
@@ -178,6 +191,22 @@ static bool btf_has_decl_tag(struct btf *btf, const char *tag_name, s32 target_i
 	return false;
 }
 
+/* True if @id is PTR -> TYPE_TAG(kflag=1, "address_space(1)") -> pointee */
+static bool is_arena_tagged_ptr(struct btf *btf, __u32 id)
+{
+	const struct btf_type *ptr, *tag;
+	const char *name;
+
+	ptr = btf__type_by_id(btf, id);
+	if (!ptr || !btf_is_ptr(ptr))
+		return false;
+	tag = btf__type_by_id(btf, ptr->type);
+	if (!tag || !btf_is_type_tag(tag) || !btf_kflag(tag))
+		return false;
+	name = btf__name_by_offset(btf, tag->name_off);
+	return name && strcmp(name, "address_space(1)") == 0;
+}
+
 void test_resolve_btfids(void)
 {
 	__u32 *test_list, *test_lists[] = { test_list_local, test_list_global };
@@ -253,6 +282,36 @@ void test_resolve_btfids(void)
 						     kfunc_symbols[i].id),
 				    kfunc_symbols[i].name);
 
+	/* Check resolve_btfids wrapped exactly the arena-flagged return/args with
+	 * the address_space(1) type attribute, and left other pointers/returns
+	 * untouched.
+	 */
+	for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++) {
+		const struct btf_type *fn, *proto;
+		const struct btf_param *params;
+		const char *name = kfunc_symbols[i].name;
+		u32 fl = kfunc_symbols[i].flags;
+		__u32 nr;
+
+		fn = btf__type_by_id(btf, kfunc_symbols[i].id);
+		if (!ASSERT_TRUE(fn && btf_is_func(fn), name))
+			continue;
+		proto = btf__type_by_id(btf, fn->type);
+		if (!ASSERT_TRUE(proto && btf_is_func_proto(proto), name))
+			continue;
+		params = btf_params(proto);
+		nr = btf_vlen(proto);
+
+		ASSERT_EQ(is_arena_tagged_ptr(btf, proto->type),
+			  !!(fl & KF_ARENA_RET), name);
+		if (nr > 0)
+			ASSERT_EQ(is_arena_tagged_ptr(btf, params[0].type),
+				  !!(fl & KF_ARENA_ARG1), name);
+		if (nr > 1)
+			ASSERT_EQ(is_arena_tagged_ptr(btf, params[1].type),
+				  !!(fl & KF_ARENA_ARG2), name);
+	}
+
 out:
 	btf__free(btf);
 }
diff --git a/tools/testing/selftests/bpf/progs/btf_data.c b/tools/testing/selftests/bpf/progs/btf_data.c
index 8587658012c3..ec34f7a6e038 100644
--- a/tools/testing/selftests/bpf/progs/btf_data.c
+++ b/tools/testing/selftests/bpf/progs/btf_data.c
@@ -58,3 +58,13 @@ int kfunc_b(struct root_struct *root)
 {
 	return 0;
 }
+
+struct root_struct *kfunc_c(struct root_struct *a, struct root_struct *b)
+{
+	return a;
+}
+
+int kfunc_d(struct root_struct *a, struct root_struct *b)
+{
+	return 0;
+}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 13/14] kbuild: Drop decl_tag_kfuncs and attributes from pahole flags
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (11 preceding siblings ...)
  2026-06-01 22:18 ` [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test Ihor Solodrai
@ 2026-06-01 22:18 ` Ihor Solodrai
  2026-06-03 23:48   ` Andrii Nakryiko
  2026-06-01 22:18 ` [PATCH bpf-next v1 14/14] docs, resolve_btfids: Document kfunc BTF annotation emission Ihor Solodrai
  2026-06-03 23:45 ` [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Andrii Nakryiko
  14 siblings, 1 reply; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:18 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

resolve_btfids now emits all kfunc-specific BTF annotations for the
kernel: the "bpf_kfunc" and "bpf_fastcall" decl tags and the
address_space(1) type attribute on arena kfunc return/arguments. These
were previously produced by pahole under the "decl_tag_kfuncs" and
"attributes" btf_features.

Drop both from the pahole invocation.

Note that "decl_tag" (generic source __attribute__((btf_decl_tag))
annotations) is a separate feature from "decl_tag_kfuncs" and must be kept.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 scripts/Makefile.btf | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
index e66e13e79653..a1812985a61a 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -14,9 +14,7 @@ pahole-flags-$(call test-ge, $(pahole-ver), 125)	+= --skip_encoding_btf_inconsis
 else
 
 # Switch to using --btf_features for v1.26 and later.
-pahole-flags-$(call test-ge, $(pahole-ver), 126)  = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs
-
-pahole-flags-$(call test-ge, $(pahole-ver), 130) += --btf_features=attributes
+pahole-flags-$(call test-ge, $(pahole-ver), 126)  = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func
 
 pahole-flags-$(call test-ge, $(pahole-ver), 131) += --btf_features=layout
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH bpf-next v1 14/14] docs, resolve_btfids: Document kfunc BTF annotation emission
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (12 preceding siblings ...)
  2026-06-01 22:18 ` [PATCH bpf-next v1 13/14] kbuild: Drop decl_tag_kfuncs and attributes from pahole flags Ihor Solodrai
@ 2026-06-01 22:18 ` Ihor Solodrai
  2026-06-03 23:45 ` [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Andrii Nakryiko
  14 siblings, 0 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-01 22:18 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Alan Maguire, Jiri Olsa, bpf, linux-kbuild

resolve_btfids now emits the bpf_kfunc and bpf_fastcall BTF decl tags
and the arena address_space(1) type attribute for kfuncs, which were
previously produced by pahole.

Reflect this in the in-tree comments and documentation.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 Documentation/bpf/kfuncs.rst    |  8 ++++++++
 scripts/Makefile.btf            |  3 +++
 tools/bpf/resolve_btfids/main.c | 11 +++++++++++
 3 files changed, 22 insertions(+)

diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 4c814ff6061e..d98e666f03a3 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -437,6 +437,14 @@ type. An example is shown below::
         }
         late_initcall(init_subsystem);
 
+At kernel build time the ``resolve_btfids`` tool discovers all kfuncs from the
+registered ``BTF_SET8_KFUNCS`` sets and emits their BTF annotations into the
+kernel's BTF; these annotations were historically produced by pahole. For each
+discovered kfunc ``resolve_btfids`` emits a ``bpf_kfunc`` BTF decl tag, a
+``bpf_fastcall`` decl tag when the kfunc is flagged ``KF_FASTCALL``, and the
+``address_space(1)`` type attribute on the return value and/or arguments flagged
+``KF_ARENA_RET``, ``KF_ARENA_ARG1`` or ``KF_ARENA_ARG2`` (see section 2.8).
+
 2.7  Specifying no-cast aliases with ___init
 --------------------------------------------
 
diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
index a1812985a61a..717e76ce96a7 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -14,6 +14,9 @@ pahole-flags-$(call test-ge, $(pahole-ver), 125)	+= --skip_encoding_btf_inconsis
 else
 
 # Switch to using --btf_features for v1.26 and later.
+#
+# kfunc BTF annotations (bpf_kfunc/bpf_fastcall decl tags and the arena
+# address_space(1) type attribute) are emitted by resolve_btfids, not pahole.
 pahole-flags-$(call test-ge, $(pahole-ver), 126)  = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func
 
 pahole-flags-$(call test-ge, $(pahole-ver), 131) += --btf_features=layout
diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index aafbcfec755b..5a4683d11426 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -58,6 +58,17 @@
  *             __BTF_ID__func__vfs_fallocate__5:
  *             .zero 4
  *	       .word (1 << 3) | (1 << 1) | (1 << 2)
+ *
+ * In addition to resolving BTF IDs, resolve_btfids performs kernel-specific
+ * BTF-to-BTF transformations for kfuncs found in BTF_SET8_KFUNCS sets. For
+ * each such kfunc it:
+ *
+ *   - emits a "bpf_kfunc" decl tag, and "bpf_fastcall" when KF_FASTCALL is set;
+ *   - wraps the return value and/or arguments flagged KF_ARENA_RET,
+ *     KF_ARENA_ARG1 or KF_ARENA_ARG2 with the "address_space(1)" type attribute;
+ *   - rewrites the prototype of KF_IMPLICIT_ARGS kfuncs.
+ *
+ * These kfunc annotations were historically produced by pahole.
  */
 
 #define  _GNU_SOURCE
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address
  2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
@ 2026-06-01 22:28   ` sashiko-bot
  2026-06-01 23:03   ` bot+bpf-ci
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-01 22:28 UTC (permalink / raw)
  To: Ihor Solodrai; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] A maliciously crafted or empty symbol size can cause an integer underflow, leading to a 4-billion iteration loop.
- [Low] Calling qsort and bsearch with a NULL base pointer when the array is empty is Undefined Behavior.
--

commit e05f36e9d7ac18c038a5933609937a41496ecf8f
Author: Ihor Solodrai <ihor.solodrai@linux.dev>

resolve_btfids: Index BTF ID symbols by address

This commit introduces an address-sorted index for parsed .BTF_ids symbols.
It uses this index in find_kfunc_flags() to scan BTF_SET8_KFUNCS entries
directly and match each entry back to the requested kfunc.

> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c

[ ... ]

> @@ -480,6 +489,49 @@ static int elf_collect(struct object *obj)
> +static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
> +{
> +	struct addr_sym key = { .addr = addr };
> +	struct addr_sym *res;
> +
> +	res = bsearch(&key, obj->addr_syms, obj->nr_addr_syms,
> +		      sizeof(*obj->addr_syms), cmp_addr_sym);

[Severity: Low]
Could this call trigger Undefined Behavior if there are no BTF ID symbols?

If the ELF file lacks these symbols, obj->addr_syms remains NULL and
obj->nr_addr_syms is 0. Calling bsearch with a NULL base pointer is
Undefined Behavior under the C standard, which can cause issues with UBSan
builds.

> +	return res ? res->name : NULL;
> +}

[ ... ]

> @@ -573,8 +625,14 @@ static int symbols_collect(struct object *obj)
>  			return -1;
>  		}
>  		id->addr[id->addr_cnt++] = sym.st_value;
> +
> +		if (push_addr_sym(obj, sym.st_value, id->name))
> +			return -1;
>  	}
>  
> +	qsort(obj->addr_syms, obj->nr_addr_syms, sizeof(*obj->addr_syms),
> +	      cmp_addr_sym);

[Severity: Low]
Does this have the same NULL pointer issue as the bsearch call?

If the array is empty and obj->addr_syms is NULL, calling qsort here
triggers the same Undefined Behavior as described above.

> +
>  	return 0;
>  }

[ ... ]

> @@ -946,43 +1004,41 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
>  static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
>  {
>  	const u32 *elf_data_ptr = obj->efile.idlist->d_buf;
>  	struct btf_id *set_id;
>  	struct rb_node *next;
>  	u64 idx, set_addr;
>  	u32 set_flags;
>  
>  	for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
>  		set_id = rb_entry(next, struct btf_id, rb_node);
>  		if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
>  			continue;
>  
>  		set_addr = set_id->addr[0];
>  		idx = (set_addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
>  		set_flags = elf_data_ptr[idx];
>  		if (!(set_flags & BTF_SET8_KFUNCS))
>  			continue;
>  
> -		for (u32 i = 0; i < kfunc_id->addr_cnt; i++) {
> -			addr = kfunc_id->addr[i];
> +		for (u32 i = 0; i < set_id->cnt; i++) {

[Severity: Medium]
Could this loop iterate billions of times if a symbol size is unexpectedly
small?

When parsing SET8 symbols, the count is calculated as:

id->cnt = sym.st_size / sizeof(uint64_t) - 1;

If sym.st_size is less than 8, id->cnt (which is a signed int) becomes -1.

Because the loop index 'i' is an unsigned 32-bit integer, the comparison
promotes the signed -1 to 0xFFFFFFFF. Will this cause the tool to hang
spinning through 4 billion iterations if it processes a corrupted or crafted
ELF object?

> +			Elf64_Addr addr = set_addr + sizeof(u64) * (i + 1);
> +			const char *name = find_name_by_addr(obj, addr);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601221805.821394-1-ihor.solodrai@linux.dev?part=5

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test
  2026-06-01 22:18 ` [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test Ihor Solodrai
@ 2026-06-01 22:29   ` sashiko-bot
  2026-06-03 23:46   ` Andrii Nakryiko
  1 sibling, 0 replies; 38+ messages in thread
From: sashiko-bot @ 2026-06-01 22:29 UTC (permalink / raw)
  To: Ihor Solodrai; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Multi-line comment does not follow the required BPF kernel comment style, with text starting on the same line as the opening '/*'.
--

commit 628aab60f23e099095fe093c7589706d10a3df81
Author: Ihor Solodrai <ihor.solodrai@linux.dev>

selftests/bpf: Verify arena type tags in resolve_btfids test

This commit extends test_resolve_btfids() to verify that resolve_btfids
correctly emits the address_space(1) type attribute for kfuncs flagged
with arena attributes.

> diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> index eeda4e3b6a7f6..6449b551fde1b 100644
> --- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> +++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
[ ... ]
> @@ -253,6 +282,36 @@ void test_resolve_btfids(void)
>  						     kfunc_symbols[i].id),
>  				    kfunc_symbols[i].name);
>  
> +	/* Check resolve_btfids wrapped exactly the arena-flagged return/args with
> +	 * the address_space(1) type attribute, and left other pointers/returns
> +	 * untouched.
> +	 */

[Severity: Low]
This isn't a bug, but does this multi-line comment violate the modern BPF
subsystem comment style? The guidelines require the opening '/*' to be on its
own line, with the text starting on the next line.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601221805.821394-1-ihor.solodrai@linux.dev?part=12

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets
  2026-06-01 22:17 ` [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets Ihor Solodrai
@ 2026-06-01 22:33   ` sashiko-bot
  2026-06-02 18:36     ` Ihor Solodrai
  2026-06-02 20:36   ` Jiri Olsa
  2026-06-03 23:45   ` Andrii Nakryiko
  2 siblings, 1 reply; 38+ messages in thread
From: sashiko-bot @ 2026-06-01 22:33 UTC (permalink / raw)
  To: Ihor Solodrai; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Ambiguous string-based lookup in resolve_btfids incorrectly resolves kfunc BTF IDs on name collisions, causing silent failures in BTF mutation and tag emission.
--

commit 8f8fb9808a6cbcfa547c7cbbeadbb0e70e3b6b81
Author: Ihor Solodrai <ihor.solodrai@linux.dev>

resolve_btfids: Discover kfuncs from BTF ID sets

This commit updates resolve_btfids to discover kfuncs by parsing BTF_ID_SET8
symbols instead of relying on bpf_kfunc decl tags. It extracts the kfunc name
from the symbol address, then looks up the BTF ID by name and records the
associated flags.

> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 43512af131482..d35a7b2460e83 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
[ ... ]
> @@ -1003,92 +1020,63 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
[ ... ]
> +static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
> +{
[ ... ]
> +		for (u32 i = 0; i < set_id->cnt; i++) {
> +			Elf64_Addr addr = set_addr + sizeof(u64) * (i + 1);
> +			const char *name = find_name_by_addr(obj, addr);
> +			struct kfunc kfunc;
> +			s32 func_id;
>  
> -			if (!name || strcmp(name, kfunc_id->name) != 0)
> +			if (!name) {
> +				pr_err("WARN: resolve_btfids: no kfunc symbol for set %s entry %u\n",
> +				       set_id->name, i);
> +				warnings++;
>  				continue;
> +			}
>  
> -			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
> -				 kfunc_id->name, set_id->name);
[ ... ]
> +			func_id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);

[Severity: High]
Does this string-based lookup risk returning the wrong BTF ID when processing
kernel modules?

When resolve_btfids processes a kernel module, btf__find_by_name_kind() searches
the base_btf (vmlinux BTF) before the module's BTF. If a module kfunc shares
a name with a static function in vmlinux, or if there are multiple static
functions with the same name within the module, it seems this might return an
incorrect BTF ID.

If the wrong BTF ID is returned, resolve_btfids could silently mutate the wrong
type in memory later, for example in process_kfunc_with_implicit_args() or
process_kfunc_with_arena_flags(). Since base_btf modifications are not saved to
the module's .BTF section, the actual module kfunc would remain unmodified. Will
this cause the BPF verifier to reject programs using the module kfunc due to
missing implicit args removal or tags?

> +			if (func_id < 0) {
> +				pr_err("WARN: resolve_btfids: no BTF FUNC for kfunc %s in set %s\n",
> +				       name, set_id->name);
> +				warnings++;
> +				continue;
> +			}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601221805.821394-1-ihor.solodrai@linux.dev?part=6

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address
  2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
  2026-06-01 22:28   ` sashiko-bot
@ 2026-06-01 23:03   ` bot+bpf-ci
  2026-06-02 13:01   ` Jiri Olsa
  2026-06-03 23:45   ` Andrii Nakryiko
  3 siblings, 0 replies; 38+ messages in thread
From: bot+bpf-ci @ 2026-06-01 23:03 UTC (permalink / raw)
  To: ihor.solodrai, ast, andrii, daniel, eddyz87, memxor
  Cc: alan.maguire, jolsa, bpf, linux-kbuild, ast, andrii, daniel,
	martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 5205 bytes --]

> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index f8a91fa7584f..43512af13148 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c

[ ... ]

> @@ -480,6 +489,49 @@ static int elf_collect(struct object *obj)
>  	return 0;
>  }
>
> +static int push_addr_sym(struct object *obj, Elf64_Addr addr, const char *name)
> +{
> +	struct addr_sym *arr = obj->addr_syms;
> +	int cap = obj->max_addr_syms;
> +
> +	if (obj->nr_addr_syms + 1 > cap) {
> +		cap = max(cap + 256, cap * 2);
> +		arr = realloc(arr, sizeof(*arr) * cap);
> +		if (!arr)
> +			return -ENOMEM;
> +		obj->max_addr_syms = cap;
> +		obj->addr_syms = arr;
> +	}
> +
> +	obj->addr_syms[obj->nr_addr_syms].addr = addr;
> +	obj->addr_syms[obj->nr_addr_syms].name = name;
> +	obj->nr_addr_syms++;
> +
> +	return 0;
> +}
> +
> +static int cmp_addr_sym(const void *a, const void *b)
> +{
> +	Elf64_Addr aa = ((const struct addr_sym *)a)->addr;
> +	Elf64_Addr ab = ((const struct addr_sym *)b)->addr;
> +
> +	if (aa < ab)
> +		return -1;
> +	if (aa > ab)
> +		return 1;
> +	return 0;
> +}
> +
> +static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
> +{
> +	struct addr_sym key = { .addr = addr };
> +	struct addr_sym *res;
> +
> +	res = bsearch(&key, obj->addr_syms, obj->nr_addr_syms,
> +		      sizeof(*obj->addr_syms), cmp_addr_sym);

If the ELF file lacks BTF ID symbols, obj->addr_syms remains NULL and
obj->nr_addr_syms is 0. Calling bsearch with a NULL base pointer is
Undefined Behavior under the C standard, which can cause issues with UBSan
builds.

> +	return res ? res->name : NULL;
> +}
> +
>  static int symbols_collect(struct object *obj)
>  {
>  	Elf_Scn *scn = NULL;
> @@ -573,8 +625,14 @@ static int symbols_collect(struct object *obj)
>  			return -1;
>  		}
>  		id->addr[id->addr_cnt++] = sym.st_value;
> +
> +		if (push_addr_sym(obj, sym.st_value, id->name))
> +			return -1;
>  	}
>
> +	qsort(obj->addr_syms, obj->nr_addr_syms, sizeof(*obj->addr_syms),
> +	      cmp_addr_sym);

Does this have the same NULL pointer issue as the bsearch call? If the
array is empty and obj->addr_syms is NULL, calling qsort here triggers the
same Undefined Behavior as described above.

[ ... ]

> @@ -946,43 +1004,41 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
>  }
>
>  /*
> - * To find the kfunc flags having its struct btf_id (with ELF addresses)
> - * we need to find the address that is in range of a set8.
> - * If a set8 is found, then the flags are located at addr + 4 bytes.
> + * To find kfunc flags, scan BTF_SET8_KFUNCS entries and use the entry
> + * address to recover the corresponding BTF_ID symbol name.
>   * Return 0 (no flags!) if not found.
>   */
>  static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
>  {
>  	const u32 *elf_data_ptr = obj->efile.idlist->d_buf;
> -	u64 set_lower_addr, set_upper_addr, addr;
>  	struct btf_id *set_id;
>  	struct rb_node *next;
> -	u32 flags;
> -	u64 idx;
> +	u64 idx, set_addr;
> +	u32 set_flags;
>
>  	for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
>  		set_id = rb_entry(next, struct btf_id, rb_node);
>  		if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
>  			continue;
>
> -		set_lower_addr = set_id->addr[0];
> -		set_upper_addr = set_lower_addr + set_id->cnt * sizeof(u64);
> +		set_addr = set_id->addr[0];
> +		idx = (set_addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
> +		set_flags = elf_data_ptr[idx];
> +		if (!(set_flags & BTF_SET8_KFUNCS))
> +			continue;
>
> -		for (u32 i = 0; i < kfunc_id->addr_cnt; i++) {
> -			addr = kfunc_id->addr[i];
> -			/*
> -			 * Lower bound is exclusive to skip the 8-byte header of the set.
> -			 * Upper bound is inclusive to capture the last entry at offset 8*cnt.
> -			 */
> -			if (set_lower_addr < addr && addr <= set_upper_addr) {
> -				pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
> -					 kfunc_id->name, set_id->name);
> -				idx = addr - obj->efile.idlist_addr;
> -				idx = idx / sizeof(u32) + 1;
> -				flags = elf_data_ptr[idx];
> -
> -				return flags;
> -			}
> +		for (u32 i = 0; i < set_id->cnt; i++) {

When parsing SET8 symbols, the count is calculated as 'id->cnt =
sym.st_size / sizeof(uint64_t) - 1'. If sym.st_size is less than 8,
id->cnt (which is a signed int) becomes -1.

Because the loop index 'i' is an unsigned 32-bit integer, the comparison
promotes the signed -1 to 0xFFFFFFFF. Could this cause the tool to hang
spinning through 4 billion iterations if it processes a corrupted or crafted
ELF object?

> +			Elf64_Addr addr = set_addr + sizeof(u64) * (i + 1);
> +			const char *name = find_name_by_addr(obj, addr);
> +
> +			if (!name || strcmp(name, kfunc_id->name) != 0)
> +				continue;
> +
> +			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
> +				 kfunc_id->name, set_id->name);
> +
> +			idx = (addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
> +			return elf_data_ptr[idx];
>  		}
>  	}
>


---
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/26786221491

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address
  2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
  2026-06-01 22:28   ` sashiko-bot
  2026-06-01 23:03   ` bot+bpf-ci
@ 2026-06-02 13:01   ` Jiri Olsa
  2026-06-02 18:28     ` Ihor Solodrai
  2026-06-03 23:45   ` Andrii Nakryiko
  3 siblings, 1 reply; 38+ messages in thread
From: Jiri Olsa @ 2026-06-02 13:01 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On Mon, Jun 01, 2026 at 03:17:56PM -0700, Ihor Solodrai wrote:

SNIP

> +static int push_addr_sym(struct object *obj, Elf64_Addr addr, const char *name)
> +{
> +	struct addr_sym *arr = obj->addr_syms;
> +	int cap = obj->max_addr_syms;
> +
> +	if (obj->nr_addr_syms + 1 > cap) {
> +		cap = max(cap + 256, cap * 2);
> +		arr = realloc(arr, sizeof(*arr) * cap);
> +		if (!arr)
> +			return -ENOMEM;
> +		obj->max_addr_syms = cap;
> +		obj->addr_syms = arr;

could you use libbpf_ensure_mem instead?

> +	}
> +
> +	obj->addr_syms[obj->nr_addr_syms].addr = addr;
> +	obj->addr_syms[obj->nr_addr_syms].name = name;
> +	obj->nr_addr_syms++;
> +
> +	return 0;
> +}
> +
> +static int cmp_addr_sym(const void *a, const void *b)
> +{
> +	Elf64_Addr aa = ((const struct addr_sym *)a)->addr;
> +	Elf64_Addr ab = ((const struct addr_sym *)b)->addr;
> +
> +	if (aa < ab)
> +		return -1;
> +	if (aa > ab)
> +		return 1;
> +	return 0;

nit, I recently spot following pattern:

        return (aa > ab) - (aa < ab);

jirka

> +}
> +
> +static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
> +{
> +	struct addr_sym key = { .addr = addr };
> +	struct addr_sym *res;
> +
> +	res = bsearch(&key, obj->addr_syms, obj->nr_addr_syms,
> +		      sizeof(*obj->addr_syms), cmp_addr_sym);
> +	return res ? res->name : NULL;
> +}

SNIP

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding
  2026-06-01 22:17 ` [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding Ihor Solodrai
@ 2026-06-02 13:02   ` Jiri Olsa
  2026-06-02 18:30     ` Ihor Solodrai
  0 siblings, 1 reply; 38+ messages in thread
From: Jiri Olsa @ 2026-06-02 13:02 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On Mon, Jun 01, 2026 at 03:17:53PM -0700, Ihor Solodrai wrote:

SNIP

> -static int resolve_symbols(void)
> +static int resolve_symbols(struct btf *btf)
>  {
> -	struct btf *btf;
> +	__u32 nr = btf__type_cnt(btf);
>  	int type_id;
> -	__u32 nr;
> -
> -	btf = btf__parse_raw("resolve_btfids.test.o.BTF");
> -	if (CHECK(libbpf_get_error(btf), "resolve",
> -		  "Failed to load BTF from resolve_btfids.test.o.BTF\n"))
> -		return -1;
> -
> -	nr = btf__type_cnt(btf);
>  
>  	for (type_id = 1; type_id < nr; type_id++) {
>  		if (__resolve_symbol(btf, type_id))

I think __resolve_symbol uses PRINT_FAIL instead of ASSERT

> -			break;
> +			return -1;
>  	}
> -
> -	btf__free(btf);
>  	return 0;
>  }
>  
> @@ -121,25 +109,22 @@ void test_resolve_btfids(void)
>  {
>  	__u32 *test_list, *test_lists[] = { test_list_local, test_list_global };
>  	unsigned int i, j;
> -	int ret = 0;
> +	struct btf *btf;
>  
> -	if (resolve_symbols())
> -		return;
> +	btf = btf__parse_raw("resolve_btfids.test.o.BTF");
> +	if (!ASSERT_OK_PTR(btf, "btf_parse"))
> +		goto out;

nit, just return?

jirka

> +
> +	if (resolve_symbols(btf))
> +		goto out;
>  

SNIP

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids
  2026-06-01 22:17 ` [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids Ihor Solodrai
@ 2026-06-02 13:02   ` Jiri Olsa
  2026-06-03 23:45   ` Andrii Nakryiko
  1 sibling, 0 replies; 38+ messages in thread
From: Jiri Olsa @ 2026-06-02 13:02 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On Mon, Jun 01, 2026 at 03:17:55PM -0700, Ihor Solodrai wrote:

SNIP

>  static int
>  __resolve_symbol(struct btf *btf, int type_id)
>  {
> @@ -108,6 +129,18 @@ __resolve_symbol(struct btf *btf, int type_id)
>  			test_symbols[i].id = type_id;
>  	}
>  
> +	if (BTF_INFO_KIND(type->info) == BTF_KIND_FUNC) {
> +		str = btf__name_by_offset(btf, type->name_off);
> +		if (str) {
> +			for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++) {
> +				if (kfunc_symbols[i].id >= 0)
> +					continue;
> +				if (!strcmp(str, kfunc_symbols[i].name))
> +					kfunc_symbols[i].id = type_id;
> +			}
> +		}
> +	}
> +
>  	return 0;
>  }
>  
> @@ -162,6 +195,29 @@ void test_resolve_btfids(void)
>  			ASSERT_LE(test_set.ids[i - 1], test_set.ids[i], "sort_check");
>  	}
>  
> +	/* Check BTF_KFUNCS_START(test_kfunc_set) */
> +	ASSERT_EQ(test_kfunc_set.flags, BTF_SET8_KFUNCS, "kfunc_set_flags");
> +	ASSERT_EQ(test_kfunc_set.cnt, ARRAY_SIZE(kfunc_symbols), "kfunc_set_cnt");
> +
> +	for (i = 0; i < test_kfunc_set.cnt; i++) {
> +		bool found = false;
> +
> +		for (j = 0; j < ARRAY_SIZE(kfunc_symbols); j++) {
> +			if (kfunc_symbols[j].id != (s32)test_kfunc_set.pairs[i].id)
> +				continue;
> +			found = true;
> +			ASSERT_EQ(test_kfunc_set.pairs[i].flags,
> +				  kfunc_symbols[j].flags, "kfunc_flags_check");
> +			break;

nit, we could reverse the logic 
			if (match) {
				....
				break
			}

then found is equal to "i < ARRAY_SIZE(kfunc_symbols)"

> +		}
> +
> +		ASSERT_TRUE(found, "kfunc_id_found");
> +
> +		if (i > 0)
> +			ASSERT_LE(test_kfunc_set.pairs[i - 1].id,
> +				  test_kfunc_set.pairs[i].id, "kfunc_sort_check");

nit, should have { .. } braces 

jirka

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address
  2026-06-02 13:01   ` Jiri Olsa
@ 2026-06-02 18:28     ` Ihor Solodrai
  0 siblings, 0 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-02 18:28 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On 6/2/26 6:01 AM, Jiri Olsa wrote:
> On Mon, Jun 01, 2026 at 03:17:56PM -0700, Ihor Solodrai wrote:
> 
> SNIP
> 
>> +static int push_addr_sym(struct object *obj, Elf64_Addr addr, const char *name)
>> +{
>> +	struct addr_sym *arr = obj->addr_syms;
>> +	int cap = obj->max_addr_syms;
>> +
>> +	if (obj->nr_addr_syms + 1 > cap) {
>> +		cap = max(cap + 256, cap * 2);
>> +		arr = realloc(arr, sizeof(*arr) * cap);
>> +		if (!arr)
>> +			return -ENOMEM;
>> +		obj->max_addr_syms = cap;
>> +		obj->addr_syms = arr;
> 
> could you use libbpf_ensure_mem instead?

Hi Jiri. Thank you for prompt review.

Yeah.. a third copy-pasted push_* function is one too many.
I'll try adopting libbpf helpers in the next revision.

> 
>> +	}
>> +
>> +	obj->addr_syms[obj->nr_addr_syms].addr = addr;
>> +	obj->addr_syms[obj->nr_addr_syms].name = name;
>> +	obj->nr_addr_syms++;
>> +
>> +	return 0;
>> +}
>> +
>> +static int cmp_addr_sym(const void *a, const void *b)
>> +{
>> +	Elf64_Addr aa = ((const struct addr_sym *)a)->addr;
>> +	Elf64_Addr ab = ((const struct addr_sym *)b)->addr;
>> +
>> +	if (aa < ab)
>> +		return -1;
>> +	if (aa > ab)
>> +		return 1;
>> +	return 0;
> 
> nit, I recently spot following pattern:
> 
>         return (aa > ab) - (aa < ab);
> 
> jirka
> 
>> +}
>> +
>> +static const char *find_name_by_addr(struct object *obj, Elf64_Addr addr)
>> +{
>> +	struct addr_sym key = { .addr = addr };
>> +	struct addr_sym *res;
>> +
>> +	res = bsearch(&key, obj->addr_syms, obj->nr_addr_syms,
>> +		      sizeof(*obj->addr_syms), cmp_addr_sym);
>> +	return res ? res->name : NULL;
>> +}
> 
> SNIP


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding
  2026-06-02 13:02   ` Jiri Olsa
@ 2026-06-02 18:30     ` Ihor Solodrai
  0 siblings, 0 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-02 18:30 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On 6/2/26 6:02 AM, Jiri Olsa wrote:
> On Mon, Jun 01, 2026 at 03:17:53PM -0700, Ihor Solodrai wrote:
> 
> SNIP
> 
>> -static int resolve_symbols(void)
>> +static int resolve_symbols(struct btf *btf)
>>  {
>> -	struct btf *btf;
>> +	__u32 nr = btf__type_cnt(btf);
>>  	int type_id;
>> -	__u32 nr;
>> -
>> -	btf = btf__parse_raw("resolve_btfids.test.o.BTF");
>> -	if (CHECK(libbpf_get_error(btf), "resolve",
>> -		  "Failed to load BTF from resolve_btfids.test.o.BTF\n"))
>> -		return -1;
>> -
>> -	nr = btf__type_cnt(btf);
>>  
>>  	for (type_id = 1; type_id < nr; type_id++) {
>>  		if (__resolve_symbol(btf, type_id))
> 
> I think __resolve_symbol uses PRINT_FAIL instead of ASSERT

You're right, I missed that. Thanks.

> 
>> [...]


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets
  2026-06-01 22:33   ` sashiko-bot
@ 2026-06-02 18:36     ` Ihor Solodrai
  0 siblings, 0 replies; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-02 18:36 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: bpf

On 6/1/26 3:33 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Ambiguous string-based lookup in resolve_btfids incorrectly resolves kfunc BTF IDs on name collisions, causing silent failures in BTF mutation and tag emission.
> --
> 
> commit 8f8fb9808a6cbcfa547c7cbbeadbb0e70e3b6b81
> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
> 
> resolve_btfids: Discover kfuncs from BTF ID sets
> 
> This commit updates resolve_btfids to discover kfuncs by parsing BTF_ID_SET8
> symbols instead of relying on bpf_kfunc decl tags. It extracts the kfunc name
> from the symbol address, then looks up the BTF ID by name and records the
> associated flags.
> 
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index 43512af131482..d35a7b2460e83 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
> [ ... ]
>> @@ -1003,92 +1020,63 @@ static int collect_decl_tags(struct btf2btf_context *ctx)
> [ ... ]
>> +static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
>> +{
> [ ... ]
>> +		for (u32 i = 0; i < set_id->cnt; i++) {
>> +			Elf64_Addr addr = set_addr + sizeof(u64) * (i + 1);
>> +			const char *name = find_name_by_addr(obj, addr);
>> +			struct kfunc kfunc;
>> +			s32 func_id;
>>  
>> -			if (!name || strcmp(name, kfunc_id->name) != 0)
>> +			if (!name) {
>> +				pr_err("WARN: resolve_btfids: no kfunc symbol for set %s entry %u\n",
>> +				       set_id->name, i);
>> +				warnings++;
>>  				continue;
>> +			}
>>  
>> -			pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
>> -				 kfunc_id->name, set_id->name);
> [ ... ]
>> +			func_id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
> 
> [Severity: High]
> Does this string-based lookup risk returning the wrong BTF ID when processing
> kernel modules?
> 
> When resolve_btfids processes a kernel module, btf__find_by_name_kind() searches
> the base_btf (vmlinux BTF) before the module's BTF. If a module kfunc shares
> a name with a static function in vmlinux, or if there are multiple static
> functions with the same name within the module, it seems this might return an
> incorrect BTF ID.

I am not convinced this is an issue, because a module kfunc shadowing a kernel
kfunc by name seems to me like a bad idea at first glance.

Still, AI might be onto something here. I'll experiment with bpf test module in
the selftests to investigate.

> 
> If the wrong BTF ID is returned, resolve_btfids could silently mutate the wrong
> type in memory later, for example in process_kfunc_with_implicit_args() or
> process_kfunc_with_arena_flags(). Since base_btf modifications are not saved to
> the module's .BTF section, the actual module kfunc would remain unmodified. Will
> this cause the BPF verifier to reject programs using the module kfunc due to
> missing implicit args removal or tags?
> 
>> +			if (func_id < 0) {
>> +				pr_err("WARN: resolve_btfids: no BTF FUNC for kfunc %s in set %s\n",
>> +				       name, set_id->name);
>> +				warnings++;
>> +				continue;
>> +			}
> 


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets
  2026-06-01 22:17 ` [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets Ihor Solodrai
  2026-06-01 22:33   ` sashiko-bot
@ 2026-06-02 20:36   ` Jiri Olsa
  2026-06-02 21:08     ` Ihor Solodrai
  2026-06-03 23:45   ` Andrii Nakryiko
  2 siblings, 1 reply; 38+ messages in thread
From: Jiri Olsa @ 2026-06-02 20:36 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On Mon, Jun 01, 2026 at 03:17:57PM -0700, Ihor Solodrai wrote:
> collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
> list of kfuncs. The decl tags are generated by pahole, which makes
> current implementation implicitly rely on those tags being generated.
> 
> The authoritative source, used by the the BPF verifier for kfunc
> registration, of functions being BPF kfuncs are
> BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
> hood. Currently resolve_btfids reads kfunc flags from these sets, and
> populates them with BTF IDs.
> 
> Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
> removing the dependency on pahole's emmission of decl tags.
> 
> Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
> look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
> recording the paired flags directly. This makes find_kfunc_flags()
> helper unnecessary, so it's removed.
> 
> Kernel functions can appear in more than one set, which is legitimate,
> since kfunc sets are prog-type dependent in the kernel. So for btf2btf
> processing deduplicate kfuncs by BTF ID, accumulate (OR) the flags,
> and warn on flags mismatch to catch inconsistent declarations.
> 
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/bpf/resolve_btfids/main.c | 122 ++++++++++++++------------------
>  1 file changed, 55 insertions(+), 67 deletions(-)
> 
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 43512af13148..d35a7b2460e8 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -970,6 +970,23 @@ static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
>  	struct kfunc *arr = ctx->kfuncs;
>  	u32 cap = ctx->max_kfuncs;
>  
> +	/*
> +	 * A kfunc can be listed in multiple BTF ID sets.
> +	 * In this case, dedup by btf_id and accumulate kfunc flags.
> +	 */
> +	for (u32 i = 0; i < ctx->nr_kfuncs; i++) {
> +		if (ctx->kfuncs[i].btf_id != kfunc->btf_id)
> +			continue;
> +
> +		if (ctx->kfuncs[i].flags != kfunc->flags) {
> +			pr_err("WARN: resolve_btfids: inconsistent flags for kfunc %s: 0x%x != 0x%x\n",
> +			       kfunc->name, ctx->kfuncs[i].flags, kfunc->flags);
> +			warnings++;
> +		}

hit few of those

WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_allocate_context: 0x5 != 0x25
WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_release_context: 0x2 != 0x22
WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_hw_request: 0x0 != 0x20
WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_hw_output_report: 0x0 != 0x20
WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_input_report: 0x0 != 0x20

I'd think flags like KF_SLEEPABLE might vary in different sets for the same kfunc,
IIUC you don't need to use KF_SLEEPABLE for syscall hook, because syscall programs
are already sleepable.. not sure there are other examples

jirka

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets
  2026-06-02 20:36   ` Jiri Olsa
@ 2026-06-02 21:08     ` Ihor Solodrai
  2026-06-03 23:45       ` Andrii Nakryiko
  0 siblings, 1 reply; 38+ messages in thread
From: Ihor Solodrai @ 2026-06-02 21:08 UTC (permalink / raw)
  To: Jiri Olsa
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On 6/2/26 1:36 PM, Jiri Olsa wrote:
> On Mon, Jun 01, 2026 at 03:17:57PM -0700, Ihor Solodrai wrote:
>> collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
>> list of kfuncs. The decl tags are generated by pahole, which makes
>> current implementation implicitly rely on those tags being generated.
>>
>> The authoritative source, used by the the BPF verifier for kfunc
>> registration, of functions being BPF kfuncs are
>> BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
>> hood. Currently resolve_btfids reads kfunc flags from these sets, and
>> populates them with BTF IDs.
>>
>> Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
>> removing the dependency on pahole's emmission of decl tags.
>>
>> Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
>> look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
>> recording the paired flags directly. This makes find_kfunc_flags()
>> helper unnecessary, so it's removed.
>>
>> Kernel functions can appear in more than one set, which is legitimate,
>> since kfunc sets are prog-type dependent in the kernel. So for btf2btf
>> processing deduplicate kfuncs by BTF ID, accumulate (OR) the flags,
>> and warn on flags mismatch to catch inconsistent declarations.
>>
>> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
>> ---
>>  tools/bpf/resolve_btfids/main.c | 122 ++++++++++++++------------------
>>  1 file changed, 55 insertions(+), 67 deletions(-)
>>
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index 43512af13148..d35a7b2460e8 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
>> @@ -970,6 +970,23 @@ static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
>>  	struct kfunc *arr = ctx->kfuncs;
>>  	u32 cap = ctx->max_kfuncs;
>>  
>> +	/*
>> +	 * A kfunc can be listed in multiple BTF ID sets.
>> +	 * In this case, dedup by btf_id and accumulate kfunc flags.
>> +	 */
>> +	for (u32 i = 0; i < ctx->nr_kfuncs; i++) {
>> +		if (ctx->kfuncs[i].btf_id != kfunc->btf_id)
>> +			continue;
>> +
>> +		if (ctx->kfuncs[i].flags != kfunc->flags) {
>> +			pr_err("WARN: resolve_btfids: inconsistent flags for kfunc %s: 0x%x != 0x%x\n",
>> +			       kfunc->name, ctx->kfuncs[i].flags, kfunc->flags);
>> +			warnings++;
>> +		}
> 
> hit few of those
> 
> WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_allocate_context: 0x5 != 0x25
> WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_release_context: 0x2 != 0x22
> WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_hw_request: 0x0 != 0x20
> WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_hw_output_report: 0x0 != 0x20
> WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_input_report: 0x0 != 0x20
> 
> I'd think flags like KF_SLEEPABLE might vary in different sets for the same kfunc,
> IIUC you don't need to use KF_SLEEPABLE for syscall hook, because syscall programs
> are already sleepable.. not sure there are other examples

That's a good point.

I think from the PoV of resolve_btfids what matters is the mismatch of
flags that trigger BTF changes, like KF_IMPLICIT_ARGS. We could check for
the specific flags here, or filter out acceptable inconsistencies (KF_SLEEPABLE).

Any other ideas?

> 
> jirka


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs
  2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
                   ` (13 preceding siblings ...)
  2026-06-01 22:18 ` [PATCH bpf-next v1 14/14] docs, resolve_btfids: Document kfunc BTF annotation emission Ihor Solodrai
@ 2026-06-03 23:45 ` Andrii Nakryiko
  14 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:45 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:18 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> BTF data for the kernel is generated through the following pipeline:
>   * DWARF is emitted by the compiler
>   * pahole reads in DWARF and produces BTF
>   * resolve_btfids makes kernel-specific btf2btf transformation and
>     patches .BTF_ids section
>
> This is orchestrated by link-vmlinux.sh, gen-btf.sh and Makefile.btf
> in ./scripts directory.
>
> Historically kernel-specific BTF features were implemented in pahole,
> and controlled by the feature flags. This requires kernel build
> process to be aware of pahole version used for the build to set
> correct runtime arguments for BTF encoding [1].
>
> This is a burden which can be alleviated by splitting kernel/module
> BTF generation in two stages:
>   1. Generic BTF generation from the kernel source code.
>   2. Kernel-specific BTF modifications to support various BPF features.
>
> So far both stages were fused in pahole's BTF encoding. By moving
> stage (2) in-tree, the dependency of kernel build on pahole can become
> much more loose.
>
> Recent work [2] made it possible to do btf2btf transformations in
> resolve_btfids, and it is already responsible for a few important BTF
> modifications for the kernel:
>   * .BTF.base generation for modules [3]
>   * BTF sorting [4]
>   * KF_IMPLICIT_ARGS support [5]
>
> This series continues the migration of kernel-specific BTF
> transformations from pahole to resolve_btfids, implementing emission
> of decl/type tags for the kfuncs and handling of the KF_ARENA_* flags.
>
> The implementation is idempotent with respect to BTF modifications: if

This sounds like an unnecessary complication. Can't we just control
pahole through flags to guarantee there will be no kernel-specific
information emitted?


> input BTF already contains target tags, corresponding resolve_btfids
> transformation is a noop. This allows for more flexibility in terms of
> dependency on pahole. In particular, older pahole (without
> decl_tag_kfuncs support, for example) can now be used in kbuild and
> the resulting vmlinux BTF will contain properly tagged kfuncs
> anyways. Conversely, if for whatever reason pahole emitted the same
> tags, they will be properly skipped.
>
> The series consists of the following patches:
>
>   * patches #1-#4 refactor and fixup resolve_btfids selftest, adding
>     kfunc checks
>     * patch #3 is a fascinating rabbit hole with a two-line test fix
>   * patch #5 refactors resolve_btfids introducing an address to
>     elf-symbol index for efficient lookups
>   * patch #6 changes how resolve_btfids discovers kfuncs: instead of
>     relying on decl_tags emitted by pahole, identify kfuncs directly
>     from BTF ID sets
>   * patch #7 implements "bpf_kfunc" decl_tag emission, with the next
>     patch testing it
>   * patch #9 adds "bpf_fastcall" handling, with the next patch testing it
>   * patch #11 implements KF_ARENA_* flags handling in resolve_btfids,
>     with the next patch testing that
>   * finally, patch #13 drops now unnecessary pahole runtime flags from
>     the kbuild, and patch #14 adds a bit of documentation
>
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/scripts/Makefile.btf?h=v7.1-rc5
> [2] https://lore.kernel.org/bpf/20251219181321.1283664-1-ihor.solodrai@linux.dev/
> [3] https://docs.kernel.org/bpf/btf.html#btf-base-section
> [4] https://lore.kernel.org/bpf/20260109130003.3313716-4-dolinux.peng@gmail.com/
> [5] https://lore.kernel.org/bpf/20260120222638.3976562-1-ihor.solodrai@linux.dev/
>
> ---
>
> Ihor Solodrai (14):
>   tools/bpf: Sync btf_ids.h to tools
>   selftests/bpf: Modernize resolve_btfids test scaffolding
>   selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE
>     builds
>   selftests/bpf: Add kfunc set test to resolve_btfids
>   resolve_btfids: Index BTF ID symbols by address
>   resolve_btfids: Discover kfuncs from BTF ID sets
>   resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs
>   selftests/bpf: Verify bpf_kfunc decl tag emission in resolve_btfids
>   resolve_btfids: Emit a decl tag for kfuncs with KF_FASTCALL
>   selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test
>   resolve_btfids: Process KF_ARENA_* flags in resolve_btfids
>   selftests/bpf: Verify arena type tags in resolve_btfids test
>   kbuild: Drop decl_tag_kfuncs and attributes from pahole flags
>   docs, resolve_btfids: Document kfunc BTF annotation emission
>
>  Documentation/bpf/kfuncs.rst                  |   8 +
>  scripts/Makefile.btf                          |   7 +-
>  tools/bpf/resolve_btfids/main.c               | 391 ++++++++++++++----
>  tools/include/linux/btf_ids.h                 |  78 +++-
>  .../selftests/bpf/prog_tests/resolve_btfids.c | 218 ++++++++--
>  tools/testing/selftests/bpf/progs/btf_data.c  |  20 +
>  6 files changed, 602 insertions(+), 120 deletions(-)
>
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds
  2026-06-01 22:17 ` [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds Ihor Solodrai
@ 2026-06-03 23:45   ` Andrii Nakryiko
  0 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:45 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:18 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> TL;DR
>
> On aarch64 with gcc toolchain, when test_progs is linked as a PIE,
> reads of BTF ID array by C name return garbage because the GNU
> assembler on aarch64 unconditionally folds .local symbol references
> into section+addend form, and GOT slots cannot carry an addend per the
> AArch64 ELF spec.
>
> Fix by marking the test's BTF ID objects with hidden visibility, which
> makes gcc emit a direct access that bypasses the GOT entirely.
>
> Details below.
>
> The subsequent patches adding kfunc checks to resolve_btfids test may
> cause test failures on aarch64 / gcc-15:
>
>   test_resolve_btfids:FAIL:kfunc_set_flags  actual 13 != expected 1
>   test_resolve_btfids:FAIL:kfunc_set_cnt    actual 0  != expected 4
>
> The test defines its BTF ID sets with the same macros as the kernel
> and reads them back directly by C name (in the same way as the kernel
> code does).
>
> test_kfunc_set is a .local symbol emitted into .BTF_ids by inline asm
> and declared to the compiler as a plain default-visibility
> extern, that is:
>     extern struct btf_id_set8 test_kfunc_set;
>
> Depending on the build environment, test_progs may be linked as a
> position-independent executable (for example, gcc defaults to -fpie
> [1]). In a PIE, taking the address of a default-visibility extern is
> routed through the GOT (Global Offset Table) [2].
>
> The GNU assembler's adjust_reloc_syms() pass (gas/write.c [3])
> replaces references to local symbols with the corresponding section
> symbol, folding the symbol's offset into the relocation addend.  On
> aarch64 this conversion is unconditional: tc_fix_adjustable() is
> defined to 1 for all fixups (gas/config/tc-aarch64.h [4]), so even
> GOT-generating relocations are subject to it.  The resulting object
> file therefore contains:
>
>   R_AARCH64_ADR_GOT_PAGE      .BTF_ids + 0x54
>   R_AARCH64_LD64_GOT_LO12_NC  .BTF_ids + 0x54
>
> However, the AArch64 ELF specification mandates that GOT-generating
> relocations must have a zero addend [5].  The +0x54 is therefore not
> honored: the linker creates a GOT slot pointing at the .BTF_ids base,
> and every access through that slot reads offset 0 instead of 0x54.
>
> This is purely a read-side problem, specific to the PIE test binary on
> aarch64 with gcc toolchain. resolve_btfids patches the set header
> correctly and the .BTF_ids bytes in test_progs are correct. vmlinux is
> unaffected because it is built with -fno-PIE [6] and reaches .BTF_ids
> with direct, addend-preserving relocations rather than the GOT. clang
> is unaffected because LLVM's assembler retains the original symbol for
> GOT relocations instead of converting to section+addend [7].
>
> To mitigate this issue, mark the test's BTF ID objects hidden with a
> visibility pragma so that gcc treats them as non-interposable and
> emits a direct access instead of a GOT load. This keeps the natural
> by-name access, works in both PIE and non-PIE builds, and needs no
> change to the BTF_ID macros or resolve_btfids.
>
> [1] https://gcc.gnu.org/onlinedocs/gnat_ugn/Position-Independent-Executable-PIE-Enabled-by-Default-on-Linux.html
> [2] https://gcc.gnu.org/wiki/Visibility
> [3] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/write.c#l922
> [4] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/config/tc-aarch64.h#l279
> [5] https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#5733relocation-operations
> [6] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Makefile?h=v7.1-rc6#n593
> [7] https://github.com/llvm/llvm-project/blob/4b3bc46d1d794b8ed78b75ccd35a6cc30235bf31/llvm/lib/MC/ELFObjectWriter.cpp#L1213-L1224
>

fascinating! but you also missed adding your SOB

Acked-by: Andrii Nakryiko <andrii@kernel.org>



> ---
> ---
>  .../selftests/bpf/prog_tests/resolve_btfids.c  | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> index d742ecb2ca8e..d0e51979d455 100644
> --- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> +++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> @@ -32,6 +32,23 @@ asm (
>  ".balign 4, 0;                            \n"
>  ".popsection;                             \n");
>
> +/*
> + * The BTF ID arrays below are .local symbols placed in .BTF_ids by
> + * inline asm, and are read here directly by C name. To the compiler
> + * they are plain, default-visibility extern objects.
> + *
> + * When test_progs is linked as a position-independent executable (PIE),
> + * taking the address of such an extern is routed through the GOT. The
> + * GNU assembler on aarch64 unconditionally converts references to
> + * .local symbols into section + addend form (".BTF_ids + <offset>"),
> + * but a GOT slot cannot carry an addend (the AArch64 ELF spec mandates
> + * zero), so the linker resolves it to the .BTF_ids base.
> + *
> + * Mark these symbols hidden so the compiler treats them as
> + * non-interposable and emits a direct, addend-preserving PC-relative
> + * access instead of a GOT load, in both PIE and non-PIE builds.
> + */
> +#pragma GCC visibility push(hidden)
>  BTF_ID_LIST(test_list_local)
>  BTF_ID_UNUSED
>  BTF_ID(typedef, S)
> @@ -60,6 +77,7 @@ BTF_ID(union,   U)
>  BTF_ID(func,    func)
>  BTF_SET_END(test_set)
>
> +#pragma GCC visibility pop

nit: empty line after pragma, no empty line before pragma so it "hugs"
relevant pieces of code instead?


but also, can't we just use BTF_ID_LIST_GLOBAL instead to avoid all this?


>  static int
>  __resolve_symbol(struct btf *btf, int type_id)
>  {
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address
  2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
                     ` (2 preceding siblings ...)
  2026-06-02 13:01   ` Jiri Olsa
@ 2026-06-03 23:45   ` Andrii Nakryiko
  3 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:45 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:19 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Keep an address-sorted index of parsed .BTF_ids symbols so code that
> the original BTF_ID symbol name can be recovered from an entry
> address.
>
> Use the index in find_kfunc_flags() to scan BTF_SET8_KFUNCS entries
> directly and match each entry back to the requested kfunc.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/bpf/resolve_btfids/main.c | 103 +++++++++++++++++++++++++-------
>  1 file changed, 80 insertions(+), 23 deletions(-)
>
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index f8a91fa7584f..43512af13148 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -119,6 +119,11 @@ struct btf_id {
>         Elf64_Addr       addr[ADDR_CNT];
>  };
>
> +struct addr_sym {
> +       Elf64_Addr       addr;
> +       const char      *name;
> +};
> +
>  struct object {
>         const char *path;
>         const char *btf_path;
> @@ -150,6 +155,10 @@ struct object {
>         int nr_structs;
>         int nr_unions;
>         int nr_typedefs;
> +
> +       struct addr_sym *addr_syms;
> +       int nr_addr_syms;
> +       int max_addr_syms;

nit: max seems misnamed, it's "capacity", so I'd choose
"addr_syms_cnt" and "addr_syms_cap" naming (I believe libbpf does that
relatively consistently)

>  };
>
>  #define KF_IMPLICIT_ARGS (1 << 16)

[...]

>         for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
>                 set_id = rb_entry(next, struct btf_id, rb_node);
>                 if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
>                         continue;
>
> -               set_lower_addr = set_id->addr[0];
> -               set_upper_addr = set_lower_addr + set_id->cnt * sizeof(u64);
> +               set_addr = set_id->addr[0];
> +               idx = (set_addr - obj->efile.idlist_addr) / sizeof(u32) + 1;

where is this +1 coming from? we have some reserved zero entry in
.BTF_ids section? I'd understand if this was symbols table, where we
do have zero entry, but I'm not quite following here...



> +               set_flags = elf_data_ptr[idx];
> +               if (!(set_flags & BTF_SET8_KFUNCS))
> +                       continue;
>
> -               for (u32 i = 0; i < kfunc_id->addr_cnt; i++) {
> -                       addr = kfunc_id->addr[i];
> -                       /*
> -                        * Lower bound is exclusive to skip the 8-byte header of the set.
> -                        * Upper bound is inclusive to capture the last entry at offset 8*cnt.
> -                        */
> -                       if (set_lower_addr < addr && addr <= set_upper_addr) {
> -                               pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
> -                                        kfunc_id->name, set_id->name);
> -                               idx = addr - obj->efile.idlist_addr;
> -                               idx = idx / sizeof(u32) + 1;
> -                               flags = elf_data_ptr[idx];
> -
> -                               return flags;
> -                       }
> +               for (u32 i = 0; i < set_id->cnt; i++) {
> +                       Elf64_Addr addr = set_addr + sizeof(u64) * (i + 1);
> +                       const char *name = find_name_by_addr(obj, addr);
> +
> +                       if (!name || strcmp(name, kfunc_id->name) != 0)
> +                               continue;
> +
> +                       pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
> +                                kfunc_id->name, set_id->name);
> +
> +                       idx = (addr - obj->efile.idlist_addr) / sizeof(u32) + 1;
> +                       return elf_data_ptr[idx];
>                 }
>         }
>
> @@ -1575,6 +1631,7 @@ int main(int argc, const char **argv)
>         btf_id__free_all(&obj.typedefs);
>         btf_id__free_all(&obj.funcs);
>         btf_id__free_all(&obj.sets);
> +       free(obj.addr_syms);
>         if (obj.efile.elf) {
>                 elf_end(obj.efile.elf);
>                 close(obj.efile.fd);
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets
  2026-06-02 21:08     ` Ihor Solodrai
@ 2026-06-03 23:45       ` Andrii Nakryiko
  0 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:45 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Jiri Olsa, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire, bpf,
	linux-kbuild

On Tue, Jun 2, 2026 at 2:09 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 6/2/26 1:36 PM, Jiri Olsa wrote:
> > On Mon, Jun 01, 2026 at 03:17:57PM -0700, Ihor Solodrai wrote:
> >> collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
> >> list of kfuncs. The decl tags are generated by pahole, which makes
> >> current implementation implicitly rely on those tags being generated.
> >>
> >> The authoritative source, used by the the BPF verifier for kfunc
> >> registration, of functions being BPF kfuncs are
> >> BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
> >> hood. Currently resolve_btfids reads kfunc flags from these sets, and
> >> populates them with BTF IDs.
> >>
> >> Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
> >> removing the dependency on pahole's emmission of decl tags.
> >>
> >> Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
> >> look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
> >> recording the paired flags directly. This makes find_kfunc_flags()
> >> helper unnecessary, so it's removed.
> >>
> >> Kernel functions can appear in more than one set, which is legitimate,
> >> since kfunc sets are prog-type dependent in the kernel. So for btf2btf
> >> processing deduplicate kfuncs by BTF ID, accumulate (OR) the flags,
> >> and warn on flags mismatch to catch inconsistent declarations.
> >>
> >> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> >> ---
> >>  tools/bpf/resolve_btfids/main.c | 122 ++++++++++++++------------------
> >>  1 file changed, 55 insertions(+), 67 deletions(-)
> >>
> >> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> >> index 43512af13148..d35a7b2460e8 100644
> >> --- a/tools/bpf/resolve_btfids/main.c
> >> +++ b/tools/bpf/resolve_btfids/main.c
> >> @@ -970,6 +970,23 @@ static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
> >>      struct kfunc *arr = ctx->kfuncs;
> >>      u32 cap = ctx->max_kfuncs;
> >>
> >> +    /*
> >> +     * A kfunc can be listed in multiple BTF ID sets.
> >> +     * In this case, dedup by btf_id and accumulate kfunc flags.
> >> +     */
> >> +    for (u32 i = 0; i < ctx->nr_kfuncs; i++) {
> >> +            if (ctx->kfuncs[i].btf_id != kfunc->btf_id)
> >> +                    continue;
> >> +
> >> +            if (ctx->kfuncs[i].flags != kfunc->flags) {
> >> +                    pr_err("WARN: resolve_btfids: inconsistent flags for kfunc %s: 0x%x != 0x%x\n",
> >> +                           kfunc->name, ctx->kfuncs[i].flags, kfunc->flags);
> >> +                    warnings++;
> >> +            }
> >
> > hit few of those
> >
> > WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_allocate_context: 0x5 != 0x25
> > WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_release_context: 0x2 != 0x22
> > WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_hw_request: 0x0 != 0x20
> > WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_hw_output_report: 0x0 != 0x20
> > WARN: resolve_btfids: inconsistent flags for kfunc hid_bpf_input_report: 0x0 != 0x20
> >
> > I'd think flags like KF_SLEEPABLE might vary in different sets for the same kfunc,
> > IIUC you don't need to use KF_SLEEPABLE for syscall hook, because syscall programs
> > are already sleepable.. not sure there are other examples
>
> That's a good point.
>
> I think from the PoV of resolve_btfids what matters is the mismatch of
> flags that trigger BTF changes, like KF_IMPLICIT_ARGS. We could check for
> the specific flags here, or filter out acceptable inconsistencies (KF_SLEEPABLE).
>
> Any other ideas?

I'd only collect and check inconsistency of flags we care about (and
those should be consistent) and ignore the rest.

>
> >
> > jirka
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids
  2026-06-01 22:17 ` [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids Ihor Solodrai
  2026-06-02 13:02   ` Jiri Olsa
@ 2026-06-03 23:45   ` Andrii Nakryiko
  1 sibling, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:45 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:18 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Extend the resolve_btfids selftest to cover kfunc sets defined with
> BTF_KFUNCS_START/BTF_KFUNCS_END.
>
> The test verifies that resolve_btfids correctly processes BTF_ID_FLAGS,
> resolves function IDs, and checks the kfunc set is sorted.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  .../selftests/bpf/prog_tests/resolve_btfids.c | 56 +++++++++++++++++++
>  tools/testing/selftests/bpf/progs/btf_data.c  | 10 ++++
>  2 files changed, 66 insertions(+)
>

[...]

>  static int
>  __resolve_symbol(struct btf *btf, int type_id)
>  {
> @@ -108,6 +129,18 @@ __resolve_symbol(struct btf *btf, int type_id)
>                         test_symbols[i].id = type_id;
>         }
>
> +       if (BTF_INFO_KIND(type->info) == BTF_KIND_FUNC) {

we have btf_is_func() for this in libbpf's btf.h

> +               str = btf__name_by_offset(btf, type->name_off);
> +               if (str) {

can't be NULL in non-corrupted BTF (it will be at least ""), and if
BTF is corrupted then let's crash

> +                       for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++) {
> +                               if (kfunc_symbols[i].id >= 0)
> +                                       continue;
> +                               if (!strcmp(str, kfunc_symbols[i].name))
> +                                       kfunc_symbols[i].id = type_id;
> +                       }
> +               }
> +       }
> +
>         return 0;
>  }
>

[...]

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets
  2026-06-01 22:17 ` [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets Ihor Solodrai
  2026-06-01 22:33   ` sashiko-bot
  2026-06-02 20:36   ` Jiri Olsa
@ 2026-06-03 23:45   ` Andrii Nakryiko
  2 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:45 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:19 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> collect_kfuncs() currently uses bpf_kfunc decl tags to identify the
> list of kfuncs. The decl tags are generated by pahole, which makes
> current implementation implicitly rely on those tags being generated.
>
> The authoritative source, used by the the BPF verifier for kfunc
> registration, of functions being BPF kfuncs are
> BTF_KFUNCS_START()/END() declarations. These are BTF_ID_SET8 under the
> hood. Currently resolve_btfids reads kfunc flags from these sets, and
> populates them with BTF IDs.
>
> Implement kfunc discovery from BTF_ID_SET8 symbols in resolve_btfids,
> removing the dependency on pahole's emmission of decl tags.
>
> Walk BTF_ID_KIND_SET8 sets, and use the address-to-symbol index to
> look up set entry's BTF_ID symbol name (before .BTF_ids is patched),
> recording the paired flags directly. This makes find_kfunc_flags()
> helper unnecessary, so it's removed.
>
> Kernel functions can appear in more than one set, which is legitimate,
> since kfunc sets are prog-type dependent in the kernel. So for btf2btf
> processing deduplicate kfuncs by BTF ID, accumulate (OR) the flags,
> and warn on flags mismatch to catch inconsistent declarations.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/bpf/resolve_btfids/main.c | 122 ++++++++++++++------------------
>  1 file changed, 55 insertions(+), 67 deletions(-)
>
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 43512af13148..d35a7b2460e8 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -970,6 +970,23 @@ static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
>         struct kfunc *arr = ctx->kfuncs;
>         u32 cap = ctx->max_kfuncs;
>
> +       /*
> +        * A kfunc can be listed in multiple BTF ID sets.
> +        * In this case, dedup by btf_id and accumulate kfunc flags.
> +        */
> +       for (u32 i = 0; i < ctx->nr_kfuncs; i++) {
> +               if (ctx->kfuncs[i].btf_id != kfunc->btf_id)
> +                       continue;
> +

with hundreds of kfuncs, this O(N^2) approach is going to be a bit
slow, should we use rb tree for lookups?

> +               if (ctx->kfuncs[i].flags != kfunc->flags) {
> +                       pr_err("WARN: resolve_btfids: inconsistent flags for kfunc %s: 0x%x != 0x%x\n",
> +                              kfunc->name, ctx->kfuncs[i].flags, kfunc->flags);
> +                       warnings++;
> +               }
> +               ctx->kfuncs[i].flags |= kfunc->flags;
> +               return 0;
> +       }
> +
>         if (ctx->nr_kfuncs + 1 > cap) {
>                 cap = max(cap + 256, cap * 2);
>                 arr = realloc(arr, sizeof(struct kfunc) * cap);

[...]

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 07/14] resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs
  2026-06-01 22:17 ` [PATCH bpf-next v1 07/14] resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs Ihor Solodrai
@ 2026-06-03 23:45   ` Andrii Nakryiko
  0 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:45 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:19 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Now that collect_kfuncs() discovers kfuncs from BTF_SET8_KFUNCS sets,
> resolve_btfids can also be the source of truth for the bpf_kfunc decl
> tag in BTF.
>
> Today pahole emits it under --btf_features=decl_tag_kfuncs; moving the
> emission in-tree lets that flag be dropped from the kernel build once
> the rest of the kfunc annotations follow.
>
> The implementation in resolve_btfids is idempotent: if a decl_tag
> exists in the input BTF, no new tag will be emitted. This allows to be
> more independent of pahole's version and runtime flags used to
> generate the BTF.
>

As I mentioned in cover letter reply, I think this is unnecessary and
we shouldn't do this. What's the real situations where this is
*necessary*?


> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/bpf/resolve_btfids/main.c | 49 ++++++++++++++++++++++++++++++---
>  1 file changed, 45 insertions(+), 4 deletions(-)
>
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index d35a7b2460e8..30a07ec4ebb6 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -1255,6 +1255,44 @@ static int process_kfunc_with_implicit_args(struct btf2btf_context *ctx, struct
>         return 0;
>  }
>
> +static bool btf_has_decl_tag(struct btf2btf_context *ctx, const char *tag_name,
> +                            u32 target_btf_id, int component_idx)
> +{
> +       const struct btf_type *t;
> +       const char *name;
> +
> +       for (u32 i = 0; i < ctx->nr_decl_tags; i++) {
> +               t = btf__type_by_id(ctx->btf, ctx->decl_tags[i]);
> +               if (t->type != target_btf_id)
> +                       continue;
> +               if (btf_decl_tag(t)->component_idx != component_idx)
> +                       continue;
> +               name = btf__name_by_offset(ctx->btf, t->name_off);
> +               if (name && strcmp(name, tag_name) == 0)
> +                       return true;
> +       }
> +       return false;
> +}
> +
> +/* Add a decl tag if an identical one is not already present. */
> +static int ensure_decl_tag(struct btf2btf_context *ctx, const char *tag_name,
> +                          u32 target_btf_id, int component_idx)
> +{
> +       int new_id;
> +
> +       if (btf_has_decl_tag(ctx, tag_name, target_btf_id, component_idx))
> +               return 0;
> +
> +       new_id = btf__add_decl_tag(ctx->btf, tag_name, target_btf_id, component_idx);
> +       if (new_id < 0) {
> +               pr_err("ERROR: resolve_btfids: failed to add '%s' decl tag for BTF id %u: %d\n",
> +                      tag_name, target_btf_id, new_id);
> +               return new_id;
> +       }
> +
> +       return push_decl_tag_id(ctx, new_id);
> +}
> +
>  static int btf2btf(struct object *obj)
>  {
>         struct btf2btf_context ctx = {};
> @@ -1267,12 +1305,15 @@ static int btf2btf(struct object *obj)
>         for (u32 i = 0; i < ctx.nr_kfuncs; i++) {
>                 struct kfunc *kfunc = &ctx.kfuncs[i];
>
> -               if (!(kfunc->flags & KF_IMPLICIT_ARGS))
> -                       continue;
> -
> -               err = process_kfunc_with_implicit_args(&ctx, kfunc);
> +               err = ensure_decl_tag(&ctx, "bpf_kfunc", kfunc->btf_id, -1);
>                 if (err)
>                         goto out;
> +
> +               if (kfunc->flags & KF_IMPLICIT_ARGS) {
> +                       err = process_kfunc_with_implicit_args(&ctx, kfunc);
> +                       if (err)
> +                               goto out;
> +               }
>         }
>
>         err = 0;
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test
  2026-06-01 22:18 ` [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test Ihor Solodrai
  2026-06-01 22:29   ` sashiko-bot
@ 2026-06-03 23:46   ` Andrii Nakryiko
  1 sibling, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:46 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:19 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Extend test_resolve_btfids() to assert that resolve_btfids emits the
> address_space(1) type attribute (a BTF_KIND_TYPE_TAG with kflag=1) on
> the return type and/or arguments of kfuncs marked with KF_ARENA_RET,
> KF_ARENA_ARG1 or KF_ARENA_ARG2.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  .../selftests/bpf/prog_tests/resolve_btfids.c | 59 +++++++++++++++++++
>  tools/testing/selftests/bpf/progs/btf_data.c  | 10 ++++
>  2 files changed, 69 insertions(+)
>

[...]

> @@ -178,6 +191,22 @@ static bool btf_has_decl_tag(struct btf *btf, const char *tag_name, s32 target_i
>         return false;
>  }
>
> +/* True if @id is PTR -> TYPE_TAG(kflag=1, "address_space(1)") -> pointee */
> +static bool is_arena_tagged_ptr(struct btf *btf, __u32 id)
> +{
> +       const struct btf_type *ptr, *tag;
> +       const char *name;
> +
> +       ptr = btf__type_by_id(btf, id);
> +       if (!ptr || !btf_is_ptr(ptr))
> +               return false;
> +       tag = btf__type_by_id(btf, ptr->type);
> +       if (!tag || !btf_is_type_tag(tag) || !btf_kflag(tag))

drop !ptr, !tag, etc, BTF is not malformed (and if it is, test_progs
crashing is not a big deal, IMO)

> +               return false;
> +       name = btf__name_by_offset(btf, tag->name_off);
> +       return name && strcmp(name, "address_space(1)") == 0;
> +}
> +
>  void test_resolve_btfids(void)
>  {
>         __u32 *test_list, *test_lists[] = { test_list_local, test_list_global };
> @@ -253,6 +282,36 @@ void test_resolve_btfids(void)
>                                                      kfunc_symbols[i].id),
>                                     kfunc_symbols[i].name);
>
> +       /* Check resolve_btfids wrapped exactly the arena-flagged return/args with
> +        * the address_space(1) type attribute, and left other pointers/returns
> +        * untouched.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++) {
> +               const struct btf_type *fn, *proto;
> +               const struct btf_param *params;
> +               const char *name = kfunc_symbols[i].name;
> +               u32 fl = kfunc_symbols[i].flags;
> +               __u32 nr;
> +
> +               fn = btf__type_by_id(btf, kfunc_symbols[i].id);
> +               if (!ASSERT_TRUE(fn && btf_is_func(fn), name))
> +                       continue;
> +               proto = btf__type_by_id(btf, fn->type);
> +               if (!ASSERT_TRUE(proto && btf_is_func_proto(proto), name))
> +                       continue;
> +               params = btf_params(proto);
> +               nr = btf_vlen(proto);
> +
> +               ASSERT_EQ(is_arena_tagged_ptr(btf, proto->type),
> +                         !!(fl & KF_ARENA_RET), name);
> +               if (nr > 0)
> +                       ASSERT_EQ(is_arena_tagged_ptr(btf, params[0].type),
> +                                 !!(fl & KF_ARENA_ARG1), name);
> +               if (nr > 1)
> +                       ASSERT_EQ(is_arena_tagged_ptr(btf, params[1].type),
> +                                 !!(fl & KF_ARENA_ARG2), name);

I'd add {} around all multi-line if bodies

> +       }
> +
>  out:
>         btf__free(btf);
>  }
> diff --git a/tools/testing/selftests/bpf/progs/btf_data.c b/tools/testing/selftests/bpf/progs/btf_data.c
> index 8587658012c3..ec34f7a6e038 100644
> --- a/tools/testing/selftests/bpf/progs/btf_data.c
> +++ b/tools/testing/selftests/bpf/progs/btf_data.c
> @@ -58,3 +58,13 @@ int kfunc_b(struct root_struct *root)
>  {
>         return 0;
>  }
> +
> +struct root_struct *kfunc_c(struct root_struct *a, struct root_struct *b)
> +{
> +       return a;
> +}
> +
> +int kfunc_d(struct root_struct *a, struct root_struct *b)
> +{
> +       return 0;
> +}
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 10/14] selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test
  2026-06-01 22:18 ` [PATCH bpf-next v1 10/14] selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test Ihor Solodrai
@ 2026-06-03 23:47   ` Andrii Nakryiko
  0 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:47 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:19 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Extend test_resolve_btfids() to assert that resolve_btfids emits a
> BTF_KIND_DECL_TAG of name "bpf_fastcall" for each kfunc marked with
> KF_FASTCALL flag.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/testing/selftests/bpf/prog_tests/resolve_btfids.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> index 7d9c3460cbed..eeda4e3b6a7f 100644
> --- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> +++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
> @@ -246,6 +246,13 @@ void test_resolve_btfids(void)
>                                              kfunc_symbols[i].id),
>                             kfunc_symbols[i].name);
>
> +       /* Check resolve_btfids emitted bpf_fastcall decl_tag for fastcall kfuncs */
> +       for (i = 0; i < ARRAY_SIZE(kfunc_symbols); i++)
> +               if (kfunc_symbols[i].flags & KF_FASTCALL)
> +                       ASSERT_TRUE(btf_has_decl_tag(btf, "bpf_fastcall",
> +                                                    kfunc_symbols[i].id),
> +                                   kfunc_symbols[i].name);
> +

{}

>  out:
>         btf__free(btf);
>  }
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 11/14] resolve_btfids: Process KF_ARENA_* flags in resolve_btfids
  2026-06-01 22:18 ` [PATCH bpf-next v1 11/14] resolve_btfids: Process KF_ARENA_* flags in resolve_btfids Ihor Solodrai
@ 2026-06-03 23:47   ` Andrii Nakryiko
  0 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:47 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:19 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> For kfuncs flagged KF_ARENA_RET, KF_ARENA_ARG1 or KF_ARENA_ARG2,
> address_space(1) attribute (type tag with kflag=1) needs to be emitted
> to BTF for the return type or arg type respectively.
>
> So far this has been done by pahole [1].
>
> Implement the emission of the arena attributes in resolve_btfids: for
> flagged kfuncs create a new function prototype with updated BTF types,
> adding tags as necessary.
>
> Similar to decl tags emission, the transformation is idempotent: if
> the types are already tagged with address_space(1), it's a noop.
>
> [1] https://lore.kernel.org/dwarves/20250228194654.1022535-1-ihor.solodrai@linux.dev/
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/bpf/resolve_btfids/main.c | 131 ++++++++++++++++++++++++++++++++
>  1 file changed, 131 insertions(+)
>
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index f276200b1a68..aafbcfec755b 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -162,6 +162,9 @@ struct object {
>  };
>
>  #define KF_FASTCALL (1 << 12)
> +#define KF_ARENA_RET (1 << 13)
> +#define KF_ARENA_ARG1 (1 << 14)
> +#define KF_ARENA_ARG2 (1 << 15)
>  #define KF_IMPLICIT_ARGS (1 << 16)
>  #define KF_IMPL_SUFFIX "_impl"
>
> @@ -1294,6 +1297,128 @@ static int ensure_decl_tag(struct btf2btf_context *ctx, const char *tag_name,
>         return push_decl_tag_id(ctx, new_id);
>  }
>
> +static bool is_arena_type_attr(struct btf *btf, u32 id)
> +{
> +       const struct btf_type *t = btf__type_by_id(btf, id);
> +       const char *name;
> +
> +       if (!t || !btf_is_type_tag(t) || !btf_kflag(t))

!t can't (shouldn't) happen, let's not check for things that are not
supposed to happen, because we'll start double-guessing what are the
situations where this is valid and whether we deal with that
correctly.

It's kernel build, we can assume valid BTF.

> +               return false;
> +       name = btf__name_by_offset(btf, t->name_off);
> +       return name && strcmp(name, "address_space(1)") == 0;
> +}
> +
> +static s32 ensure_arena_tagged_ptr(struct btf *btf, u32 ptr_id)
> +{
> +       const struct btf_type *ptr = btf__type_by_id(btf, ptr_id);
> +       s32 tag_id;
> +
> +       if (!ptr || !btf_is_ptr(ptr))
> +               return -EINVAL;
> +
> +       if (is_arena_type_attr(btf, ptr->type))
> +               return ptr_id;
> +
> +       tag_id = btf__add_type_attr(btf, "address_space(1)", ptr->type);
> +       if (tag_id < 0)
> +               return tag_id;
> +
> +       return btf__add_ptr(btf, tag_id);
> +}
> +
> +/*
> + * Build a FUNC_PROTO for @kfunc with each arena-flagged return/parameter
> + * pointer tagged with address_space(1). Pointers already tagged are kept as is.
> + *
> + * If nothing needs tagging, the original proto id is returned unchanged.
> + * Otherwise a new FUNC_PROTO is created and its id returned. The original
> + * proto may be shared with sibling FUNCs, so it must not be modified in place.
> + */
> +static s32 ensure_arena_tagged_proto(struct btf *btf, struct kfunc *kfunc)
> +{
> +       const struct btf_type *func = btf__type_by_id(btf, kfunc->btf_id);
> +       u32 proto_id = func->type;
> +       const struct btf_type *proto = btf__type_by_id(btf, proto_id);
> +       const struct btf_param *params = btf_params(proto);
> +       u32 nr_params = btf_vlen(proto);
> +       s32 arg0_type_id = nr_params > 0 ? (s32)params[0].type : -1;
> +       s32 arg1_type_id = nr_params > 1 ? (s32)params[1].type : -1;
> +       s32 ret_type_id = proto->type;
> +       s32 new_proto_id, id;
> +       bool changed = false;
> +       int err;
> +
> +       if (kfunc->flags & KF_ARENA_RET) {
> +               id = ensure_arena_tagged_ptr(btf, ret_type_id);
> +               if (id < 0)
> +                       return id;
> +               changed |= id != ret_type_id;
> +               ret_type_id = id;
> +       }
> +
> +       if (nr_params > 0 && (kfunc->flags & KF_ARENA_ARG1)) {
> +               id = ensure_arena_tagged_ptr(btf, arg0_type_id);
> +               if (id < 0)
> +                       return id;
> +               changed |= id != arg0_type_id;
> +               arg0_type_id = id;
> +       }

else scream loudly, this is not supposed to happen. we are being too
lax about these violations here

this is a good place to check and emit human-readable errors about
invalid kfunc declarations, so let's do it

and again, let's not do the ensure pattern, add unconditionally, don't
check "changed" (it will), etc

> +
> +       if (nr_params > 1 && (kfunc->flags & KF_ARENA_ARG2)) {
> +               id = ensure_arena_tagged_ptr(btf, arg1_type_id);
> +               if (id < 0)
> +                       return id;
> +               changed |= id != arg1_type_id;
> +               arg1_type_id = id;
> +       }
> +

[...]

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH bpf-next v1 13/14] kbuild: Drop decl_tag_kfuncs and attributes from pahole flags
  2026-06-01 22:18 ` [PATCH bpf-next v1 13/14] kbuild: Drop decl_tag_kfuncs and attributes from pahole flags Ihor Solodrai
@ 2026-06-03 23:48   ` Andrii Nakryiko
  0 siblings, 0 replies; 38+ messages in thread
From: Andrii Nakryiko @ 2026-06-03 23:48 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Alan Maguire,
	Jiri Olsa, bpf, linux-kbuild

On Mon, Jun 1, 2026 at 3:19 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> resolve_btfids now emits all kfunc-specific BTF annotations for the
> kernel: the "bpf_kfunc" and "bpf_fastcall" decl tags and the
> address_space(1) type attribute on arena kfunc return/arguments. These
> were previously produced by pahole under the "decl_tag_kfuncs" and
> "attributes" btf_features.
>
> Drop both from the pahole invocation.
>
> Note that "decl_tag" (generic source __attribute__((btf_decl_tag))
> annotations) is a separate feature from "decl_tag_kfuncs" and must be kept.
>

logistically, this pahole flag drop should go in the same commit as
resolve_btfids change that adds all of its functionality... which
probably requires merging few patches, but given all my ranting about
not doing "ensure" pattern, it's unavoidable, IMO



> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  scripts/Makefile.btf | 4 +---
>  1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
> index e66e13e79653..a1812985a61a 100644
> --- a/scripts/Makefile.btf
> +++ b/scripts/Makefile.btf
> @@ -14,9 +14,7 @@ pahole-flags-$(call test-ge, $(pahole-ver), 125)      += --skip_encoding_btf_inconsis
>  else
>
>  # Switch to using --btf_features for v1.26 and later.
> -pahole-flags-$(call test-ge, $(pahole-ver), 126)  = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs
> -
> -pahole-flags-$(call test-ge, $(pahole-ver), 130) += --btf_features=attributes
> +pahole-flags-$(call test-ge, $(pahole-ver), 126)  = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func
>
>  pahole-flags-$(call test-ge, $(pahole-ver), 131) += --btf_features=layout
>
> --
> 2.54.0
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2026-06-03 23:48 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-01 22:17 [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Ihor Solodrai
2026-06-01 22:17 ` [PATCH bpf-next v1 01/14] tools/bpf: Sync btf_ids.h to tools Ihor Solodrai
2026-06-01 22:17 ` [PATCH bpf-next v1 02/14] selftests/bpf: Modernize resolve_btfids test scaffolding Ihor Solodrai
2026-06-02 13:02   ` Jiri Olsa
2026-06-02 18:30     ` Ihor Solodrai
2026-06-01 22:17 ` [PATCH bpf-next v1 03/14] selftests/bpf: Fix resolve_btfids test reads of BTF ID sets in PIE builds Ihor Solodrai
2026-06-03 23:45   ` Andrii Nakryiko
2026-06-01 22:17 ` [PATCH bpf-next v1 04/14] selftests/bpf: Add kfunc set test to resolve_btfids Ihor Solodrai
2026-06-02 13:02   ` Jiri Olsa
2026-06-03 23:45   ` Andrii Nakryiko
2026-06-01 22:17 ` [PATCH bpf-next v1 05/14] resolve_btfids: Index BTF ID symbols by address Ihor Solodrai
2026-06-01 22:28   ` sashiko-bot
2026-06-01 23:03   ` bot+bpf-ci
2026-06-02 13:01   ` Jiri Olsa
2026-06-02 18:28     ` Ihor Solodrai
2026-06-03 23:45   ` Andrii Nakryiko
2026-06-01 22:17 ` [PATCH bpf-next v1 06/14] resolve_btfids: Discover kfuncs from BTF ID sets Ihor Solodrai
2026-06-01 22:33   ` sashiko-bot
2026-06-02 18:36     ` Ihor Solodrai
2026-06-02 20:36   ` Jiri Olsa
2026-06-02 21:08     ` Ihor Solodrai
2026-06-03 23:45       ` Andrii Nakryiko
2026-06-03 23:45   ` Andrii Nakryiko
2026-06-01 22:17 ` [PATCH bpf-next v1 07/14] resolve_btfids: Emit bpf_kfunc BTF decl tag for discovered kfuncs Ihor Solodrai
2026-06-03 23:45   ` Andrii Nakryiko
2026-06-01 22:17 ` [PATCH bpf-next v1 08/14] selftests/bpf: Verify bpf_kfunc decl tag emission in resolve_btfids Ihor Solodrai
2026-06-01 22:18 ` [PATCH bpf-next v1 09/14] resolve_btfids: Emit a decl tag for kfuncs with KF_FASTCALL Ihor Solodrai
2026-06-01 22:18 ` [PATCH bpf-next v1 10/14] selftests/bpf: Verify bpf_fastcall decl tags in resolve_btfids test Ihor Solodrai
2026-06-03 23:47   ` Andrii Nakryiko
2026-06-01 22:18 ` [PATCH bpf-next v1 11/14] resolve_btfids: Process KF_ARENA_* flags in resolve_btfids Ihor Solodrai
2026-06-03 23:47   ` Andrii Nakryiko
2026-06-01 22:18 ` [PATCH bpf-next v1 12/14] selftests/bpf: Verify arena type tags in resolve_btfids test Ihor Solodrai
2026-06-01 22:29   ` sashiko-bot
2026-06-03 23:46   ` Andrii Nakryiko
2026-06-01 22:18 ` [PATCH bpf-next v1 13/14] kbuild: Drop decl_tag_kfuncs and attributes from pahole flags Ihor Solodrai
2026-06-03 23:48   ` Andrii Nakryiko
2026-06-01 22:18 ` [PATCH bpf-next v1 14/14] docs, resolve_btfids: Document kfunc BTF annotation emission Ihor Solodrai
2026-06-03 23:45 ` [PATCH bpf-next v1 00/14] resolve_btfids: Implement BTF tags emission for kfuncs Andrii Nakryiko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.