* [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump
@ 2026-11-05 17:58 Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 01/17] Revert "libbpf: Render the arena type tag with its address space" Ihor Solodrai
` (17 more replies)
0 siblings, 18 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
bpftool's C dump wraps its output in a clang attribute pragma:
#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
GCC-BPF implements preserve_access_index, but not the pragma. Since
the commit 675b4e24bc50 ("bpf: Temporarily define
BPF_NO_PRESEVE_ACCESS_INDEX for GCC") the BPF selftests build GCC
objects with -DBPF_NO_PRESERVE_ACCESS_INDEX, which disables automatic
CO-RE for vmlinux.h field accesses.
This series replaces the pragma with an explicit attribute on each
record definition, and uses the same mechanism to annotate the context
types that the verifier rewrites with preserve_static_offset.
Jose proposed per-record attributes in May 2024 [1]. It stalled on a
requirement from preserve_static_offset (PSO) work [2] to add
attributes on selected types only, which a fixed string cannot
express.
BTF decl tags can carry an arbitrary attribute list since commit
51d1b1d42841 ("libbpf: Introduce kflag for type_tags and decl_tags in
BTF"), and btf_dump renders them on record definitions, since
76ea31239021 ("libbpf: Render decl_tags in btf_dump"). This allows
bpftool to put any attribute on any subset of records, with no new
libbpf API.
btf_dump now prints the strings of kflagged decl and type tags as is,
allowing macros defined by the generated header. bpftool attaches decl
tags from its dump split to input type tags; btf_dump prints the decl
tag instead. The input, including module base BTF, stays unchanged.
Overrides render legacy "address_space(1)" as
__attribute__((address_space(1))) and plain "arena" as __bpf_arena,
which adds address_space(1) while preserving the BTF tag.
resolve_btfids emits the complete specifier in new kernel BTF; legacy
handling remains in bpftool.
Old bpftool built with libbpf 1.6 or later emits invalid arena kfunc
prototypes from new kernel BTF, breaking programs that include them.
Users will be advised to update their bpftool when generating
vmlinux.h for new kernels.
[1] https://lore.kernel.org/bpf/20240503111836.25275-1-jose.marchesi@oracle.com/
[2] https://lore.kernel.org/bpf/20231208000531.19179-1-eddyz87@gmail.com/
[3] https://github.com/llvm/llvm-project/pull/234280
---
Series structure:
* 1-5: Preparation: revert the libbpf arena special case, fix string
lookup, and prepare split dumping and test fixtures.
* 6-9: Decl-tag overrides, verbatim kflagged tags, legacy arena
compatibility and tests.
* 10-15: Per-record CO-RE and static-offset annotations, tests and docs.
* 16-17: __bpf_arena rendering and tests.
v3->v4:
* Let decl tags override type tag rendering in btf_dump.
* Replace the in-place legacy arena fixup with such overrides. This
also handles module and merged BTF without changing the input.
(Alexei, Andrii)
* Add a GENERATED HEADERS section and an example showing both macro
definitions and their annotations (Quentin)
* Revert libbpf's special case for the plain "arena" type
tag. Render it as __bpf_arena in bpftool instead.
* Reorder some patches vs v3: prep first, and keep the renderer,
resolve_btfids producer and legacy compatibility changes together
in one commit ("Dump kflagged BTF tags as is" patch)
* Add type-tag override tests, legacy module BTF coverage, and the
__bpf_arena case
* Remove unused array/modifier unwrapping from the anonymous record walk
* Reword commit messages, documentation and comments
v3: https://lore.kernel.org/bpf/20261030233355.1836749-1-ihor.solodrai@linux.dev/
v2->v3:
* Print kflagged decl and type tags as is in btf_dump, and encode
the complete arena specifier in resolve_btfids (Andrii)
* Define __bpf_relocatable and __bpf_static_offset as complete
attribute specifiers, so record definitions carry only the macro
names (Andrii)
* New patch "bpftool: Normalize legacy arena type tags before C dumps"
keeps C dumps of older kernel BTF valid
* Rename the no_pai map and helper to non_relocatable, and use a bool
array instead of linux/bitmap.h (Andrii)
* selftest refactoring: build bpftool_btf_dump fixtures in
btf_to_tmpfile()
* Commit message and documentation rewording, other nits
v2: https://lore.kernel.org/bpf/20261023222652.3927736-1-ihor.solodrai@linux.dev/
v1->v2:
* Rename __bpf_core_pai to __bpf_relocatable
* Rename __bpf_core_pso to __bpf_static_offset
* Use a split BTF to add decl_tags in bpftool instead of a detached
copy (Alexei, Alan)
* New patch "libbpf: Look up BTF strings without making BTF
modifiable" lets btf__find_str() build its string index without
making BTF modifiable
* Dropped patch "bpftool: Dump C from a detached BTF copy"
* The Clang crash on __builtin_preserve_field_info over PSO types is
fixed in LLVM [3]. PSO stays on by default; -D__bpf_static_offset=
remains the documented workaround for Clang 18 through 23
* GCC-BPF gets __bpf_relocatable from patch #3, so its narrow loads
from context types fail verification until patch #4 (bpf-ci bot),
left as is
* Commit message and documentation rewording
v1: https://lore.kernel.org/bpf/20261016000012.2232059-1-ihor.solodrai@linux.dev/
---
Ihor Solodrai (17):
Revert "libbpf: Render the arena type tag with its address space"
libbpf: Look up BTF strings without making BTF modifiable
bpftool: Dump C through a split BTF
selftests/bpf: Build BTF fixtures in btf_to_tmpfile()
selftests/bpf: Cover unknown BTF header-extension bytes in bpftool C
dumps
libbpf: Let decl tags override how btf_dump renders type tags
libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is
selftests/bpf: Cover type tag rendering overrides in btf_dump
selftests/bpf: Cover legacy arena type tags in bpftool C dumps
bpftool: Apply __bpf_relocatable per record on BTF dump
bpftool: Skip __bpf_relocatable on verifier-rewritten context types
bpftool: Emit __bpf_static_offset on verifier-rewritten context types
selftests/bpf: Cover record attributes in bpftool BTF dumps
selftests/bpf: Cover context and socket accesses through vmlinux.h
bpftool: Document __bpf_relocatable and __bpf_static_offset
bpftool: Render the arena type tag as __bpf_arena
selftests/bpf: Cover the arena type tag in bpftool C dumps
Documentation/bpf/btf.rst | 24 +-
Documentation/bpf/kfuncs.rst | 3 +-
.../bpf/bpftool/Documentation/bpftool-btf.rst | 73 ++++-
tools/bpf/bpftool/btf.c | 195 ++++++++++++-
tools/bpf/resolve_btfids/main.c | 2 +-
tools/lib/bpf/btf.c | 51 ++--
tools/lib/bpf/btf_dump.c | 19 +-
.../bpf/bpftool_btf_dump_sorted.expected | 54 +++-
.../bpf/bpftool_btf_dump_unsorted.expected | 54 +++-
.../bpf/prog_tests/bpftool_btf_dump.c | 259 +++++++++++++++---
.../selftests/bpf/prog_tests/btf_dump.c | 119 ++++----
.../selftests/bpf/prog_tests/resolve_btfids.c | 4 +-
.../bpf/prog_tests/test_struct_ops_arena.c | 2 +-
.../selftests/bpf/prog_tests/verifier.c | 2 +
.../progs/struct_ops_arena_member_render.c | 7 +-
.../bpf/progs/verifier_ctx_rewritten.c | 60 ++++
16 files changed, 774 insertions(+), 154 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/verifier_ctx_rewritten.c
base-commit: a3c8813409f13779fec225468aba7d4067a66468
--
2.56.0
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 01/17] Revert "libbpf: Render the arena type tag with its address space"
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 02/17] libbpf: Look up BTF strings without making BTF modifiable Ihor Solodrai
` (16 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
This reverts commit 7418d9291c57ea563b5fa706dea0883effe5d295.
This reverts commit 31ef8ab6dfc7152e4b64bcae19a3e07b10dd8fea.
The commit made btf_dump print the plain "arena" type tag as
__attribute__((address_space(1))) followed by the tag, special-casing
one tag name in the generic renderer. How a kernel type tag is spelled
in a generated header is up to the tool that generates it, so render
every plain type tag the same way again.
No libbpf release contains the reverted change. libbpf v1.8.0 is
synced up to commit acff58e30517 ("libbpf: Fix cleanup on invalid
CO-RE relocation offsets").
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
Later in this series bpftool spells "arena" as __bpf_arena in the
generated vmlinux.h, through decl tags in its own split BTF.
---
tools/lib/bpf/btf_dump.c | 3 ---
.../testing/selftests/bpf/prog_tests/btf_dump.c | 16 +++-------------
.../bpf/progs/struct_ops_arena_member_render.c | 8 ++++----
3 files changed, 7 insertions(+), 20 deletions(-)
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index ea9a34db4c00..ad43bbd3b442 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -1595,9 +1595,6 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
name = btf_name_of(d, t->name_off);
if (btf_kflag(t))
btf_dump_printf(d, " __attribute__((%s))", name);
- else if (strcmp(name, "arena") == 0)
- /* the tag denotes a pointer into the BPF arena address space */
- btf_dump_printf(d, " __attribute__((address_space(1))) __attribute__((btf_type_tag(\"arena\")))");
else
btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name);
break;
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index dbef014f75ac..5a5ee001ac8b 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -604,10 +604,8 @@ static void test_btf_dump_type_tags(void)
* struct s {
* void __attribute__((btf_type_tag(\"void_tag\"))) *p1;
* void __attribute__((void_attr)) *p2;
- * void __arena *p3;
* };
*
- * The arena tag is rendered with the address space it denotes.
*/
id = btf__add_type_tag(btf, "void_tag", 0);
@@ -620,25 +618,17 @@ static void test_btf_dump_type_tags(void)
id = btf__add_ptr(btf, id);
ASSERT_EQ(id, 4, "void_ptr_id2");
- id = btf__add_type_tag(btf, "arena", 0);
- ASSERT_EQ(id, 5, "arena_tag_id");
- id = btf__add_ptr(btf, id);
- ASSERT_EQ(id, 6, "void_ptr_id3");
-
- id = btf__add_struct(btf, "s", 24);
- ASSERT_EQ(id, 7, "struct_id");
+ id = btf__add_struct(btf, "s", 8);
+ ASSERT_EQ(id, 5, "struct_id");
err = btf__add_field(btf, "p1", 2, 0, 0);
ASSERT_OK(err, "field_ok1");
- err = btf__add_field(btf, "p2", 4, 64, 0);
+ err = btf__add_field(btf, "p2", 4, 0, 0);
ASSERT_OK(err, "field_ok2");
- err = btf__add_field(btf, "p3", 6, 128, 0);
- ASSERT_OK(err, "field_ok3");
test_ctx__dump_and_compare(&t,
"struct s {\n"
" void __attribute__((btf_type_tag(\"void_tag\"))) *p1;\n"
" void __attribute__((void_attr)) *p2;\n"
-" void __attribute__((address_space(1))) __attribute__((btf_type_tag(\"arena\"))) *p3;\n"
"};\n\n", "dump_and_compare");
test_ctx__free(&t);
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c b/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
index c4e134e14f87..253ffdd86d32 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
@@ -11,10 +11,10 @@
char _license[] SEC("license") = "GPL";
/*
- * The module types as a vmlinux.h generated by a libbpf that does not render
- * the arena tag with its address space shows them: an attribute on a pointer
- * in the kernel address space, so the program has to cast the member to the
- * arena address space before using it.
+ * The module types as vmlinux.h renders them: the tag is an attribute on a
+ * pointer in the kernel address space, so the program has to cast the member
+ * to the arena address space before using it. That is what a program built
+ * against vmlinux.h sees for a kernel struct with an __arena member.
*/
#define __arena_tag __attribute__((btf_type_tag("arena")))
#define __nullable_tag __attribute__((btf_type_tag("nullable")))
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 02/17] libbpf: Look up BTF strings without making BTF modifiable
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 01/17] Revert "libbpf: Render the arena type tag with its address space" Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 18:41 ` bot+bpf-ci
2026-11-05 17:58 ` [PATCH bpf-next v4 03/17] bpftool: Dump C through a split BTF Ihor Solodrai
` (15 subsequent siblings)
17 siblings, 1 reply; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
btf__find_str() needs a hash index of the string section, and gets one
by making BTF modifiable. That copies types and strings out of the raw
data, and fails with -EOPNOTSUPP on BTF with unknown extension bytes.
btf__add_str() and split btf__dedup() look up strings in the base BTF
first. So adding a string to split BTF, or deduping it, makes the base
modifiable as a side effect, and fails if the base comes from a newer
kernel with new header fields.
Build the string index without making BTF modifiable, and reuse it if
BTF becomes modifiable later. The index can now exist on read-only
BTF, so btf__free() frees it unconditionally.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/lib/bpf/btf.c | 45 +++++++++++++++++++++++++++------------------
1 file changed, 27 insertions(+), 18 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index e1ff97098ee5..5786232c1fea 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -153,9 +153,10 @@ struct btf {
*/
int start_str_off;
- /* only one of strs_data or strs_set can be non-NULL, depending on
- * whether BTF is in a modifiable state (strs_set is used) or not
- * (strs_data points inside raw_data)
+ /*
+ * strs_data points inside raw_data until BTF becomes modifiable,
+ * then strings live in strs_set. Before that, strs_set may exist
+ * as a lookup index for btf__find_str().
*/
void *strs_data;
/* a set of unique strings */
@@ -1252,15 +1253,15 @@ void btf__free(struct btf *btf)
if (btf_is_modifiable(btf)) {
/* if BTF was modified after loading, it will have a split
- * in-memory representation for types, strings and layout
- * sections, so we need to free all of them individually. It
- * might still have a cached contiguous raw data present,
- * which will be unconditionally freed below.
+ * in-memory representation for types and layout sections,
+ * so we need to free both individually. It might still have
+ * a cached contiguous raw data present, which will be
+ * unconditionally freed below.
*/
free(btf->types_data);
- strset__free(btf->strs_set);
free(btf->layout);
}
+ strset__free(btf->strs_set);
btf_free_raw_data(btf);
free(btf->raw_data_swapped);
free(btf->type_offs);
@@ -2043,6 +2044,21 @@ static void btf_invalidate_raw_data(struct btf *btf)
btf->named_start_id = 0;
}
+static int btf_ensure_strs_set(struct btf *btf)
+{
+ struct strset *set;
+
+ if (btf->strs_set)
+ return 0;
+
+ set = strset__new(BTF_MAX_STR_OFFSET, btf->strs_data, btf->hdr.str_len);
+ if (IS_ERR(set))
+ return PTR_ERR(set);
+
+ btf->strs_set = set;
+ return 0;
+}
+
/* Ensure BTF is ready to be modified (by splitting into a three memory
* regions for types, strings and layout. Also invalidate cached
* raw_data, if any.
@@ -2050,7 +2066,6 @@ static void btf_invalidate_raw_data(struct btf *btf)
static int btf_ensure_modifiable(struct btf *btf)
{
void *types, *layout = NULL;
- struct strset *set = NULL;
int err = -ENOMEM;
if (btf_is_modifiable(btf)) {
@@ -2078,18 +2093,14 @@ static int btf_ensure_modifiable(struct btf *btf)
btf->hdr.layout_len);
}
- /* build lookup index for all strings */
- set = strset__new(BTF_MAX_STR_OFFSET, btf->strs_data, btf->hdr.str_len);
- if (IS_ERR(set)) {
- err = PTR_ERR(set);
+ err = btf_ensure_strs_set(btf);
+ if (err)
goto err_out;
- }
/* only when everything was successful, update internal state */
btf->types_data = types;
btf->types_data_cap = btf->hdr.type_len;
btf->strs_data = NULL;
- btf->strs_set = set;
if (layout)
btf->layout = layout;
/* if BTF was created from scratch, all strings are guaranteed to be
@@ -2108,7 +2119,6 @@ static int btf_ensure_modifiable(struct btf *btf)
return 0;
err_out:
- strset__free(set);
free(types);
free(layout);
return err;
@@ -2131,8 +2141,7 @@ int btf__find_str(struct btf *btf, const char *s)
return off;
}
- /* BTF needs to be in a modifiable state to build string lookup index */
- err = btf_ensure_modifiable(btf);
+ err = btf_ensure_strs_set(btf);
if (err)
return libbpf_err(err);
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 03/17] bpftool: Dump C through a split BTF
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 01/17] Revert "libbpf: Render the arena type tag with its address space" Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 02/17] libbpf: Look up BTF strings without making BTF modifiable Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 04/17] selftests/bpf: Build BTF fixtures in btf_to_tmpfile() Ihor Solodrai
` (14 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
btf_dump prints decl tags, so bpftool can annotate types in the C dump
by adding decl tags to the BTF it dumps. Adding them to the input BTF
would make it modifiable, which libbpf refuses for BTF with unknown
header-extension bytes, such as BTF from a newer kernel.
Dump C through an empty split BTF over the input instead, so that the
annotations can go there. btf__new_empty_split() does not inherit the
base pointer size, so set it explicitly.
No functional change.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
Later in this series bpftool adds decl tags to the split, for record
attributes and type tag spellings.
---
tools/bpf/bpftool/btf.c | 28 +++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 385521e6b028..a438b5a03f5a 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -1092,17 +1092,30 @@ static struct sort_datum *sort_btf_c(const struct btf *btf)
return datums;
}
-static int dump_btf_c(const struct btf *btf,
+static int dump_btf_c(struct btf *btf,
__u32 *root_type_ids, int root_type_cnt, bool sort_dump)
{
struct sort_datum *datums = NULL;
- struct btf_dump *d;
+ struct btf_dump *d = NULL;
+ struct btf *split;
+ size_t ptr_sz;
int err = 0, i;
- d = btf_dump__new(btf, btf_dump_printf, NULL, NULL);
- if (!d)
+ split = btf__new_empty_split(btf);
+ if (!split)
return -errno;
+ /* btf__new_empty_split() does not inherit the base pointer size */
+ ptr_sz = btf__pointer_size(btf);
+ if (ptr_sz)
+ btf__set_pointer_size(split, ptr_sz);
+
+ d = btf_dump__new(split, btf_dump_printf, NULL, NULL);
+ if (!d) {
+ err = -errno;
+ goto done;
+ }
+
printf("#ifndef __VMLINUX_H__\n");
printf("#define __VMLINUX_H__\n");
printf("\n");
@@ -1130,10 +1143,10 @@ static int dump_btf_c(const struct btf *btf,
goto done;
}
} else {
- int cnt = btf__type_cnt(btf);
+ int cnt = btf__type_cnt(split);
if (sort_dump)
- datums = sort_btf_c(btf);
+ datums = sort_btf_c(split);
for (i = 0; i < cnt; i++) {
int idx = datums ? datums[i].index : i;
@@ -1146,7 +1159,7 @@ static int dump_btf_c(const struct btf *btf,
goto done;
}
- err = dump_btf_kfuncs(d, btf);
+ err = dump_btf_kfuncs(d, split);
if (err)
goto done;
}
@@ -1160,6 +1173,7 @@ static int dump_btf_c(const struct btf *btf,
done:
free(datums);
btf_dump__free(d);
+ btf__free(split);
return err;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 04/17] selftests/bpf: Build BTF fixtures in btf_to_tmpfile()
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (2 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 03/17] bpftool: Dump C through a split BTF Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 05/17] selftests/bpf: Cover unknown BTF header-extension bytes in bpftool C dumps Ihor Solodrai
` (13 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
test_bpftool_btf_dump() builds each BTF fixture, writes it to a
temporary file, and frees the BTF after the subtests on that file
finish, although they only read the file. Each fixture repeats this
handling, and one that fails to build skips the subtests of all later
fixtures.
Pass the fixture constructor to btf_to_tmpfile(), which frees the BTF
once it is written. A fixture that fails to build now skips only its
own subtests.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../bpf/prog_tests/bpftool_btf_dump.c | 58 +++++++++----------
1 file changed, 26 insertions(+), 32 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
index 534aa79a4278..80e8e435d702 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -109,30 +109,39 @@ static struct btf *mk_loc_btf(void)
return btf;
}
-static int btf_to_tmpfile(const struct btf *btf, char *path)
+static int btf_to_tmpfile(struct btf *(*mk)(void), char *path)
{
ssize_t written;
+ struct btf *btf;
const void *raw;
+ int err = -1;
__u32 sz;
int fd;
+ btf = mk();
+ if (!btf)
+ return -1;
+
raw = btf__raw_data(btf, &sz);
if (!ASSERT_OK_PTR(raw, "raw_data"))
- return -1;
+ goto out;
snprintf(path, PATH_MAX, "/tmp/bpftool_btf_dump.XXXXXX");
fd = mkstemp(path);
if (!ASSERT_OK_FD(fd, "mkstemp_btf"))
- return -1;
+ goto out;
written = write(fd, raw, sz);
close(fd);
if (!ASSERT_EQ(written, sz, "write_btf")) {
unlink(path);
- return -1;
+ goto out;
}
- return 0;
+ err = 0;
+out:
+ btf__free(btf);
+ return err;
}
static char *dump_c(const char *btf_path, bool sorted)
@@ -265,33 +274,18 @@ static void test_loc_dump(const char *btf_path)
void test_bpftool_btf_dump(void)
{
char path[PATH_MAX];
- struct btf *btf = NULL;
-
- btf = mk_btf();
- if (!btf)
- return;
-
- if (btf_to_tmpfile(btf, path))
- goto out_btf;
- if (test__start_subtest("c_sorted"))
- test_dump(path, true);
- if (test__start_subtest("c_unsorted"))
- test_dump(path, false);
- unlink(path);
-
- btf__free(btf);
-
- btf = mk_loc_btf();
- if (!btf)
- return;
-
- if (btf_to_tmpfile(btf, path))
- goto out_btf;
+ if (!btf_to_tmpfile(mk_btf, path)) {
+ if (test__start_subtest("c_sorted"))
+ test_dump(path, true);
+ if (test__start_subtest("c_unsorted"))
+ test_dump(path, false);
+ unlink(path);
+ }
- if (test__start_subtest("loc_dump"))
- test_loc_dump(path);
- unlink(path);
-out_btf:
- btf__free(btf);
+ if (!btf_to_tmpfile(mk_loc_btf, path)) {
+ if (test__start_subtest("loc_dump"))
+ test_loc_dump(path);
+ unlink(path);
+ }
}
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 05/17] selftests/bpf: Cover unknown BTF header-extension bytes in bpftool C dumps
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (3 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 04/17] selftests/bpf: Build BTF fixtures in btf_to_tmpfile() Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 06/17] libbpf: Let decl tags override how btf_dump renders type tags Ihor Solodrai
` (12 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
libbpf can parse BTF whose header carries unknown nonzero extension
bytes, such as BTF from a newer kernel, but refuses to modify it.
bpftool dumps C through a split BTF over the input, so that it can
annotate the dump without modifying the input.
Check that a C dump of such BTF matches the dump of the same BTF
without the extension bytes.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../bpf/prog_tests/bpftool_btf_dump.c | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
index 80e8e435d702..0cfff1b96666 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -57,6 +57,42 @@ static struct btf *mk_btf(void)
return btf;
}
+/* mk_btf() with a nonzero byte in an unknown header extension */
+static struct btf *mk_hdr_extra_btf(void)
+{
+ struct btf *btf, *ext_btf = NULL;
+ const __u32 extra_sz = 4;
+ __u32 hdr_len, sz;
+ const void *raw;
+ char *ext;
+
+ btf = mk_btf();
+ if (!btf)
+ return NULL;
+
+ raw = btf__raw_data(btf, &sz);
+ if (!ASSERT_OK_PTR(raw, "raw_data"))
+ goto out;
+
+ hdr_len = ((const struct btf_header *)raw)->hdr_len;
+
+ ext = calloc(1, sz + extra_sz);
+ if (!ASSERT_OK_PTR(ext, "alloc_ext"))
+ goto out;
+
+ memcpy(ext, raw, hdr_len);
+ ext[hdr_len] = 0xA5; /* any nonzero value */
+ memcpy(ext + hdr_len + extra_sz, raw + hdr_len, sz - hdr_len);
+ ((struct btf_header *)ext)->hdr_len = hdr_len + extra_sz;
+
+ ext_btf = btf__new(ext, sz + extra_sz);
+ ASSERT_OK_PTR(ext_btf, "btf_new_ext");
+ free(ext);
+out:
+ btf__free(btf);
+ return ext_btf;
+}
+
static struct btf *mk_loc_btf(void)
{
struct btf *btf;
@@ -283,6 +319,12 @@ void test_bpftool_btf_dump(void)
unlink(path);
}
+ if (!btf_to_tmpfile(mk_hdr_extra_btf, path)) {
+ if (test__start_subtest("hdr_extra"))
+ test_dump(path, true);
+ unlink(path);
+ }
+
if (!btf_to_tmpfile(mk_loc_btf, path)) {
if (test__start_subtest("loc_dump"))
test_loc_dump(path);
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 06/17] libbpf: Let decl tags override how btf_dump renders type tags
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (4 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 05/17] selftests/bpf: Cover unknown BTF header-extension bytes in bpftool C dumps Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-06 0:24 ` Andrii Nakryiko
2026-11-05 17:58 ` [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is Ihor Solodrai
` (11 subsequent siblings)
17 siblings, 1 reply; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
A tool that dumps C from kernel BTF may need a type tag printed
differently than btf_dump prints it by default, for example as a macro
that the generated header defines. The tool can't change the tag
itself: kernel BTF must keep the encoding the kernel expects, and if
the dumped BTF is the base of a split, a type in the base can't refer
to a type added to the split.
A decl tag annotates another type by referencing it, so a split can
attach one to a type tag of its base.
Let such decl tags override the rendering of the type tag: when a type
tag has decl tags with component_idx -1, print them in its place, the
same way as decl tags on declarations. The tool that builds the dumped
BTF then decides how each type tag is rendered.
The kernel rejects decl tags on type tags, so kernel BTF cannot have
any, and its C dumps are unchanged.
Document this in btf.rst.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
Documentation/bpf/btf.rst | 10 ++++++++++
tools/lib/bpf/btf_dump.c | 12 ++++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
index 29de1222c3e7..ea0d699749af 100644
--- a/Documentation/bpf/btf.rst
+++ b/Documentation/bpf/btf.rst
@@ -511,6 +511,9 @@ representing the attribute-list of the attribute specifier. For
example, for an ``__attribute__((aligned(4)))`` the string's contents
is ``aligned(4)``.
+``btf_dump`` also accepts decl tags that reference a ``type_tag``, as
+described under ``BTF_KIND_TYPE_TAG``.
+
2.2.18 BTF_KIND_TYPE_TAG
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -542,6 +545,13 @@ If ``info.kind_flag`` is 1, then the type tag represents an arbitrary
__attribute__, and the ``name_off`` encodes a string representing the
attribute-list of the attribute specifier.
+``btf_dump`` prints decl tags that reference a type tag, with
+``btf_decl_tag.component_idx`` of ``-1``, in place of that type tag. They
+are printed the same way as decl tags on declarations. This lets a split
+BTF built for a C dump change how the type tags of its base BTF are
+printed, without modifying the base. The kernel rejects decl tags that
+reference type tags.
+
2.2.19 BTF_KIND_ENUM64
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index ad43bbd3b442..c12c3c248407 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -1033,10 +1033,11 @@ static void btf_dump_emit_decl_tag(struct btf_dump *d, const struct btf_type *t)
* tags of one type form a run that binary search finds the start of, in a
* fixed order so that the same BTF always renders the same C.
*/
-static void btf_dump_emit_decl_tags(struct btf_dump *d, __u32 id, int comp_idx)
+static int btf_dump_emit_decl_tags(struct btf_dump *d, __u32 id, int comp_idx)
{
size_t i, lo = 0, hi = d->decl_tag_cnt, mid;
const struct btf_type *t;
+ int cnt = 0;
/* lower bound on target_id */
while (lo < hi) {
@@ -1051,9 +1052,13 @@ static void btf_dump_emit_decl_tags(struct btf_dump *d, __u32 id, int comp_idx)
for (i = lo; i < d->decl_tag_cnt && d->decl_tags[i].target_id == id; i++) {
t = btf__type_by_id(d->btf, d->decl_tags[i].tag_id);
- if (btf_decl_tag(t)->component_idx == comp_idx)
+ if (btf_decl_tag(t)->component_idx == comp_idx) {
btf_dump_emit_decl_tag(d, t);
+ cnt++;
+ }
}
+
+ return cnt;
}
static void btf_dump_emit_struct_def(struct btf_dump *d,
@@ -1592,6 +1597,9 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
break;
case BTF_KIND_TYPE_TAG:
btf_dump_emit_mods(d, decls);
+ /* decl tags override the type tag, see btf.rst */
+ if (btf_dump_emit_decl_tags(d, id, -1))
+ break;
name = btf_name_of(d, t->name_off);
if (btf_kflag(t))
btf_dump_printf(d, " __attribute__((%s))", name);
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (5 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 06/17] libbpf: Let decl tags override how btf_dump renders type tags Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 18:16 ` sashiko-bot
2026-11-06 0:25 ` Andrii Nakryiko
2026-11-05 17:58 ` [PATCH bpf-next v4 08/17] selftests/bpf: Cover type tag rendering overrides in btf_dump Ihor Solodrai
` (10 subsequent siblings)
17 siblings, 2 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
btf_dump wraps the strings of kflagged decl and type tags in an
attribute specifier, so such a tag can only stand for an __attribute__.
Print the strings as is instead. A tag can then carry any annotation,
including an identifier whose definition is left to the consumer of the
dump, such as a macro defined by a generated header.
resolve_btfids, the only in-tree producer of kflagged tags in kernel
BTF, tags arena pointers in kfunc prototypes with "address_space(1)".
Make it encode the complete specifier, __attribute__((address_space(1))).
Kernel BTF produced before this change carries the bare string, which
does not compile when printed as is. pahole emitted it from
commit 21cb33c7e065 ("kbuild, bpf: Enable --btf_features=attributes"),
and resolve_btfids from commit 27a78c2e7eee ("resolve_btfids: Process
KF_ARENA_* flags in resolve_btfids").
In the split BTF that bpftool dumps C through, attach a kflagged decl
tag with the complete specifier to each such type tag, which btf_dump
prints in its place. The input stays unmodified, and module BTF, whose
base holds the vmlinux arena kfuncs, is handled the same way.
A C dump of new kernel BTF by an older bpftool, built with libbpf 1.6
or later, nests the attribute specifier in the arena kfunc prototypes,
which does not compile.
Update the BTF reference, kfunc documentation, libbpf API comments and
existing tests to match.
Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
Documentation/bpf/btf.rst | 14 +--
Documentation/bpf/kfuncs.rst | 3 +-
tools/bpf/bpftool/btf.c | 43 +++++++++
tools/bpf/resolve_btfids/main.c | 2 +-
tools/lib/bpf/btf.c | 6 +-
tools/lib/bpf/btf_dump.c | 4 +-
.../selftests/bpf/prog_tests/btf_dump.c | 89 +++++++++++--------
.../selftests/bpf/prog_tests/resolve_btfids.c | 4 +-
8 files changed, 112 insertions(+), 53 deletions(-)
diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
index ea0d699749af..99833048de60 100644
--- a/Documentation/bpf/btf.rst
+++ b/Documentation/bpf/btf.rst
@@ -506,10 +506,11 @@ If ``info.kind_flag`` is 0, then this is a normal decl tag, and the
``name_off`` encodes btf_decl_tag attribute string.
If ``info.kind_flag`` is 1, then the decl tag represents an arbitrary
-__attribute__. In this case, ``name_off`` encodes a string
-representing the attribute-list of the attribute specifier. For
-example, for an ``__attribute__((aligned(4)))`` the string's contents
-is ``aligned(4)``.
+annotation. ``name_off`` encodes the complete annotation string, which
+``btf_dump`` prints unchanged at the declaration. For example, an attribute
+specifier is encoded as ``__attribute__((aligned(4)))``, including the
+wrapper. A macro can be encoded as its identifier, leaving its definition
+to the generated header or its user.
``btf_dump`` also accepts decl tags that reference a ``type_tag``, as
described under ``BTF_KIND_TYPE_TAG``.
@@ -542,8 +543,9 @@ normal type tag, and the ``name_off`` encodes btf_type_tag attribute
string.
If ``info.kind_flag`` is 1, then the type tag represents an arbitrary
-__attribute__, and the ``name_off`` encodes a string representing the
-attribute-list of the attribute specifier.
+annotation. As with decl tags, ``name_off`` encodes the complete annotation
+string, which ``btf_dump`` prints unchanged. An attribute specifier must
+include its wrapper, for example ``__attribute__((address_space(1)))``.
``btf_dump`` prints decl tags that reference a type tag, with
``btf_decl_tag.component_idx`` of ``-1``, in place of that type tag. They
diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 901c37504217..87b3c4e4bea2 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -567,7 +567,8 @@ At kernel build time the ``resolve_btfids`` tool finds all kfuncs declared with
For each kfunc it 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 that use arena pointers (see
-sections 2.3.8 and 2.8).
+sections 2.3.8 and 2.8). The type tag stores the complete
+``__attribute__((address_space(1)))`` specifier with ``kind_flag`` set.
2.7 Specifying no-cast aliases with ___init
--------------------------------------------
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index a438b5a03f5a..b4516361a3fe 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -1092,6 +1092,45 @@ static struct sort_datum *sort_btf_c(const struct btf *btf)
return datums;
}
+/*
+ * Kernel BTF keeps its type tags as the kernel expects them. Decl tags in
+ * the dump split choose how btf_dump prints them.
+ */
+static const struct {
+ bool kflag;
+ const char *name;
+ const char *override;
+} btf_c_type_tags[] = {
+ /* the arena attribute in older kernel BTF */
+ { true, "address_space(1)", "__attribute__((address_space(1)))" },
+};
+
+static int btf_c_annotate_type_tags(struct btf *btf)
+{
+ __u32 i, cnt = btf__type_cnt(btf);
+ size_t j;
+ int err;
+
+ for (i = 1; i < cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(btf, i);
+ const char *name;
+
+ if (!btf_is_type_tag(t))
+ continue;
+ name = btf__name_by_offset(btf, t->name_off);
+ for (j = 0; j < ARRAY_SIZE(btf_c_type_tags); j++) {
+ if (btf_kflag(t) != btf_c_type_tags[j].kflag ||
+ strcmp(name, btf_c_type_tags[j].name))
+ continue;
+ err = btf__add_decl_attr(btf, btf_c_type_tags[j].override, i, -1);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static int dump_btf_c(struct btf *btf,
__u32 *root_type_ids, int root_type_cnt, bool sort_dump)
{
@@ -1110,6 +1149,10 @@ static int dump_btf_c(struct btf *btf,
if (ptr_sz)
btf__set_pointer_size(split, ptr_sz);
+ err = btf_c_annotate_type_tags(split);
+ if (err)
+ goto done;
+
d = btf_dump__new(split, btf_dump_printf, NULL, NULL);
if (!d) {
err = -errno;
diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 5ad547356303..c16dd93fa6d4 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -204,7 +204,7 @@ struct btf_links {
#define KF_ARENA_ARG2 (1 << 15)
#define KF_IMPLICIT_ARGS (1 << 16)
#define KF_IMPL_SUFFIX "_impl"
-#define TYPE_ATTR_ARENA "address_space(1)"
+#define TYPE_ATTR_ARENA "__attribute__((address_space(1)))"
#define PARAM_SUFFIX_ARENA "__arena"
#define PARAM_SUFFIX_ARENA_NULLABLE "__arena__nullable"
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 5786232c1fea..0984a8ccb526 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -3078,7 +3078,8 @@ int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id)
* Append new BTF_KIND_TYPE_TAG type with:
* - *value*, non-empty/non-NULL tag value;
* - *ref_type_id* - referenced type ID, it might not exist yet;
- * Set info->kflag to 1, indicating this tag is an __attribute__
+ * The kind flag is set; btf_dump prints *value* unchanged.
+ * Attribute specifiers must include the __attribute__((...)) wrapper.
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
@@ -3413,7 +3414,8 @@ int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
* - *ref_type_id* - referenced type ID, it might not exist yet;
* - *component_idx* - -1 for tagging reference type, otherwise struct/union
* member or function argument index;
- * Set info->kflag to 1, indicating this tag is an __attribute__
+ * The kind flag is set; btf_dump prints *value* unchanged.
+ * Attribute specifiers must include the __attribute__((...)) wrapper.
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index c12c3c248407..fcd97aa90589 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -1023,7 +1023,7 @@ static void btf_dump_emit_decl_tag(struct btf_dump *d, const struct btf_type *t)
const char *name = btf_name_of(d, t->name_off);
if (btf_kflag(t))
- btf_dump_printf(d, " __attribute__((%s))", name);
+ btf_dump_printf(d, " %s", name);
else
btf_dump_printf(d, " __attribute__((btf_decl_tag(\"%s\")))", name);
}
@@ -1602,7 +1602,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
break;
name = btf_name_of(d, t->name_off);
if (btf_kflag(t))
- btf_dump_printf(d, " __attribute__((%s))", name);
+ btf_dump_printf(d, " %s", name);
else
btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name);
break;
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index 5a5ee001ac8b..ee8ccefc2a45 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -320,11 +320,12 @@ static void test_btf_dump_incremental(void)
" Y___2 = 1,\n"
" } y;\n"
" struct s s;\n"
-"} __attribute__((round2));\n\n", "c_dump1");
+"} round2;\n\n", "c_dump1");
test_ctx__free(&t);
}
+/* Bare annotation names in the tag tests stand for user-defined macros. */
static void test_btf_dump_decl_tags(void)
{
struct test_ctx t = {};
@@ -339,14 +340,14 @@ static void test_btf_dump_decl_tags(void)
/*
* Generate BTF corresponding to the following C code:
*
- * struct s1 { int f; } __attribute__((attr1))
+ * struct s1 { int f; } __attribute__((aligned(4), packed))
* __attribute__((btf_decl_tag("plain_tag")));
- * typedef struct { int f; } __attribute__((a1)) __attribute__((a2)) t1;
- * union u1 { int f; } __attribute__((uattr));
- * struct p1 { char c; int i; } __attribute__((packed)) __attribute__((pattr));
- * struct outer { struct { int g; } __attribute__((inner)) in; };
- * struct s2 { int f; } __attribute__((on_s2));
- * struct s3 { int f; } __attribute__((on_s3));
+ * typedef struct { int f; } a1 a2 t1;
+ * union u1 { int f; } uattr;
+ * struct p1 { char c; int i; } __attribute__((packed)) pattr;
+ * struct outer { struct { int g; } inner in; };
+ * struct s2 { int f; } on_s2;
+ * struct s3 { int f; } on_s3;
*
* plus attributes on a var and a func, which btf_dump does not declare.
*/
@@ -355,12 +356,13 @@ static void test_btf_dump_decl_tags(void)
id = btf__add_int(btf, "char", 1, BTF_INT_CHAR);
ASSERT_EQ(id, 2, "char_id");
- /* a named record with one attribute of each form */
+ /* a complete attribute specifier next to a normal decl tag */
id = btf__add_struct(btf, "s1", 4);
ASSERT_EQ(id, 3, "s1_id");
ASSERT_OK(btf__add_field(btf, "f", 1, 0, 0), "s1_field");
- ASSERT_EQ(btf__add_decl_attr(btf, "attr1", 3, -1), 4, "s1_attr");
- /* no kflag: rendered as a btf_decl_tag(), not as a bare attribute */
+ ASSERT_EQ(btf__add_decl_attr(btf, "__attribute__((aligned(4), packed))", 3, -1),
+ 4, "s1_attr");
+ /* no kflag: rendered as a btf_decl_tag() */
ASSERT_EQ(btf__add_decl_tag(btf, "plain_tag", 3, -1), 5, "s1_plain_tag");
/*
@@ -428,34 +430,34 @@ static void test_btf_dump_decl_tags(void)
test_ctx__dump_and_compare(&t,
"struct s1 {\n"
" int f;\n"
-"} __attribute__((attr1)) __attribute__((btf_decl_tag(\"plain_tag\")));\n"
+"} __attribute__((aligned(4), packed)) __attribute__((btf_decl_tag(\"plain_tag\")));\n"
"\n"
"typedef struct {\n"
" int f;\n"
-"} __attribute__((a1)) __attribute__((a2)) t1;\n"
+"} a1 a2 t1;\n"
"\n"
"union u1 {\n"
" int f;\n"
-"} __attribute__((uattr));\n"
+"} uattr;\n"
"\n"
"struct p1 {\n"
" char c;\n"
" int i;\n"
-"} __attribute__((packed)) __attribute__((pattr));\n"
+"} __attribute__((packed)) pattr;\n"
"\n"
"struct outer {\n"
" struct {\n"
" int g;\n"
-" } __attribute__((inner)) in;\n"
+" } inner in;\n"
"};\n"
"\n"
"struct s2 {\n"
" int f;\n"
-"} __attribute__((on_s2));\n"
+"} on_s2;\n"
"\n"
"struct s3 {\n"
" int f;\n"
-"} __attribute__((on_s3));\n"
+"} on_s3;\n"
"\n", "dump_and_compare");
test_ctx__free(&t);
@@ -481,12 +483,12 @@ static void test_btf_dump_decl_tags_members(void)
* Generate BTF corresponding to the following C code:
*
* struct m1 {
- * int (*fp)(void) __attribute__((fp_attr));
+ * int (*fp)(void) fp_attr;
* int a;
- * int b __attribute__((b1)) __attribute__((b2));
- * int arr[4] __attribute__((arr_attr));
- * } __attribute__((rec));
- * struct bits { int x : 3 __attribute__((x_attr)); int y : 29; };
+ * int b b1 b2;
+ * int arr[4] arr_attr;
+ * } rec;
+ * struct bits { int x : 3 x_attr; int y : 29; };
*/
id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
ASSERT_EQ(id, 1, "int_id");
@@ -520,14 +522,14 @@ static void test_btf_dump_decl_tags_members(void)
test_ctx__dump_and_compare(&t,
"struct m1 {\n"
-" int (*fp)(void) __attribute__((fp_attr));\n"
+" int (*fp)(void) fp_attr;\n"
" int a;\n"
-" int b __attribute__((b1)) __attribute__((b2));\n"
-" int arr[4] __attribute__((arr_attr));\n"
-"} __attribute__((rec));\n"
+" int b b1 b2;\n"
+" int arr[4] arr_attr;\n"
+"} rec;\n"
"\n"
"struct bits {\n"
-" int x: 3 __attribute__((x_attr));\n"
+" int x: 3 x_attr;\n"
" int y: 29;\n"
"};\n"
"\n", "dump_and_compare");
@@ -554,18 +556,18 @@ static void test_btf_dump_decl_tags_typedef(void)
/*
* Generate BTF corresponding to the following C code:
*
- * typedef int td __attribute__((t1))
+ * typedef int td t1
* __attribute__((btf_decl_tag("t2")));
* typedef struct {
- * int f __attribute__((memb_attr));
- * } __attribute__((rec_attr)) both __attribute__((both_attr));
+ * int f memb_attr;
+ * } rec_attr both both_attr;
*/
id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
ASSERT_EQ(id, 1, "int_id");
id = btf__add_typedef(btf, "td", 1);
ASSERT_EQ(id, 2, "td_id");
ASSERT_EQ(btf__add_decl_attr(btf, "t1", 2, -1), 3, "td_attr1");
- /* no kflag: rendered as a btf_decl_tag(), not as a bare attribute */
+ /* no kflag: rendered as a btf_decl_tag() */
ASSERT_EQ(btf__add_decl_tag(btf, "t2", 2, -1), 4, "td_attr2");
id = btf__add_struct(btf, NULL, 4);
@@ -578,11 +580,11 @@ static void test_btf_dump_decl_tags_typedef(void)
ASSERT_EQ(btf__add_decl_attr(btf, "both_attr", 8, -1), 9, "both_attr");
test_ctx__dump_and_compare(&t,
-"typedef int td __attribute__((t1)) __attribute__((btf_decl_tag(\"t2\")));\n"
+"typedef int td t1 __attribute__((btf_decl_tag(\"t2\")));\n"
"\n"
"typedef struct {\n"
-" int f __attribute__((memb_attr));\n"
-"} __attribute__((rec_attr)) both __attribute__((both_attr));\n"
+" int f memb_attr;\n"
+"} rec_attr both both_attr;\n"
"\n", "dump_and_compare");
test_ctx__free(&t);
@@ -603,7 +605,8 @@ static void test_btf_dump_type_tags(void)
*
* struct s {
* void __attribute__((btf_type_tag(\"void_tag\"))) *p1;
- * void __attribute__((void_attr)) *p2;
+ * void __attribute__((aligned(8))) *p2;
+ * void __type_marker *p3;
* };
*
*/
@@ -613,22 +616,30 @@ static void test_btf_dump_type_tags(void)
id = btf__add_ptr(btf, id);
ASSERT_EQ(id, 2, "void_ptr_id1");
- id = btf__add_type_attr(btf, "void_attr", 0);
+ id = btf__add_type_attr(btf, "__attribute__((aligned(8)))", 0);
ASSERT_EQ(id, 3, "type_attr_id");
id = btf__add_ptr(btf, id);
ASSERT_EQ(id, 4, "void_ptr_id2");
+ id = btf__add_type_attr(btf, "__type_marker", 0);
+ ASSERT_EQ(id, 5, "type_marker_id");
+ id = btf__add_ptr(btf, id);
+ ASSERT_EQ(id, 6, "void_ptr_id3");
+
id = btf__add_struct(btf, "s", 8);
- ASSERT_EQ(id, 5, "struct_id");
+ ASSERT_EQ(id, 7, "struct_id");
err = btf__add_field(btf, "p1", 2, 0, 0);
ASSERT_OK(err, "field_ok1");
err = btf__add_field(btf, "p2", 4, 0, 0);
ASSERT_OK(err, "field_ok2");
+ err = btf__add_field(btf, "p3", 6, 0, 0);
+ ASSERT_OK(err, "field_ok3");
test_ctx__dump_and_compare(&t,
"struct s {\n"
" void __attribute__((btf_type_tag(\"void_tag\"))) *p1;\n"
-" void __attribute__((void_attr)) *p2;\n"
+" void __attribute__((aligned(8))) *p2;\n"
+" void __type_marker *p3;\n"
"};\n\n", "dump_and_compare");
test_ctx__free(&t);
diff --git a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
index 3f9949e8227d..e96b209f3fe0 100644
--- a/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
+++ b/tools/testing/selftests/bpf/prog_tests/resolve_btfids.c
@@ -14,7 +14,7 @@
#define DECL_TAG_FASTCALL "bpf_fastcall"
#define DECL_TAG_KFUNC "bpf_kfunc"
-#define TYPE_ATTR_ARENA "address_space(1)"
+#define TYPE_ATTR_ARENA "__attribute__((address_space(1)))"
#define ARENA_ARG(n) (1U << (n))
#ifndef KF_FASTCALL
@@ -239,7 +239,7 @@ static void check_kfunc_set(struct btf_id_set8 *set)
}
}
-/* True if @id is PTR -> TYPE_TAG(kflag=1, "address_space(1)") -> pointee */
+/* True if @id is PTR -> TYPE_TAG(kflag=1, TYPE_ATTR_ARENA) -> pointee */
static bool is_arena_tagged_ptr(struct btf *btf, __u32 id)
{
const struct btf_type *ptr, *tag;
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 08/17] selftests/bpf: Cover type tag rendering overrides in btf_dump
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (6 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 09/17] selftests/bpf: Cover legacy arena type tags in bpftool C dumps Ihor Solodrai
` (9 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
Check that btf_dump prints the kflagged decl tags attached to a plain
and to a kflagged type tag in place of the type tag.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../selftests/bpf/prog_tests/btf_dump.c | 26 ++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index ee8ccefc2a45..bfa74db17987 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -607,8 +607,12 @@ static void test_btf_dump_type_tags(void)
* void __attribute__((btf_type_tag(\"void_tag\"))) *p1;
* void __attribute__((aligned(8))) *p2;
* void __type_marker *p3;
+ * void __plain_repl *p4;
+ * void __kflag_repl *p5;
* };
*
+ * The type tags of p4 and p5 carry kflagged decl tags, printed in their
+ * place.
*/
id = btf__add_type_tag(btf, "void_tag", 0);
@@ -626,20 +630,40 @@ static void test_btf_dump_type_tags(void)
id = btf__add_ptr(btf, id);
ASSERT_EQ(id, 6, "void_ptr_id3");
+ id = btf__add_type_tag(btf, "plain_tag", 0);
+ ASSERT_EQ(id, 7, "plain_tag_id");
+ id = btf__add_decl_attr(btf, "__plain_repl", 7, -1);
+ ASSERT_EQ(id, 8, "plain_repl_id");
+ id = btf__add_ptr(btf, 7);
+ ASSERT_EQ(id, 9, "void_ptr_id4");
+
+ id = btf__add_type_attr(btf, "kflag_tag", 0);
+ ASSERT_EQ(id, 10, "kflag_tag_id");
+ id = btf__add_decl_attr(btf, "__kflag_repl", 10, -1);
+ ASSERT_EQ(id, 11, "kflag_repl_id");
+ id = btf__add_ptr(btf, 10);
+ ASSERT_EQ(id, 12, "void_ptr_id5");
+
id = btf__add_struct(btf, "s", 8);
- ASSERT_EQ(id, 7, "struct_id");
+ ASSERT_EQ(id, 13, "struct_id");
err = btf__add_field(btf, "p1", 2, 0, 0);
ASSERT_OK(err, "field_ok1");
err = btf__add_field(btf, "p2", 4, 0, 0);
ASSERT_OK(err, "field_ok2");
err = btf__add_field(btf, "p3", 6, 0, 0);
ASSERT_OK(err, "field_ok3");
+ err = btf__add_field(btf, "p4", 9, 0, 0);
+ ASSERT_OK(err, "field_ok4");
+ err = btf__add_field(btf, "p5", 12, 0, 0);
+ ASSERT_OK(err, "field_ok5");
test_ctx__dump_and_compare(&t,
"struct s {\n"
" void __attribute__((btf_type_tag(\"void_tag\"))) *p1;\n"
" void __attribute__((aligned(8))) *p2;\n"
" void __type_marker *p3;\n"
+" void __plain_repl *p4;\n"
+" void __kflag_repl *p5;\n"
"};\n\n", "dump_and_compare");
test_ctx__free(&t);
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 09/17] selftests/bpf: Cover legacy arena type tags in bpftool C dumps
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (7 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 08/17] selftests/bpf: Cover type tag rendering overrides in btf_dump Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 10/17] bpftool: Apply __bpf_relocatable per record on BTF dump Ihor Solodrai
` (8 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
Older kernel BTF tags arena pointers in kfunc prototypes with a kflagged
"address_space(1)" type tag. bpftool prints such tags as the complete
attribute specifier.
Add bpftool_btf_dump subtests that dump a minimal BTF with an arena
kfunc in the legacy encoding, alone and as the base of a split BTF with
an arena kfunc of its own, dumped with -B as module BTF is. Check that
the C prototypes carry the complete specifier.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../bpf/prog_tests/bpftool_btf_dump.c | 106 +++++++++++++++++-
1 file changed, 102 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
index 0cfff1b96666..a0f801fcaa47 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -93,6 +93,58 @@ static struct btf *mk_hdr_extra_btf(void)
return ext_btf;
}
+/*
+ * Older kernel BTF encodes the arena attribute as "address_space(1)":
+ *
+ * int __attribute__((address_space(1))) *arena(void);
+ */
+static struct btf *mk_legacy_arena_btf(void)
+{
+ struct btf *btf;
+
+ btf = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf, "new_empty"))
+ return NULL;
+
+ btf__add_int(btf, "int", 4, BTF_INT_SIGNED);
+ btf__add_type_attr(btf, "address_space(1)", 1);
+ btf__add_ptr(btf, 2);
+ btf__add_func_proto(btf, 3);
+ btf__add_func(btf, "arena", BTF_FUNC_GLOBAL, 4);
+ btf__add_decl_tag(btf, "bpf_kfunc", 5, -1);
+
+ VALIDATE_RAW_BTF(
+ btf,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] TYPE_TAG 'address_space(1)' type_id=1 kflag=1",
+ "[3] PTR '(anon)' type_id=2",
+ "[4] FUNC_PROTO '(anon)' ret_type_id=3 vlen=0",
+ "[5] FUNC 'arena' type_id=4 linkage=global",
+ "[6] DECL_TAG 'bpf_kfunc' type_id=5 component_idx=-1");
+
+ return btf;
+}
+
+static struct btf *legacy_arena_base;
+
+/* Module BTF over legacy_arena_base, with an arena kfunc of its own */
+static struct btf *mk_legacy_arena_split_btf(void)
+{
+ struct btf *btf;
+
+ btf = btf__new_empty_split(legacy_arena_base);
+ if (!ASSERT_OK_PTR(btf, "new_empty_split"))
+ return NULL;
+
+ btf__add_type_attr(btf, "address_space(1)", 1);
+ btf__add_ptr(btf, 7);
+ btf__add_func_proto(btf, 8);
+ btf__add_func(btf, "arena_mod", BTF_FUNC_GLOBAL, 9);
+ btf__add_decl_tag(btf, "bpf_kfunc", 10, -1);
+
+ return btf;
+}
+
static struct btf *mk_loc_btf(void)
{
struct btf *btf;
@@ -180,7 +232,7 @@ static int btf_to_tmpfile(struct btf *(*mk)(void), char *path)
return err;
}
-static char *dump_c(const char *btf_path, bool sorted)
+static char *dump_c(const char *btf_path, const char *base_path, bool sorted)
{
char args[MAX_BPFTOOL_CMD_LEN];
char *buf;
@@ -190,8 +242,8 @@ static char *dump_c(const char *btf_path, bool sorted)
if (!ASSERT_OK_PTR(buf, "alloc_dump"))
return NULL;
- snprintf(args, sizeof(args), "btf dump file %s format c%s",
- btf_path, sorted ? "" : " unsorted");
+ snprintf(args, sizeof(args), "%s%s btf dump file %s format c%s",
+ base_path ? "-B " : "", base_path ?: "", btf_path, sorted ? "" : " unsorted");
err = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);
if (!ASSERT_OK(err, "btf_dump_format_c")) {
@@ -255,7 +307,7 @@ static void test_dump(const char *btf_path, bool sorted)
exp_path = sorted ? EXPECTED_SORTED : EXPECTED_UNSORTED;
- dump = dump_c(btf_path, sorted);
+ dump = dump_c(btf_path, NULL, sorted);
if (!dump)
return;
@@ -270,6 +322,44 @@ static void test_dump(const char *btf_path, bool sorted)
free(dump);
}
+static void test_legacy_arena(const char *btf_path)
+{
+ char *dump;
+
+ dump = dump_c(btf_path, NULL, true);
+ if (!dump)
+ return;
+
+ ASSERT_HAS_SUBSTR(dump, "int __attribute__((address_space(1))) *arena(void)",
+ "arena_kfunc");
+ free(dump);
+}
+
+static void test_legacy_arena_split(const char *base_path)
+{
+ char path[PATH_MAX];
+ char *dump;
+
+ legacy_arena_base = btf__parse(base_path, NULL);
+ if (!ASSERT_OK_PTR(legacy_arena_base, "parse_base"))
+ return;
+ if (btf_to_tmpfile(mk_legacy_arena_split_btf, path))
+ goto out;
+
+ dump = dump_c(path, base_path, true);
+ unlink(path);
+ if (!dump)
+ goto out;
+
+ ASSERT_HAS_SUBSTR(dump, "int __attribute__((address_space(1))) *arena(void)",
+ "base_arena_kfunc");
+ ASSERT_HAS_SUBSTR(dump, "int __attribute__((address_space(1))) *arena_mod(void)",
+ "split_arena_kfunc");
+ free(dump);
+out:
+ btf__free(legacy_arena_base);
+}
+
static void test_loc_dump(const char *btf_path)
{
const char expected[] =
@@ -325,6 +415,14 @@ void test_bpftool_btf_dump(void)
unlink(path);
}
+ if (!btf_to_tmpfile(mk_legacy_arena_btf, path)) {
+ if (test__start_subtest("legacy_arena"))
+ test_legacy_arena(path);
+ if (test__start_subtest("legacy_arena_split"))
+ test_legacy_arena_split(path);
+ unlink(path);
+ }
+
if (!btf_to_tmpfile(mk_loc_btf, path)) {
if (test__start_subtest("loc_dump"))
test_loc_dump(path);
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 10/17] bpftool: Apply __bpf_relocatable per record on BTF dump
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (8 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 09/17] selftests/bpf: Cover legacy arena type tags in bpftool C dumps Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 18:41 ` bot+bpf-ci
2026-11-05 17:58 ` [PATCH bpf-next v4 11/17] bpftool: Skip __bpf_relocatable on verifier-rewritten context types Ihor Solodrai
` (7 subsequent siblings)
17 siblings, 1 reply; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
bpftool wraps the C dump in a clang attribute pragma that applies
preserve_access_index to every struct and union, so field accesses
through vmlinux.h types get CO-RE relocations. GCC-BPF supports the
attribute but not the pragma, so it gets no CO-RE from the header.
Jose proposed replacing the Clang pragmas with per-record attributes
in May 2024 [1]. The declaration attribute support in btf_dump [2] now
provides the per-type control discussed in that thread.
Apply the attribute to each struct and union definition instead, as
kflagged decl tags rendered by btf_dump. Add the tags to the split BTF
that bpftool dumps through. The tags use the __bpf_relocatable macro,
which by default expands to __attribute__((preserve_access_index)) if
the compiler supports it.
Programs built with Clang are unchanged.
GCC-BPF programs can now get CO-RE relocations from vmlinux.h. The
selftests GCC-BPF build defines BPF_NO_PRESERVE_ACCESS_INDEX, so it is
not affected yet.
[1]: https://lore.kernel.org/bpf/20240503111836.25275-1-jose.marchesi@oracle.com/
[2]: https://lore.kernel.org/bpf/20260929234608.48503-1-ihor.solodrai@linux.dev/
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/bpf/bpftool/btf.c | 34 +++++++++++++++----
.../bpf/bpftool_btf_dump_sorted.expected | 16 ++++-----
.../bpf/bpftool_btf_dump_unsorted.expected | 16 ++++-----
3 files changed, 44 insertions(+), 22 deletions(-)
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index b4516361a3fe..aa4290d5cabc 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -1131,6 +1131,24 @@ static int btf_c_annotate_type_tags(struct btf *btf)
return 0;
}
+static int btf_c_annotate_records(struct btf *btf)
+{
+ __u32 i, cnt = btf__type_cnt(btf);
+ int err;
+
+ for (i = 1; i < cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(btf, i);
+
+ if (!btf_is_composite(t))
+ continue;
+ err = btf__add_decl_attr(btf, "__bpf_relocatable", i, -1);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
static int dump_btf_c(struct btf *btf,
__u32 *root_type_ids, int root_type_cnt, bool sort_dump)
{
@@ -1153,6 +1171,10 @@ static int dump_btf_c(struct btf *btf,
if (err)
goto done;
+ err = btf_c_annotate_records(split);
+ if (err)
+ goto done;
+
d = btf_dump__new(split, btf_dump_printf, NULL, NULL);
if (!d) {
err = -errno;
@@ -1162,8 +1184,12 @@ static int dump_btf_c(struct btf *btf,
printf("#ifndef __VMLINUX_H__\n");
printf("#define __VMLINUX_H__\n");
printf("\n");
- printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
- printf("#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n");
+ printf("#ifndef __bpf_relocatable\n");
+ printf("#if !defined(BPF_NO_PRESERVE_ACCESS_INDEX) && __has_attribute(preserve_access_index)\n");
+ printf("#define __bpf_relocatable __attribute__((preserve_access_index))\n");
+ printf("#else\n");
+ printf("#define __bpf_relocatable\n");
+ printf("#endif\n");
printf("#endif\n\n");
printf("#ifndef __ksym\n");
printf("#define __ksym __attribute__((section(\".ksyms\")))\n");
@@ -1207,10 +1233,6 @@ static int dump_btf_c(struct btf *btf,
goto done;
}
- printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
- printf("#pragma clang attribute pop\n");
- printf("#endif\n");
- printf("\n");
printf("#endif /* __VMLINUX_H__ */\n");
done:
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
index 5470b2b43229..f2389e3dceed 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
@@ -1,8 +1,12 @@
#ifndef __VMLINUX_H__
#define __VMLINUX_H__
-#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
-#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
+#ifndef __bpf_relocatable
+#if !defined(BPF_NO_PRESERVE_ACCESS_INDEX) && __has_attribute(preserve_access_index)
+#define __bpf_relocatable __attribute__((preserve_access_index))
+#else
+#define __bpf_relocatable
+#endif
#endif
#ifndef __ksym
@@ -30,19 +34,15 @@ struct holey {
long: 32;
long: 32;
int tail;
-};
+} __bpf_relocatable;
struct s {
int f;
-};
+} __bpf_relocatable;
/* BPF kfuncs */
#ifndef BPF_NO_KFUNC_PROTOTYPES
#endif
-#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
-#pragma clang attribute pop
-#endif
-
#endif /* __VMLINUX_H__ */
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
index 4310adc2ed51..549045677a45 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
@@ -1,8 +1,12 @@
#ifndef __VMLINUX_H__
#define __VMLINUX_H__
-#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
-#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
+#ifndef __bpf_relocatable
+#if !defined(BPF_NO_PRESERVE_ACCESS_INDEX) && __has_attribute(preserve_access_index)
+#define __bpf_relocatable __attribute__((preserve_access_index))
+#else
+#define __bpf_relocatable
+#endif
#endif
#ifndef __ksym
@@ -26,7 +30,7 @@ struct holey {
long: 32;
long: 32;
int tail;
-};
+} __bpf_relocatable;
enum {
E0 = 1,
@@ -34,15 +38,11 @@ enum {
struct s {
int f;
-};
+} __bpf_relocatable;
/* BPF kfuncs */
#ifndef BPF_NO_KFUNC_PROTOTYPES
#endif
-#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
-#pragma clang attribute pop
-#endif
-
#endif /* __VMLINUX_H__ */
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 11/17] bpftool: Skip __bpf_relocatable on verifier-rewritten context types
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (9 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 10/17] bpftool: Apply __bpf_relocatable per record on BTF dump Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 12/17] bpftool: Emit __bpf_static_offset " Ihor Solodrai
` (6 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
The verifier translates accesses to UAPI context and socket types,
such as __sk_buff and bpf_sock, into accesses to kernel structures.
It picks the kernel field by the instruction's offset and keeps the
base register. For example, in bpf_convert_ctx_access() [1]:
switch (si->off) {
...
case offsetof(struct __sk_buff, mark):
...
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
bpf_target_off(struct sk_buff, mark,
4, target_size));
So these accesses require a load or store with an immediate offset
from an unmodified pointer. Their UAPI field offsets are fixed, so
CO-RE relocation is unnecessary. With preserve_access_index, the
compiler can instead emit pointer arithmetic, for example for a
one-byte load at offset 1 into skb->mark. The verifier then rejects an
otherwise valid program.
Omit the attribute from the 13 such types when dumping C. Also omit it
from their nested anonymous records: keeping it there can root
relocations at an unnamed type, which libbpf cannot resolve.
The last addition to this set was bpf_sk_lookup, introduced in 2020 by
commit e9ddbb7707ff ("bpf: Introduce SK_LOOKUP program type with a
dedicated attach point"). Keep the set hardcoded in bpftool so the
exclusion also works with older kernels.
[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/filter.c?h=v7.2#n9939
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/bpf/bpftool/btf.c | 77 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 75 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index aa4290d5cabc..d913723a691a 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -1131,22 +1131,95 @@ static int btf_c_annotate_type_tags(struct btf *btf)
return 0;
}
+/*
+ * The verifier translates field accesses of some types into accesses
+ * to kernel structures, see bpf_convert_ctx_accesses().
+ * The translation keys on the immediate offset of each load or store,
+ * so offsets into these types must stay immediates.
+ */
+static const char * const btf_c_static_offset_types[] = {
+ "__sk_buff",
+ "bpf_perf_event_data",
+ "bpf_sk_lookup",
+ "bpf_sock",
+ "bpf_sock_addr",
+ "bpf_sock_ops",
+ "bpf_sockopt",
+ "bpf_sysctl",
+ "bpf_tcp_sock",
+ "bpf_xdp_sock",
+ "sk_msg_md",
+ "sk_reuseport_md",
+ "xdp_md",
+};
+
+static bool btf_c_needs_static_offset(const struct btf *btf, const struct btf_type *t)
+{
+ const char *name;
+ size_t i;
+
+ if (!btf_is_struct(t))
+ return false;
+
+ name = btf__name_by_offset(btf, t->name_off);
+ for (i = 0; i < ARRAY_SIZE(btf_c_static_offset_types); i++) {
+ if (!strcmp(name, btf_c_static_offset_types[i]))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Anonymous records within types that need static offsets must not start
+ * CO-RE access chains. Named records, typedefs and pointer targets are
+ * separate types.
+ */
+static void btf_c_mark_non_relocatable(const struct btf *btf, __u32 id, bool *non_relocatable)
+{
+ const struct btf_type *t = btf__type_by_id(btf, id);
+ const struct btf_member *m = btf_members(t);
+ __u32 i;
+
+ non_relocatable[id] = true;
+ for (i = 0; i < btf_vlen(t); i++, m++) {
+ const struct btf_type *mt = btf__type_by_id(btf, m->type);
+
+ if (btf_is_composite(mt) && !mt->name_off && !non_relocatable[m->type])
+ btf_c_mark_non_relocatable(btf, m->type, non_relocatable);
+ }
+}
+
static int btf_c_annotate_records(struct btf *btf)
{
__u32 i, cnt = btf__type_cnt(btf);
+ bool *non_relocatable;
int err;
+ non_relocatable = calloc(cnt, sizeof(*non_relocatable));
+ if (!non_relocatable)
+ return -ENOMEM;
+
+ for (i = 1; i < cnt; i++) {
+ if (btf_c_needs_static_offset(btf, btf__type_by_id(btf, i)))
+ btf_c_mark_non_relocatable(btf, i, non_relocatable);
+ }
+
for (i = 1; i < cnt; i++) {
const struct btf_type *t = btf__type_by_id(btf, i);
if (!btf_is_composite(t))
continue;
+ if (non_relocatable[i])
+ continue;
err = btf__add_decl_attr(btf, "__bpf_relocatable", i, -1);
if (err < 0)
- return err;
+ goto out;
}
- return 0;
+ err = 0;
+out:
+ free(non_relocatable);
+ return err;
}
static int dump_btf_c(struct btf *btf,
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 12/17] bpftool: Emit __bpf_static_offset on verifier-rewritten context types
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (10 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 11/17] bpftool: Skip __bpf_relocatable on verifier-rewritten context types Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 13/17] selftests/bpf: Cover record attributes in bpftool BTF dumps Ihor Solodrai
` (5 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
The verifier translates accesses to some UAPI context and socket types
into accesses to kernel structures. These accesses require an
unmodified base and an immediate field offset.
Leaving preserve_access_index (PAI) off does not stop the compiler
from merging loads at different field offsets into one load through an
adjusted pointer. The verifier rejects the resulting program.
preserve_static_offset (PSO), available since Clang 18, preserves
static field offsets in load and store instruction immediates. Apply
it to these 13 types in vmlinux.h through a __bpf_static_offset macro.
Eduard introduced PSO in Clang and proposed annotating BPF context
types in 2023 [1]. His v3 RFC [2] left pt_regs annotations in
architecture headers unresolved. However, since commit
de6c7d99f898 ("bpf: Relax fixed offset check for PTR_TO_CTX"), program
types without convert_ctx_access() accept fixed offsets from modified
context pointers. pt_regs can now keep PAI, while PSO on
bpf_perf_event_data covers its regs member.
PSO creates no CO-RE relocations, so BPF_NO_PRESERVE_ACCESS_INDEX does
not disable it.
Clang 18 through 23 crash on __builtin_preserve_field_info over a PSO
record, including bpf_core_field_*() and the CO-RE bitfield helpers.
-D__bpf_static_offset= can disable PSO for affected programs. LLVM main
has the fix [3].
[1]: https://lore.kernel.org/bpf/20231208000531.19179-1-eddyz87@gmail.com/
[2]: https://lore.kernel.org/bpf/20231220133411.22978-1-eddyz87@gmail.com/
[3]: https://github.com/llvm/llvm-project/pull/234280
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/bpf/bpftool/btf.c | 16 ++++++++++++++--
.../bpf/bpftool_btf_dump_sorted.expected | 8 ++++++++
.../bpf/bpftool_btf_dump_unsorted.expected | 8 ++++++++
3 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index d913723a691a..1329745c5854 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -1206,12 +1206,17 @@ static int btf_c_annotate_records(struct btf *btf)
for (i = 1; i < cnt; i++) {
const struct btf_type *t = btf__type_by_id(btf, i);
+ const char *attr;
if (!btf_is_composite(t))
continue;
- if (non_relocatable[i])
+ if (!non_relocatable[i])
+ attr = "__bpf_relocatable";
+ else if (btf_c_needs_static_offset(btf, t))
+ attr = "__bpf_static_offset";
+ else
continue;
- err = btf__add_decl_attr(btf, "__bpf_relocatable", i, -1);
+ err = btf__add_decl_attr(btf, attr, i, -1);
if (err < 0)
goto out;
}
@@ -1264,6 +1269,13 @@ static int dump_btf_c(struct btf *btf,
printf("#define __bpf_relocatable\n");
printf("#endif\n");
printf("#endif\n\n");
+ printf("#ifndef __bpf_static_offset\n");
+ printf("#if __has_attribute(preserve_static_offset)\n");
+ printf("#define __bpf_static_offset __attribute__((preserve_static_offset))\n");
+ printf("#else\n");
+ printf("#define __bpf_static_offset\n");
+ printf("#endif\n");
+ printf("#endif\n\n");
printf("#ifndef __ksym\n");
printf("#define __ksym __attribute__((section(\".ksyms\")))\n");
printf("#endif\n\n");
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
index f2389e3dceed..b56b676b96a5 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
@@ -9,6 +9,14 @@
#endif
#endif
+#ifndef __bpf_static_offset
+#if __has_attribute(preserve_static_offset)
+#define __bpf_static_offset __attribute__((preserve_static_offset))
+#else
+#define __bpf_static_offset
+#endif
+#endif
+
#ifndef __ksym
#define __ksym __attribute__((section(".ksyms")))
#endif
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
index 549045677a45..543db89078af 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
@@ -9,6 +9,14 @@
#endif
#endif
+#ifndef __bpf_static_offset
+#if __has_attribute(preserve_static_offset)
+#define __bpf_static_offset __attribute__((preserve_static_offset))
+#else
+#define __bpf_static_offset
+#endif
+#endif
+
#ifndef __ksym
#define __ksym __attribute__((section(".ksyms")))
#endif
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 13/17] selftests/bpf: Cover record attributes in bpftool BTF dumps
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (11 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 12/17] bpftool: Emit __bpf_static_offset " Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 14/17] selftests/bpf: Cover context and socket accesses through vmlinux.h Ihor Solodrai
` (4 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
Types whose field accesses need static offsets carry __bpf_static_offset,
while their nested anonymous records must stay unannotated. Other
records retain __bpf_relocatable, including context types such as
bpf_nf_ctx whose accesses do not need static offsets.
Extend the existing C dump test fixture with selected context and
socket types and recursively nested anonymous unions.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../bpf/bpftool_btf_dump_sorted.expected | 24 ++++++++++
.../bpf/bpftool_btf_dump_unsorted.expected | 24 ++++++++++
.../bpf/prog_tests/bpftool_btf_dump.c | 47 +++++++++++++++++++
3 files changed, 95 insertions(+)
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
index b56b676b96a5..190cf16e49a2 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
@@ -37,6 +37,30 @@ enum {
E0 = 1,
};
+struct __sk_buff {
+ int len;
+ union {
+ int sk;
+ };
+} __bpf_static_offset;
+
+struct bpf_nf_ctx {
+ int f;
+} __bpf_relocatable;
+
+struct bpf_sk_lookup {
+ union {
+ union {
+ int cookie;
+ };
+ int family;
+ };
+} __bpf_static_offset;
+
+struct bpf_tcp_sock {
+ int snd_cwnd;
+} __bpf_static_offset;
+
struct holey {
int c;
long: 32;
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
index 543db89078af..1a0a7b016c50 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
@@ -48,6 +48,30 @@ struct s {
int f;
} __bpf_relocatable;
+struct __sk_buff {
+ int len;
+ union {
+ int sk;
+ };
+} __bpf_static_offset;
+
+struct bpf_sk_lookup {
+ union {
+ union {
+ int cookie;
+ };
+ int family;
+ };
+} __bpf_static_offset;
+
+struct bpf_tcp_sock {
+ int snd_cwnd;
+} __bpf_static_offset;
+
+struct bpf_nf_ctx {
+ int f;
+} __bpf_relocatable;
+
/* BPF kfuncs */
#ifndef BPF_NO_KFUNC_PROTOTYPES
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
index a0f801fcaa47..3cda90062fc6 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -20,6 +20,18 @@
* };
* enum { E0 = 1 };
* struct s { int f; };
+ * struct __sk_buff {
+ * int len;
+ * union { int sk; };
+ * };
+ * struct bpf_sk_lookup {
+ * union {
+ * union { int cookie; };
+ * int family;
+ * };
+ * };
+ * struct bpf_tcp_sock { int snd_cwnd; };
+ * struct bpf_nf_ctx { int f; };
*/
static struct btf *mk_btf(void)
{
@@ -42,6 +54,25 @@ static struct btf *mk_btf(void)
btf__add_struct(btf, "s", 4);
btf__add_field(btf, "f", 1, 0, 0);
+ btf__add_union(btf, NULL, 4);
+ btf__add_field(btf, "sk", 1, 0, 0);
+ btf__add_struct(btf, "__sk_buff", 8);
+ btf__add_field(btf, "len", 1, 0, 0);
+ btf__add_field(btf, NULL, 6, 32, 0);
+
+ btf__add_union(btf, NULL, 4);
+ btf__add_field(btf, "cookie", 1, 0, 0);
+ btf__add_union(btf, NULL, 4);
+ btf__add_field(btf, NULL, 8, 0, 0);
+ btf__add_field(btf, "family", 1, 0, 0);
+ btf__add_struct(btf, "bpf_sk_lookup", 4);
+ btf__add_field(btf, NULL, 9, 0, 0);
+
+ btf__add_struct(btf, "bpf_tcp_sock", 4);
+ btf__add_field(btf, "snd_cwnd", 1, 0, 0);
+ btf__add_struct(btf, "bpf_nf_ctx", 4);
+ btf__add_field(btf, "f", 1, 0, 0);
+
VALIDATE_RAW_BTF(
btf,
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
@@ -52,6 +83,22 @@ static struct btf *mk_btf(void)
"[4] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
"\t'E0' val=1",
"[5] STRUCT 's' size=4 vlen=1\n"
+ "\t'f' type_id=1 bits_offset=0",
+ "[6] UNION '(anon)' size=4 vlen=1\n"
+ "\t'sk' type_id=1 bits_offset=0",
+ "[7] STRUCT '__sk_buff' size=8 vlen=2\n"
+ "\t'len' type_id=1 bits_offset=0\n"
+ "\t'(anon)' type_id=6 bits_offset=32",
+ "[8] UNION '(anon)' size=4 vlen=1\n"
+ "\t'cookie' type_id=1 bits_offset=0",
+ "[9] UNION '(anon)' size=4 vlen=2\n"
+ "\t'(anon)' type_id=8 bits_offset=0\n"
+ "\t'family' type_id=1 bits_offset=0",
+ "[10] STRUCT 'bpf_sk_lookup' size=4 vlen=1\n"
+ "\t'(anon)' type_id=9 bits_offset=0",
+ "[11] STRUCT 'bpf_tcp_sock' size=4 vlen=1\n"
+ "\t'snd_cwnd' type_id=1 bits_offset=0",
+ "[12] STRUCT 'bpf_nf_ctx' size=4 vlen=1\n"
"\t'f' type_id=1 bits_offset=0");
return btf;
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 14/17] selftests/bpf: Cover context and socket accesses through vmlinux.h
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (12 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 13/17] selftests/bpf: Cover record attributes in bpftool BTF dumps Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 15/17] bpftool: Document __bpf_relocatable and __bpf_static_offset Ihor Solodrai
` (3 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
Incorrect vmlinux.h annotations can make valid context and socket
accesses fail to load.
Add load tests through the generated header for these cases:
- fields in nested anonymous unions of context types, which vmlinux.h
leaves unannotated, and a bpf_sock field.
- skb_narrow_field(): a narrow skb->mark load, which the verifier
rejects if __sk_buff carries __bpf_relocatable.
- skb_merge_loads(): skb field loads merged across branches, which the
verifier rejects without __bpf_static_offset. Built only if the
compiler supports preserve_static_offset.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../selftests/bpf/prog_tests/verifier.c | 2 +
.../bpf/progs/verifier_ctx_rewritten.c | 60 +++++++++++++++++++
2 files changed, 62 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/verifier_ctx_rewritten.c
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index ae23304c1340..d41339ef00d8 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -45,6 +45,7 @@
#include "verifier_const.skel.h"
#include "verifier_const_or.skel.h"
#include "verifier_ctx.skel.h"
+#include "verifier_ctx_rewritten.skel.h"
#include "verifier_ctx_sk_msg.skel.h"
#include "verifier_d_path.skel.h"
#include "verifier_default_trusted_ptr.skel.h"
@@ -242,6 +243,7 @@ void test_verifier_cgroup_storage(void) { RUN(verifier_cgroup_storage); }
void test_verifier_const(void) { RUN(verifier_const); }
void test_verifier_const_or(void) { RUN(verifier_const_or); }
void test_verifier_ctx(void) { RUN_TESTS(verifier_ctx); }
+void test_verifier_ctx_rewritten(void) { RUN_TESTS(verifier_ctx_rewritten); }
void test_verifier_ctx_sk_msg(void) { RUN(verifier_ctx_sk_msg); }
void test_verifier_d_path(void) { RUN(verifier_d_path); }
void test_verifier_default_trusted_ptr(void) { RUN_TESTS(verifier_default_trusted_ptr); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx_rewritten.c b/tools/testing/selftests/bpf/progs/verifier_ctx_rewritten.c
new file mode 100644
index 000000000000..5feba7e1af73
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_ctx_rewritten.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+SEC("tc")
+__success
+int skb_socket(struct __sk_buff *skb)
+{
+ struct bpf_sock *sk = skb->sk;
+
+ return sk ? sk->family : 0;
+}
+
+SEC("sockops")
+__success
+int sockops_args_reply(struct bpf_sock_ops *skops)
+{
+ skops->reply = skops->args[1];
+ return 0;
+}
+
+SEC("sk_msg")
+__success
+int sk_msg_data(struct sk_msg_md *msg)
+{
+ return msg->data < msg->data_end;
+}
+
+SEC("cgroup/getsockopt")
+__success
+int sockopt_optval(struct bpf_sockopt *ctx)
+{
+ return ctx->optval < ctx->optval_end;
+}
+
+SEC("tc")
+__success
+int skb_narrow_field(struct __sk_buff *skb)
+{
+ /* a nonzero byte offset becomes pointer arithmetic with preserve_access_index */
+ return ((__u8 *)&skb->mark)[1];
+}
+
+#if __has_attribute(preserve_static_offset)
+SEC("tc")
+__success
+int skb_merge_loads(struct __sk_buff *skb)
+{
+ __u32 value;
+
+ if (skb->len)
+ value = skb->pkt_type;
+ else
+ value = skb->mark;
+ return value;
+}
+#endif
+
+char _license[] SEC("license") = "GPL";
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 15/17] bpftool: Document __bpf_relocatable and __bpf_static_offset
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (13 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 14/17] selftests/bpf: Cover context and socket accesses through vmlinux.h Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 16/17] bpftool: Render the arena type tag as __bpf_arena Ihor Solodrai
` (2 subsequent siblings)
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
The C dump annotates record definitions with the __bpf_relocatable and
__bpf_static_offset macros, which programs can pre-define.
BPF_NO_PRESERVE_ACCESS_INDEX disables only __bpf_relocatable. The
bpftool-btf page describes none of this.
Describe both macros in a new GENERATED HEADERS section, including how
to avoid the Clang crash on CO-RE field-info builtins on types
annotated with __bpf_static_offset, and add an example of the generated
header.
Suggested-by: Quentin Monnet <qmo@kernel.org>
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../bpf/bpftool/Documentation/bpftool-btf.rst | 67 ++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index c6dc445cd4a1..a3778f449d73 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -68,7 +68,8 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
**format** option can be used to override default (raw) output format. Raw
(**raw**) or C-syntax (**c**) output formats are supported. With C-style
formatting, the output is sorted by default. Use the **unsorted** option
- to avoid sorting the output.
+ to avoid sorting the output. See **GENERATED HEADERS** for the
+ annotations in C output.
**root_id** option can be used to filter a dump to a single type and all
its dependent types. It cannot be used with any other types of filtering
@@ -98,6 +99,30 @@ OPTIONS
BTF object is passed through other handles, this option becomes
necessary.
+GENERATED HEADERS
+=================
+C output annotates struct and union definitions individually. Most records
+carry ``__bpf_relocatable``. The generated header defines it as
+``__attribute__((preserve_access_index))`` where supported, or as empty
+when **BPF_NO_PRESERVE_ACCESS_INDEX** is defined or the compiler lacks the
+attribute. GCC-BPF can apply CO-RE through these attributes.
+
+Context and socket types whose field accesses the verifier rewrites carry
+``__bpf_static_offset`` instead, and their nested anonymous records carry
+no attribute. Where supported (Clang 18 and later), ``__bpf_static_offset``
+expands to ``__attribute__((preserve_static_offset))``. It is empty
+otherwise. The attribute keeps static field offsets in load and store
+instruction immediates. **BPF_NO_PRESERVE_ACCESS_INDEX** does not affect
+``__bpf_static_offset``.
+
+Define either macro before including the header, as a complete annotation
+such as ``__attribute__((preserve_access_index))``, or as empty.
+
+Clang 18 through 23 crash when ``__builtin_preserve_field_info`` is used on
+a type marked ``__bpf_static_offset``, including through ``bpf_core_field_*``
+and the CO-RE bitfield helpers. Compile with ``-D__bpf_static_offset=`` to
+use these helpers with an affected compiler.
+
EXAMPLES
========
**# bpftool btf dump id 1226**
@@ -273,3 +298,43 @@ All the standard ways to specify map or program are supported:
[104859] FUNC 'smbalert_work' type_id=9695 linkage=static
[104860] FUNC 'smbus_alert' type_id=71367 linkage=static
[104861] FUNC 'smbus_do_alert' type_id=84827 linkage=static
+
+**$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h**
+
+**$ cat vmlinux.h**
+
+::
+
+ #ifndef __VMLINUX_H__
+ #define __VMLINUX_H__
+
+ #ifndef __bpf_relocatable
+ #if !defined(BPF_NO_PRESERVE_ACCESS_INDEX) && __has_attribute(preserve_access_index)
+ #define __bpf_relocatable __attribute__((preserve_access_index))
+ #else
+ #define __bpf_relocatable
+ #endif
+ #endif
+
+ #ifndef __bpf_static_offset
+ #if __has_attribute(preserve_static_offset)
+ #define __bpf_static_offset __attribute__((preserve_static_offset))
+ #else
+ #define __bpf_static_offset
+ #endif
+ #endif
+ ...
+ struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+ } __bpf_relocatable;
+ ...
+ struct __sk_buff {
+ __u32 len;
+ ...
+ union {
+ struct bpf_flow_keys *flow_keys;
+ };
+ ...
+ } __bpf_static_offset;
+ ...
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 16/17] bpftool: Render the arena type tag as __bpf_arena
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (14 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 15/17] bpftool: Document __bpf_relocatable and __bpf_static_offset Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 17/17] selftests/bpf: Cover the arena type tag in bpftool C dumps Ihor Solodrai
2026-11-06 0:30 ` [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump patchwork-bot+netdevbpf
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
Kernel structs shared with BPF programs mark pointers into a BPF arena
with __arena, which kernel BTF carries as a plain "arena" type tag
that the verifier relies on. btf_dump prints the tag as
__attribute__((btf_type_tag("arena"))) on a pointer in the default
address space, so a program built against vmlinux.h has to cast such a
pointer to the arena address space before using it.
Attach a kflagged decl tag to each "arena" type tag in the dump split,
so that btf_dump prints __bpf_arena instead. In the generated header:
#define __bpf_arena __attribute__((address_space(1))) __attribute__((btf_type_tag("arena")))
So the program gets an arena pointer and its BTF keeps the tag the
kernel expects. Programs can pre-define the macro.
Use __bpf_arena to avoid conflicts with unguarded __arena definitions
in libarena and sched_ext headers.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
tools/bpf/bpftool/Documentation/bpftool-btf.rst | 6 ++++++
tools/bpf/bpftool/btf.c | 5 +++++
.../testing/selftests/bpf/bpftool_btf_dump_sorted.expected | 4 ++++
.../selftests/bpf/bpftool_btf_dump_unsorted.expected | 4 ++++
.../selftests/bpf/prog_tests/test_struct_ops_arena.c | 2 +-
.../selftests/bpf/progs/struct_ops_arena_member_render.c | 7 +++----
6 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index a3778f449d73..9fe035b80103 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -123,6 +123,12 @@ a type marked ``__bpf_static_offset``, including through ``bpf_core_field_*``
and the CO-RE bitfield helpers. Compile with ``-D__bpf_static_offset=`` to
use these helpers with an affected compiler.
+Pointers carrying the ``arena`` type tag in kernel BTF are rendered with
+``__bpf_arena``. This macro expands to
+``__attribute__((address_space(1))) __attribute__((btf_type_tag("arena")))``,
+so programs can use the pointers without explicit casts. Define
+``__bpf_arena`` before including the header to override it.
+
EXAMPLES
========
**# bpftool btf dump id 1226**
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 1329745c5854..c06b01f2c5b3 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -1103,6 +1103,8 @@ static const struct {
} btf_c_type_tags[] = {
/* the arena attribute in older kernel BTF */
{ true, "address_space(1)", "__attribute__((address_space(1)))" },
+ /* __arena in include/linux/bpf_compiler_types.h */
+ { false, "arena", "__bpf_arena" },
};
static int btf_c_annotate_type_tags(struct btf *btf)
@@ -1276,6 +1278,9 @@ static int dump_btf_c(struct btf *btf,
printf("#define __bpf_static_offset\n");
printf("#endif\n");
printf("#endif\n\n");
+ printf("#ifndef __bpf_arena\n");
+ printf("#define __bpf_arena __attribute__((address_space(1))) __attribute__((btf_type_tag(\"arena\")))\n");
+ printf("#endif\n\n");
printf("#ifndef __ksym\n");
printf("#define __ksym __attribute__((section(\".ksyms\")))\n");
printf("#endif\n\n");
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
index 190cf16e49a2..0ecf53b29042 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
@@ -17,6 +17,10 @@
#endif
#endif
+#ifndef __bpf_arena
+#define __bpf_arena __attribute__((address_space(1))) __attribute__((btf_type_tag("arena")))
+#endif
+
#ifndef __ksym
#define __ksym __attribute__((section(".ksyms")))
#endif
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
index 1a0a7b016c50..24a8db96eab0 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
@@ -17,6 +17,10 @@
#endif
#endif
+#ifndef __bpf_arena
+#define __bpf_arena __attribute__((address_space(1))) __attribute__((btf_type_tag("arena")))
+#endif
+
#ifndef __ksym
#define __ksym __attribute__((section(".ksyms")))
#endif
diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c
index e7580b9f0fc2..2e915b350cd2 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c
@@ -259,7 +259,7 @@ static void arena_autobind_excl(void)
struct_ops_arena_autobind__destroy(skel);
}
-/* The same through the vmlinux.h rendering of the members, which takes casts. */
+/* The same through an older bpftool's vmlinux.h rendering of the members, which takes casts. */
static void arena_member_render(void)
{
struct struct_ops_arena_member_render *skel;
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c b/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
index 253ffdd86d32..b6b66934266b 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
@@ -11,10 +11,9 @@
char _license[] SEC("license") = "GPL";
/*
- * The module types as vmlinux.h renders them: the tag is an attribute on a
- * pointer in the kernel address space, so the program has to cast the member
- * to the arena address space before using it. That is what a program built
- * against vmlinux.h sees for a kernel struct with an __arena member.
+ * The module types as an older bpftool renders them in vmlinux.h: the tag is
+ * an attribute on a pointer in the kernel address space, so the program has
+ * to cast the member to the arena address space before using it.
*/
#define __arena_tag __attribute__((btf_type_tag("arena")))
#define __nullable_tag __attribute__((btf_type_tag("nullable")))
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH bpf-next v4 17/17] selftests/bpf: Cover the arena type tag in bpftool C dumps
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (15 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 16/17] bpftool: Render the arena type tag as __bpf_arena Ihor Solodrai
@ 2026-11-05 17:58 ` Ihor Solodrai
2026-11-06 0:30 ` [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump patchwork-bot+netdevbpf
17 siblings, 0 replies; 24+ messages in thread
From: Ihor Solodrai @ 2026-11-05 17:58 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet
Cc: bpf, Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
bpftool prints the plain "arena" type tag as __bpf_arena. Add an
arena-tagged pointer to the BTF behind the bpftool C dump goldens and
expect __bpf_arena in its place.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../selftests/bpf/bpftool_btf_dump_sorted.expected | 2 ++
.../selftests/bpf/bpftool_btf_dump_unsorted.expected | 2 ++
.../selftests/bpf/prog_tests/bpftool_btf_dump.c | 10 +++++++++-
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
index 0ecf53b29042..2370a60ca7c5 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_sorted.expected
@@ -41,6 +41,8 @@ enum {
E0 = 1,
};
+typedef int __bpf_arena *arena_ptr;
+
struct __sk_buff {
int len;
union {
diff --git a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
index 24a8db96eab0..540a073c410f 100644
--- a/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
+++ b/tools/testing/selftests/bpf/bpftool_btf_dump_unsorted.expected
@@ -76,6 +76,8 @@ struct bpf_nf_ctx {
int f;
} __bpf_relocatable;
+typedef int __bpf_arena *arena_ptr;
+
/* BPF kfuncs */
#ifndef BPF_NO_KFUNC_PROTOTYPES
diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
index 3cda90062fc6..ce37aee5f288 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
@@ -32,6 +32,7 @@
* };
* struct bpf_tcp_sock { int snd_cwnd; };
* struct bpf_nf_ctx { int f; };
+ * typedef int __arena *arena_ptr;
*/
static struct btf *mk_btf(void)
{
@@ -73,6 +74,10 @@ static struct btf *mk_btf(void)
btf__add_struct(btf, "bpf_nf_ctx", 4);
btf__add_field(btf, "f", 1, 0, 0);
+ btf__add_type_tag(btf, "arena", 1);
+ btf__add_ptr(btf, 13);
+ btf__add_typedef(btf, "arena_ptr", 14);
+
VALIDATE_RAW_BTF(
btf,
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
@@ -99,7 +104,10 @@ static struct btf *mk_btf(void)
"[11] STRUCT 'bpf_tcp_sock' size=4 vlen=1\n"
"\t'snd_cwnd' type_id=1 bits_offset=0",
"[12] STRUCT 'bpf_nf_ctx' size=4 vlen=1\n"
- "\t'f' type_id=1 bits_offset=0");
+ "\t'f' type_id=1 bits_offset=0",
+ "[13] TYPE_TAG 'arena' type_id=1",
+ "[14] PTR '(anon)' type_id=13",
+ "[15] TYPEDEF 'arena_ptr' type_id=14");
return btf;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is
2026-11-05 17:58 ` [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is Ihor Solodrai
@ 2026-11-05 18:16 ` sashiko-bot
2026-11-06 0:25 ` Andrii Nakryiko
1 sibling, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-11-05 18:16 UTC (permalink / raw)
To: Ihor Solodrai; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] bpf: changing kflag BTF_KIND_TYPE_TAG format breaks C dumps across versions
--
commit 02905c14788165a6bc8db8f66ae9bfa227c2d55a
Author: Ihor Solodrai <ihor.solodrai@linux.dev>
libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is
This commit changes btf_dump to print kflagged BTF decl and type tags as-is
without wrapping them in __attribute__((...)), updates resolve_btfids to
encode the full __attribute__((address_space(1))) string in TYPE_ATTR_ARENA,
and adds a split-BTF decl tag override in bpftool for older kernel BTF.
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 5ad5473563034..c16dd93fa6d4c 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -204,7 +204,7 @@ struct btf_links {
> #define KF_ARENA_ARG2 (1 << 15)
> #define KF_IMPLICIT_ARGS (1 << 16)
> #define KF_IMPL_SUFFIX "_impl"
> -#define TYPE_ATTR_ARENA "address_space(1)"
> +#define TYPE_ATTR_ARENA "__attribute__((address_space(1)))"
> #define PARAM_SUFFIX_ARENA "__arena"
> #define PARAM_SUFFIX_ARENA_NULLABLE "__arena__nullable"
>
> diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
> index c12c3c248407f..fcd97aa905897 100644
> --- a/tools/lib/bpf/btf_dump.c
> +++ b/tools/lib/bpf/btf_dump.c
> @@ -1023,7 +1023,7 @@ static void btf_dump_emit_decl_tag(struct btf_dump *d, const struct btf_type *t)
> const char *name = btf_name_of(d, t->name_off);
>
> if (btf_kflag(t))
> - btf_dump_printf(d, " __attribute__((%s))", name);
> + btf_dump_printf(d, " %s", name);
> else
> btf_dump_printf(d, " __attribute__((btf_decl_tag(\"%s\")))", name);
> }
> @@ -1602,7 +1602,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
> break;
> name = btf_name_of(d, t->name_off);
> if (btf_kflag(t))
> - btf_dump_printf(d, " __attribute__((%s))", name);
> + btf_dump_printf(d, " %s", name);
> else
> btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name);
> break;
[Severity: Medium]
As reported by bot+bpf-ci@kernel.org on v3 [1], changing how kflagged
BTF_KIND_TYPE_TAG entries are encoded by resolve_btfids and emitted by
btf_dump_emit_type_chain() causes regressions when dumping C headers across
versions.
In tools/bpf/resolve_btfids/main.c, TYPE_ATTR_ARENA is changed to
"__attribute__((address_space(1)))" and passed to btf__add_type_attr() in
arena_tag_ptr(), which encodes the full attribute wrapper into vmlinux BTF for
arena kfuncs. When an existing bpftool built with libbpf 1.6 or later dumps
vmlinux.h from a newly built kernel, its btf_dump_emit_type_chain() wraps the
kflagged type tag in __attribute__((%s)) again, emitting:
void __attribute__((__attribute__((address_space(1))))) *
This breaks compilation of any BPF program including vmlinux.h unless
BPF_NO_KFUNC_PROTOTYPES is defined.
Conversely, in tools/lib/bpf/btf_dump.c, changing btf_dump_emit_type_chain()
to print kflagged BTF_KIND_TYPE_TAG strings without __attribute__((...))
means that any external caller of btf_dump__dump_type() or
btf_dump__emit_type_decl() outside bpftool (which does not run
btf_c_annotate_type_tags()) will emit bare address_space(1) when dumping
existing v6.16+ kernel BTF or BTF produced by pahole with
--btf_features=attributes, which also fails to compile.
Since btf_dump already allows a kflagged BTF_KIND_DECL_TAG (with
component_idx = -1) in the dump split BTF to override how a referenced
BTF_KIND_TYPE_TAG is printed, could btf_dump_emit_type_chain() and
TYPE_ATTR_ARENA in resolve_btfids be left unchanged, and raw printing be
restricted to kflagged BTF_KIND_DECL_TAG in btf_dump_emit_decl_tag() as
requested by Alexei Starovoitov on v3?
[1] https://lore.kernel.org/r/b8912e062900f51fd8c8f44c44ef43d0788ce6af12582abc0060dd6cd4c05065@mail.kernel.org
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105175845.30357-1-ihor.solodrai@linux.dev?part=7
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH bpf-next v4 02/17] libbpf: Look up BTF strings without making BTF modifiable
2026-11-05 17:58 ` [PATCH bpf-next v4 02/17] libbpf: Look up BTF strings without making BTF modifiable Ihor Solodrai
@ 2026-11-05 18:41 ` bot+bpf-ci
0 siblings, 0 replies; 24+ messages in thread
From: bot+bpf-ci @ 2026-11-05 18:41 UTC (permalink / raw)
To: ihor.solodrai, ast, andrii, daniel, eddyz87, memxor, qmo
Cc: bpf, alan.maguire, cupertino.miranda, david.faust, irogers,
jose.marchesi, vineet.gupta, yonghong.song, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2729 bytes --]
> libbpf: Look up BTF strings without making BTF modifiable
TL;DR: The base-BTF modifiable loop and stale comment in btf_rewrite_str() may
now be obsolete; btf__add_btf() into split BTF may still fail with -EOPNOTSUPP
on vmlinux BTF with extra header fields, so drop the loop or note it.
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
[ ... ]
> @@ -2131,8 +2141,7 @@ int btf__find_str(struct btf *btf, const char *s)
> return off;
> }
>
> - /* BTF needs to be in a modifiable state to build string lookup index */
> - err = btf_ensure_modifiable(btf);
> + err = btf_ensure_strs_set(btf);
> if (err)
> return libbpf_err(err);
After this change btf__add_str() no longer makes the base BTFs modifiable
or frees their raw data. Is the workaround in btf_rewrite_str() now
obsolete?
tools/lib/bpf/btf.c:btf_rewrite_str() {
/*
* btf__add_str() first looks the string up in the base BTFs of dst,
* which makes each of them modifiable and frees its raw data. If src
* shares a base with dst and the string lives in that base, the
* pointer taken below would be left dangling. So make the bases
* modifiable before the pointer is taken.
*/
for (base = p->dst->base_btf; base; base = base->base_btf) {
err = btf_ensure_modifiable(base);
if (err)
return err;
}
...
}
The comment is no longer accurate, since btf__find_str() on a base now only
calls btf_ensure_strs_set() and never calls btf_invalidate_raw_data(). A
pointer from btf__str_by_offset() into a read-only shared base stays valid.
The loop still runs, so btf__add_type() and btf__add_btf() into split BTF
still make every base modifiable. That still fails with -EOPNOTSUPP when
a base has unknown header bytes:
if (btf->has_hdr_extra)
return -EOPNOTSUPP;
btf__add_btf() only calls btf_rewrite_str() for
*str_off >= src_btf->start_str_off, so it never touches strings of a shared
base, yet the loop forces the base modifiable anyway. For example, bpftool
merge_btf_files() does:
combined = btf__new_empty_split(vmlinux_base);
btf__add_btf(combined, mod);
which reaches btf_rewrite_str() -> btf_ensure_modifiable(vmlinux_base), and
that fails for vmlinux BTF with new header fields, which is the case the
commit message says it wants to avoid.
Could the loop and its comment be dropped as part of this change, or should
the commit message note that btf__add_type() and btf__add_btf() still
require a modifiable base?
---
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/42081937070
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH bpf-next v4 10/17] bpftool: Apply __bpf_relocatable per record on BTF dump
2026-11-05 17:58 ` [PATCH bpf-next v4 10/17] bpftool: Apply __bpf_relocatable per record on BTF dump Ihor Solodrai
@ 2026-11-05 18:41 ` bot+bpf-ci
0 siblings, 0 replies; 24+ messages in thread
From: bot+bpf-ci @ 2026-11-05 18:41 UTC (permalink / raw)
To: ihor.solodrai, ast, andrii, daniel, eddyz87, memxor, qmo
Cc: bpf, alan.maguire, cupertino.miranda, david.faust, irogers,
jose.marchesi, vineet.gupta, yonghong.song, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2848 bytes --]
> bpftool: Apply __bpf_relocatable per record on BTF dump
TL;DR: Tagging every struct, including verifier-rewritten context types like
__sk_buff, may make GCC-BPF programs fail verification until later patches fix
it; unverified. Consider excluding context types in this patch for
bisectability.
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
[ ... ]
> @@ -1131,6 +1131,24 @@ static int btf_c_annotate_type_tags(struct btf *btf)
> return 0;
> }
>
> +static int btf_c_annotate_records(struct btf *btf)
> +{
> + __u32 i, cnt = btf__type_cnt(btf);
> + int err;
> +
> + for (i = 1; i < cnt; i++) {
> + const struct btf_type *t = btf__type_by_id(btf, i);
> +
> + if (!btf_is_composite(t))
> + continue;
> + err = btf__add_decl_attr(btf, "__bpf_relocatable", i, -1);
Can this break GCC-BPF programs that access verifier-rewritten context
types?
btf_c_annotate_records() tags every struct and union, including __sk_buff,
xdp_md, bpf_sock, bpf_sock_ops, sk_msg_md, bpf_sockopt and the like.
For Clang nothing changes, since the old pragma already applied
preserve_access_index to these types. GCC-BPF ignored the pragma, so
vmlinux.h never gave it preserve_access_index. With this patch GCC-BPF
applies the attribute to the context types as well, unless
BPF_NO_PRESERVE_ACCESS_INDEX is defined.
For program types with convert_ctx_access, check_ctx_access() in the
verifier rejects a ctx pointer that has an added offset:
__check_ptr_off_reg(env, reg, argno, fixed_off_ok)
fixed_off_ok = !env->ops->convert_ctx_access
With preserve_access_index on these types the compiler can emit pointer
arithmetic, for example for a one-byte load at offset 1 into skb->mark,
and the verifier then rejects an otherwise valid program. A GCC-BPF tc
program that includes vmlinux.h without BPF_NO_PRESERVE_ACCESS_INDEX could
pass before this patch and fail after it.
The in-tree GCC-BPF selftests are not affected because the bpf_gcc flavor
passes -DBPF_NO_PRESERVE_ACCESS_INDEX. I did not build with GCC-BPF to
confirm the exact instructions it emits.
This is addressed later in the series by 87b9a5139319 ("bpftool: Skip
__bpf_relocatable on verifier-rewritten context types") and
c6d74cfddc8d ("bpftool: Emit __bpf_static_offset on verifier-rewritten
context types"). Would it be better to put the context-type exclusion in
this patch, or ahead of it, so GCC-BPF context accesses don't break in the
window between them (for example when bisecting)?
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
---
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/42081937070
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH bpf-next v4 06/17] libbpf: Let decl tags override how btf_dump renders type tags
2026-11-05 17:58 ` [PATCH bpf-next v4 06/17] libbpf: Let decl tags override how btf_dump renders type tags Ihor Solodrai
@ 2026-11-06 0:24 ` Andrii Nakryiko
0 siblings, 0 replies; 24+ messages in thread
From: Andrii Nakryiko @ 2026-11-06 0:24 UTC (permalink / raw)
To: Ihor Solodrai
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet, bpf,
Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
On Thu, Nov 5, 2026 at 10:00 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> A tool that dumps C from kernel BTF may need a type tag printed
> differently than btf_dump prints it by default, for example as a macro
> that the generated header defines. The tool can't change the tag
> itself: kernel BTF must keep the encoding the kernel expects, and if
> the dumped BTF is the base of a split, a type in the base can't refer
> to a type added to the split.
>
> A decl tag annotates another type by referencing it, so a split can
> attach one to a type tag of its base.
>
> Let such decl tags override the rendering of the type tag: when a type
> tag has decl tags with component_idx -1, print them in its place, the
> same way as decl tags on declarations. The tool that builds the dumped
> BTF then decides how each type tag is rendered.
>
> The kernel rejects decl tags on type tags, so kernel BTF cannot have
> any, and its C dumps are unchanged.
>
> Document this in btf.rst.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
> Documentation/bpf/btf.rst | 10 ++++++++++
> tools/lib/bpf/btf_dump.c | 12 ++++++++++--
> 2 files changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
> index 29de1222c3e7..ea0d699749af 100644
> --- a/Documentation/bpf/btf.rst
> +++ b/Documentation/bpf/btf.rst
> @@ -511,6 +511,9 @@ representing the attribute-list of the attribute specifier. For
> example, for an ``__attribute__((aligned(4)))`` the string's contents
> is ``aligned(4)``.
>
> +``btf_dump`` also accepts decl tags that reference a ``type_tag``, as
> +described under ``BTF_KIND_TYPE_TAG``.
> +
um... all this is libbpf-specific concerns, I don't think these doc
changes belong in Documentation/bpf/btf.rst
if at all, we should just maybe add it somewhere next to btf_dump API
in libbpf's btf_dump.h (or just not document this quirk at all)
I've applied, but I should we should clean this up in a follow up
> 2.2.18 BTF_KIND_TYPE_TAG
> ~~~~~~~~~~~~~~~~~~~~~~~~
>
> @@ -542,6 +545,13 @@ If ``info.kind_flag`` is 1, then the type tag represents an arbitrary
> __attribute__, and the ``name_off`` encodes a string representing the
> attribute-list of the attribute specifier.
>
> +``btf_dump`` prints decl tags that reference a type tag, with
> +``btf_decl_tag.component_idx`` of ``-1``, in place of that type tag. They
> +are printed the same way as decl tags on declarations. This lets a split
> +BTF built for a C dump change how the type tags of its base BTF are
> +printed, without modifying the base. The kernel rejects decl tags that
> +reference type tags.
> +
> 2.2.19 BTF_KIND_ENUM64
> ~~~~~~~~~~~~~~~~~~~~~~
>
> diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
> index ad43bbd3b442..c12c3c248407 100644
> --- a/tools/lib/bpf/btf_dump.c
> +++ b/tools/lib/bpf/btf_dump.c
> @@ -1033,10 +1033,11 @@ static void btf_dump_emit_decl_tag(struct btf_dump *d, const struct btf_type *t)
> * tags of one type form a run that binary search finds the start of, in a
> * fixed order so that the same BTF always renders the same C.
> */
> -static void btf_dump_emit_decl_tags(struct btf_dump *d, __u32 id, int comp_idx)
> +static int btf_dump_emit_decl_tags(struct btf_dump *d, __u32 id, int comp_idx)
> {
> size_t i, lo = 0, hi = d->decl_tag_cnt, mid;
> const struct btf_type *t;
> + int cnt = 0;
>
> /* lower bound on target_id */
> while (lo < hi) {
> @@ -1051,9 +1052,13 @@ static void btf_dump_emit_decl_tags(struct btf_dump *d, __u32 id, int comp_idx)
> for (i = lo; i < d->decl_tag_cnt && d->decl_tags[i].target_id == id; i++) {
> t = btf__type_by_id(d->btf, d->decl_tags[i].tag_id);
>
> - if (btf_decl_tag(t)->component_idx == comp_idx)
> + if (btf_decl_tag(t)->component_idx == comp_idx) {
> btf_dump_emit_decl_tag(d, t);
> + cnt++;
> + }
> }
> +
> + return cnt;
> }
>
> static void btf_dump_emit_struct_def(struct btf_dump *d,
> @@ -1592,6 +1597,9 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
> break;
> case BTF_KIND_TYPE_TAG:
> btf_dump_emit_mods(d, decls);
> + /* decl tags override the type tag, see btf.rst */
> + if (btf_dump_emit_decl_tags(d, id, -1))
> + break;
> name = btf_name_of(d, t->name_off);
> if (btf_kflag(t))
> btf_dump_printf(d, " __attribute__((%s))", name);
> --
> 2.56.0
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is
2026-11-05 17:58 ` [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is Ihor Solodrai
2026-11-05 18:16 ` sashiko-bot
@ 2026-11-06 0:25 ` Andrii Nakryiko
1 sibling, 0 replies; 24+ messages in thread
From: Andrii Nakryiko @ 2026-11-06 0:25 UTC (permalink / raw)
To: Ihor Solodrai
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Quentin Monnet, bpf,
Alan Maguire, Cupertino Miranda, David Faust, Ian Rogers,
Jose E . Marchesi, Vineet Gupta, Yonghong Song
On Thu, Nov 5, 2026 at 10:00 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> btf_dump wraps the strings of kflagged decl and type tags in an
> attribute specifier, so such a tag can only stand for an __attribute__.
>
> Print the strings as is instead. A tag can then carry any annotation,
> including an identifier whose definition is left to the consumer of the
> dump, such as a macro defined by a generated header.
>
> resolve_btfids, the only in-tree producer of kflagged tags in kernel
> BTF, tags arena pointers in kfunc prototypes with "address_space(1)".
> Make it encode the complete specifier, __attribute__((address_space(1))).
>
> Kernel BTF produced before this change carries the bare string, which
> does not compile when printed as is. pahole emitted it from
> commit 21cb33c7e065 ("kbuild, bpf: Enable --btf_features=attributes"),
> and resolve_btfids from commit 27a78c2e7eee ("resolve_btfids: Process
> KF_ARENA_* flags in resolve_btfids").
>
> In the split BTF that bpftool dumps C through, attach a kflagged decl
> tag with the complete specifier to each such type tag, which btf_dump
> prints in its place. The input stays unmodified, and module BTF, whose
> base holds the vmlinux arena kfuncs, is handled the same way.
>
> A C dump of new kernel BTF by an older bpftool, built with libbpf 1.6
> or later, nests the attribute specifier in the arena kfunc prototypes,
> which does not compile.
>
> Update the BTF reference, kfunc documentation, libbpf API comments and
> existing tests to match.
>
> Suggested-by: Andrii Nakryiko <andrii@kernel.org>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
> Documentation/bpf/btf.rst | 14 +--
> Documentation/bpf/kfuncs.rst | 3 +-
> tools/bpf/bpftool/btf.c | 43 +++++++++
> tools/bpf/resolve_btfids/main.c | 2 +-
> tools/lib/bpf/btf.c | 6 +-
> tools/lib/bpf/btf_dump.c | 4 +-
> .../selftests/bpf/prog_tests/btf_dump.c | 89 +++++++++++--------
> .../selftests/bpf/prog_tests/resolve_btfids.c | 4 +-
> 8 files changed, 112 insertions(+), 53 deletions(-)
>
> diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
> index ea0d699749af..99833048de60 100644
> --- a/Documentation/bpf/btf.rst
> +++ b/Documentation/bpf/btf.rst
> @@ -506,10 +506,11 @@ If ``info.kind_flag`` is 0, then this is a normal decl tag, and the
> ``name_off`` encodes btf_decl_tag attribute string.
>
> If ``info.kind_flag`` is 1, then the decl tag represents an arbitrary
> -__attribute__. In this case, ``name_off`` encodes a string
> -representing the attribute-list of the attribute specifier. For
> -example, for an ``__attribute__((aligned(4)))`` the string's contents
> -is ``aligned(4)``.
> +annotation. ``name_off`` encodes the complete annotation string, which
> +``btf_dump`` prints unchanged at the declaration. For example, an attribute
> +specifier is encoded as ``__attribute__((aligned(4)))``, including the
> +wrapper. A macro can be encoded as its identifier, leaving its definition
> +to the generated header or its user.
>
> ``btf_dump`` also accepts decl tags that reference a ``type_tag``, as
> described under ``BTF_KIND_TYPE_TAG``.
> @@ -542,8 +543,9 @@ normal type tag, and the ``name_off`` encodes btf_type_tag attribute
> string.
>
> If ``info.kind_flag`` is 1, then the type tag represents an arbitrary
> -__attribute__, and the ``name_off`` encodes a string representing the
> -attribute-list of the attribute specifier.
> +annotation. As with decl tags, ``name_off`` encodes the complete annotation
> +string, which ``btf_dump`` prints unchanged. An attribute specifier must
> +include its wrapper, for example ``__attribute__((address_space(1)))``.
same here, I wouldn't add a reference to btf_dump in this doc
>
> ``btf_dump`` prints decl tags that reference a type tag, with
> ``btf_decl_tag.component_idx`` of ``-1``, in place of that type tag. They
> diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
> index 901c37504217..87b3c4e4bea2 100644
> --- a/Documentation/bpf/kfuncs.rst
> +++ b/Documentation/bpf/kfuncs.rst
> @@ -567,7 +567,8 @@ At kernel build time the ``resolve_btfids`` tool finds all kfuncs declared with
> For each kfunc it 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 that use arena pointers (see
> -sections 2.3.8 and 2.8).
> +sections 2.3.8 and 2.8). The type tag stores the complete
> +``__attribute__((address_space(1)))`` specifier with ``kind_flag`` set.
>
> 2.7 Specifying no-cast aliases with ___init
> --------------------------------------------
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index a438b5a03f5a..b4516361a3fe 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
> @@ -1092,6 +1092,45 @@ static struct sort_datum *sort_btf_c(const struct btf *btf)
> return datums;
> }
>
> +/*
> + * Kernel BTF keeps its type tags as the kernel expects them. Decl tags in
> + * the dump split choose how btf_dump prints them.
> + */
> +static const struct {
> + bool kflag;
> + const char *name;
> + const char *override;
> +} btf_c_type_tags[] = {
> + /* the arena attribute in older kernel BTF */
> + { true, "address_space(1)", "__attribute__((address_space(1)))" },
> +};
> +
> +static int btf_c_annotate_type_tags(struct btf *btf)
> +{
> + __u32 i, cnt = btf__type_cnt(btf);
> + size_t j;
> + int err;
> +
> + for (i = 1; i < cnt; i++) {
> + const struct btf_type *t = btf__type_by_id(btf, i);
> + const char *name;
> +
> + if (!btf_is_type_tag(t))
> + continue;
> + name = btf__name_by_offset(btf, t->name_off);
> + for (j = 0; j < ARRAY_SIZE(btf_c_type_tags); j++) {
> + if (btf_kflag(t) != btf_c_type_tags[j].kflag ||
> + strcmp(name, btf_c_type_tags[j].name))
> + continue;
> + err = btf__add_decl_attr(btf, btf_c_type_tags[j].override, i, -1);
> + if (err < 0)
> + return err;
subjective, but I'd just hard-code two special cases instead of doing
the whole table search, it's not like this will keep expanding, we
just need to handle existing fixed set of "legacy" cases
(if you agree, send in a follow up, please)
> + }
> + }
> +
> + return 0;
> +}
> +
> static int dump_btf_c(struct btf *btf,
> __u32 *root_type_ids, int root_type_cnt, bool sort_dump)
> {
> @@ -1110,6 +1149,10 @@ static int dump_btf_c(struct btf *btf,
> if (ptr_sz)
> btf__set_pointer_size(split, ptr_sz);
>
> + err = btf_c_annotate_type_tags(split);
> + if (err)
> + goto done;
> +
> d = btf_dump__new(split, btf_dump_printf, NULL, NULL);
> if (!d) {
> err = -errno;
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 5ad547356303..c16dd93fa6d4 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -204,7 +204,7 @@ struct btf_links {
> #define KF_ARENA_ARG2 (1 << 15)
> #define KF_IMPLICIT_ARGS (1 << 16)
> #define KF_IMPL_SUFFIX "_impl"
> -#define TYPE_ATTR_ARENA "address_space(1)"
> +#define TYPE_ATTR_ARENA "__attribute__((address_space(1)))"
> #define PARAM_SUFFIX_ARENA "__arena"
> #define PARAM_SUFFIX_ARENA_NULLABLE "__arena__nullable"
>
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index 5786232c1fea..0984a8ccb526 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
> @@ -3078,7 +3078,8 @@ int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id)
> * Append new BTF_KIND_TYPE_TAG type with:
> * - *value*, non-empty/non-NULL tag value;
> * - *ref_type_id* - referenced type ID, it might not exist yet;
> - * Set info->kflag to 1, indicating this tag is an __attribute__
> + * The kind flag is set; btf_dump prints *value* unchanged.
> + * Attribute specifiers must include the __attribute__((...)) wrapper.
> * Returns:
> * - >0, type ID of newly added BTF type;
> * - <0, on error.
> @@ -3413,7 +3414,8 @@ int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
> * - *ref_type_id* - referenced type ID, it might not exist yet;
> * - *component_idx* - -1 for tagging reference type, otherwise struct/union
> * member or function argument index;
> - * Set info->kflag to 1, indicating this tag is an __attribute__
> + * The kind flag is set; btf_dump prints *value* unchanged.
> + * Attribute specifiers must include the __attribute__((...)) wrapper.
nit: too specific, that decl_tag is an arbitrary text, user can figure
out what it needs there depending on its use case
> * Returns:
> * - >0, type ID of newly added BTF type;
> * - <0, on error.
[...]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
` (16 preceding siblings ...)
2026-11-05 17:58 ` [PATCH bpf-next v4 17/17] selftests/bpf: Cover the arena type tag in bpftool C dumps Ihor Solodrai
@ 2026-11-06 0:30 ` patchwork-bot+netdevbpf
17 siblings, 0 replies; 24+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-11-06 0:30 UTC (permalink / raw)
To: Ihor Solodrai
Cc: ast, andrii, daniel, eddyz87, memxor, qmo, bpf, alan.maguire,
cupertino.miranda, david.faust, irogers, jose.marchesi,
vineet.gupta, yonghong.song
Hello:
This series was applied to bpf/bpf-next.git (master)
by Andrii Nakryiko <andrii@kernel.org>:
On Thu, 5 Nov 2026 10:58:28 -0700 you wrote:
> bpftool's C dump wraps its output in a clang attribute pragma:
>
> #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
>
> GCC-BPF implements preserve_access_index, but not the pragma. Since
> the commit 675b4e24bc50 ("bpf: Temporarily define
> BPF_NO_PRESEVE_ACCESS_INDEX for GCC") the BPF selftests build GCC
> objects with -DBPF_NO_PRESERVE_ACCESS_INDEX, which disables automatic
> CO-RE for vmlinux.h field accesses.
>
> [...]
Here is the summary with links:
- [bpf-next,v4,01/17] Revert "libbpf: Render the arena type tag with its address space"
https://git.kernel.org/bpf/bpf-next/c/be5adbb5dbe7
- [bpf-next,v4,02/17] libbpf: Look up BTF strings without making BTF modifiable
https://git.kernel.org/bpf/bpf-next/c/efeb5ab5753d
- [bpf-next,v4,03/17] bpftool: Dump C through a split BTF
https://git.kernel.org/bpf/bpf-next/c/7eddda6a121b
- [bpf-next,v4,04/17] selftests/bpf: Build BTF fixtures in btf_to_tmpfile()
https://git.kernel.org/bpf/bpf-next/c/525ab88efb6d
- [bpf-next,v4,05/17] selftests/bpf: Cover unknown BTF header-extension bytes in bpftool C dumps
https://git.kernel.org/bpf/bpf-next/c/36bf1f2bde41
- [bpf-next,v4,06/17] libbpf: Let decl tags override how btf_dump renders type tags
https://git.kernel.org/bpf/bpf-next/c/fbafb695c5d2
- [bpf-next,v4,07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is
https://git.kernel.org/bpf/bpf-next/c/79dcfd3e36a7
- [bpf-next,v4,08/17] selftests/bpf: Cover type tag rendering overrides in btf_dump
https://git.kernel.org/bpf/bpf-next/c/987293843db2
- [bpf-next,v4,09/17] selftests/bpf: Cover legacy arena type tags in bpftool C dumps
https://git.kernel.org/bpf/bpf-next/c/f857eecf674d
- [bpf-next,v4,10/17] bpftool: Apply __bpf_relocatable per record on BTF dump
https://git.kernel.org/bpf/bpf-next/c/e35e286214ba
- [bpf-next,v4,11/17] bpftool: Skip __bpf_relocatable on verifier-rewritten context types
https://git.kernel.org/bpf/bpf-next/c/8b01716790dc
- [bpf-next,v4,12/17] bpftool: Emit __bpf_static_offset on verifier-rewritten context types
https://git.kernel.org/bpf/bpf-next/c/e15918abdd48
- [bpf-next,v4,13/17] selftests/bpf: Cover record attributes in bpftool BTF dumps
https://git.kernel.org/bpf/bpf-next/c/a3335b95bd37
- [bpf-next,v4,14/17] selftests/bpf: Cover context and socket accesses through vmlinux.h
https://git.kernel.org/bpf/bpf-next/c/1fc6998042e0
- [bpf-next,v4,15/17] bpftool: Document __bpf_relocatable and __bpf_static_offset
https://git.kernel.org/bpf/bpf-next/c/aac7a06b609d
- [bpf-next,v4,16/17] bpftool: Render the arena type tag as __bpf_arena
https://git.kernel.org/bpf/bpf-next/c/40483927e5b9
- [bpf-next,v4,17/17] selftests/bpf: Cover the arena type tag in bpftool C dumps
https://git.kernel.org/bpf/bpf-next/c/325f10b127f4
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2026-11-06 0:30 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 17:58 [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 01/17] Revert "libbpf: Render the arena type tag with its address space" Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 02/17] libbpf: Look up BTF strings without making BTF modifiable Ihor Solodrai
2026-11-05 18:41 ` bot+bpf-ci
2026-11-05 17:58 ` [PATCH bpf-next v4 03/17] bpftool: Dump C through a split BTF Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 04/17] selftests/bpf: Build BTF fixtures in btf_to_tmpfile() Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 05/17] selftests/bpf: Cover unknown BTF header-extension bytes in bpftool C dumps Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 06/17] libbpf: Let decl tags override how btf_dump renders type tags Ihor Solodrai
2026-11-06 0:24 ` Andrii Nakryiko
2026-11-05 17:58 ` [PATCH bpf-next v4 07/17] libbpf, bpftool, resolve_btfids: Dump kflagged BTF tags as is Ihor Solodrai
2026-11-05 18:16 ` sashiko-bot
2026-11-06 0:25 ` Andrii Nakryiko
2026-11-05 17:58 ` [PATCH bpf-next v4 08/17] selftests/bpf: Cover type tag rendering overrides in btf_dump Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 09/17] selftests/bpf: Cover legacy arena type tags in bpftool C dumps Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 10/17] bpftool: Apply __bpf_relocatable per record on BTF dump Ihor Solodrai
2026-11-05 18:41 ` bot+bpf-ci
2026-11-05 17:58 ` [PATCH bpf-next v4 11/17] bpftool: Skip __bpf_relocatable on verifier-rewritten context types Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 12/17] bpftool: Emit __bpf_static_offset " Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 13/17] selftests/bpf: Cover record attributes in bpftool BTF dumps Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 14/17] selftests/bpf: Cover context and socket accesses through vmlinux.h Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 15/17] bpftool: Document __bpf_relocatable and __bpf_static_offset Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 16/17] bpftool: Render the arena type tag as __bpf_arena Ihor Solodrai
2026-11-05 17:58 ` [PATCH bpf-next v4 17/17] selftests/bpf: Cover the arena type tag in bpftool C dumps Ihor Solodrai
2026-11-06 0:30 ` [PATCH bpf-next v4 00/17] bpftool: Per-record CO-RE attributes in the C dump patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox