* [PATCH v3 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 9:03 ` bot+bpf-ci
2026-09-16 7:41 ` [PATCH v3 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
` (9 subsequent siblings)
10 siblings, 1 reply; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
Add BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO and BTF_KIND_LOCSEC
to help represent location information for functions.
BTF_KIND_LOC_PARAM is used to represent how we retrieve data at a
location; either via register(s), or register+offset, a dereference
of a register+offset or a constant value.
BTF_KIND_LOC_PROTO represents location information about a location
with multiple BTF_KIND_LOC_PARAMs.
And finally BTF_KIND_LOCSEC is a set of location sites, each
of which has
- a BTF_KIND_FUNC function associated with the inline site
- a location prototype specifying where to find the function
parameters
- an address offset relative to the kernel base address
This can be used to support representing
- a fully-inlined function at potentially multiple inline sites
with potentially different parameter availability
- a partially-inlined function where some _LOC_PROTOs represent
inlined sites as above and others have normal _FUNC representations
Also BTF_KIND_LOCSEC struct btf_loc will have two type id
references; one for the associated func, the other for the loc_proto.
Accordingly increase the number of m_offs references in btf_field_desc
to 2.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
---
include/linux/btf.h | 22 ++-
include/uapi/linux/btf.h | 65 ++++++-
kernel/bpf/btf.c | 304 ++++++++++++++++++++++++++++++++-
tools/include/uapi/linux/btf.h | 65 ++++++-
4 files changed, 450 insertions(+), 6 deletions(-)
diff --git a/include/linux/btf.h b/include/linux/btf.h
index ddd0f4f32d24..f29ed358b7a8 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -261,6 +261,11 @@ const char *btf_type_str(const struct btf_type *t);
i < btf_type_vlen(datasec_type); \
i++, member++)
+#define for_each_loc(i, locsec_type, member) \
+ for (i = 0, member = btf_type_loc_secinfo(locsec_type); \
+ i < btf_type_vlen(locsec_type); \
+ i++, member++)
+
static inline bool btf_type_is_ptr(const struct btf_type *t)
{
return BTF_INFO_KIND(t->info) == BTF_KIND_PTR;
@@ -329,6 +334,21 @@ static inline u64 btf_enum64_value(const struct btf_enum64 *e)
return ((u64)e->val_hi32 << 32) | e->val_lo32;
}
+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)
+{
+ return (struct btf_loc_param *)(t + 1);
+}
+
+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)
+{
+ return (__u32 *)(t + 1);
+}
+
+static inline struct btf_loc *btf_type_loc_secinfo(const struct btf_type *t)
+{
+ return (struct btf_loc *)(t + 1);
+}
+
static inline bool btf_is_composite(const struct btf_type *t)
{
u16 kind = btf_kind(t);
@@ -559,7 +579,7 @@ struct btf_field_desc {
/* member struct size, or zero, if no members */
int m_sz;
/* repeated per-member offsets */
- int m_off_cnt, m_offs[1];
+ int m_off_cnt, m_offs[2];
};
struct btf_field_iter {
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index 618167cab4e6..a85d293f4127 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -92,7 +92,9 @@ enum {
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
-
+ BTF_KIND_LOC_PARAM = 20, /* Location parameter information */
+ BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */
+ BTF_KIND_LOCSEC = 22, /* Location section */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
};
@@ -212,4 +214,65 @@ struct btf_enum64 {
__u32 val_hi32;
};
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ * of size, vlen and _SIGNED flag determines it. If the value requires
+ * 64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
+ * normalized with respect to kernel/module base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ * with vlen 2 it is a multi-register parameter.
+ * - a _REG | OFFSET describes an address without dereferencing it.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ * number specified.
+ * - a REG | DEREF | OFFSET with vlen specifies the register value in
+ * the first 4-byte value and the offset in the remainder.
+ */
+enum btf_loc_param_flags {
+ BTF_LOC_PARAM_SIGNED = 0x1,
+ BTF_LOC_PARAM_CONST = 0x2,
+ BTF_LOC_PARAM_ADDR = 0x4,
+ BTF_LOC_PARAM_REG = 0x8,
+ BTF_LOC_PARAM_DEREF = 0x10,
+ BTF_LOC_PARAM_OFFSET = 0x20,
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location. The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information for a specific ELF section;
+ * for example locations in ".text" are in a LOCSEC named "inline.text".
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to kernel base address
+ */
+
+struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+};
+
#endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 7daf4c286c9b..af5f161b5bdd 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -345,6 +345,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
const char *btf_type_str(const struct btf_type *t)
@@ -517,11 +520,27 @@ static bool btf_type_is_decl_tag(const struct btf_type *t)
return BTF_INFO_KIND(t->info) == BTF_KIND_DECL_TAG;
}
+static bool btf_type_is_loc_param(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PARAM;
+}
+
+static bool btf_type_is_loc_proto(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOC_PROTO;
+}
+
+static bool btf_type_is_locsec(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info) == BTF_KIND_LOCSEC;
+}
+
static bool btf_type_nosize(const struct btf_type *t)
{
return btf_type_is_void(t) || btf_type_is_fwd(t) ||
btf_type_is_func(t) || btf_type_is_func_proto(t) ||
- btf_type_is_decl_tag(t);
+ btf_type_is_decl_tag(t) || btf_type_is_loc_param(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t);
}
static bool btf_type_nosize_or_null(const struct btf_type *t)
@@ -796,7 +815,9 @@ static bool btf_type_needs_resolve(const struct btf_type *t)
btf_type_is_var(t) ||
btf_type_is_func(t) ||
btf_type_is_decl_tag(t) ||
- btf_type_is_datasec(t);
+ btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) ||
+ btf_type_is_locsec(t);
}
/* t->size can be used */
@@ -4729,6 +4750,279 @@ static const struct btf_kind_operations enum64_ops = {
.show = btf_enum64_show,
};
+static s32 btf_loc_param_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 size, meta_needed, vlen = btf_vlen(t);
+
+ meta_needed = sizeof(*p) + sizeof(__u32) * vlen;
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+ size = t->size;
+ if (size > 16 || !is_power_of_2(size)) {
+ btf_verifier_log_type(env, t, "Unexpected size");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+}
+
+static void btf_loc_param_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const struct btf_loc_param *p = btf_loc_param(t);
+ u32 i, vlen = btf_vlen(t);
+
+ btf_verifier_log(env, "size=%u vlen=%u flags=0x%x", t->size, vlen, p->flags);
+ for (i = 0; i < vlen; i++)
+ btf_verifier_log(env, ", %u", p->values[i]);
+}
+
+static const struct btf_kind_operations loc_param_ops = {
+ .check_meta = btf_loc_param_check_meta,
+ .resolve = btf_df_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_param_log,
+ .show = btf_df_show,
+};
+
+static s32 btf_loc_proto_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 meta_needed;
+
+ meta_needed = sizeof(__u32) * btf_type_vlen(t);
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (t->name_off) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ btf_verifier_log_type(env, t, NULL);
+
+ return meta_needed;
+}
+
+static void btf_loc_proto_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+
+ btf_verifier_log(env, "vlen=%u", nr_params);
+ for (i = 0; i < nr_params; i++, params++)
+ btf_verifier_log(env, ", %u", *params);
+}
+
+static int btf_loc_proto_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const __u32 *params = btf_loc_proto_params(t);
+ u32 i, nr_params = btf_type_vlen(t);
+ struct btf *btf = env->btf;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid loc_proto type");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_params; i++) {
+ const struct btf_type *param_type;
+ u32 param_type_id = params[i];
+
+ if (!param_type_id)
+ continue;
+
+ param_type = btf_type_by_id(btf, param_type_id);
+ if (!param_type || !btf_type_is_loc_param(param_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_param#%u", i + 1);
+ return -EINVAL;
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations loc_proto_ops = {
+ .check_meta = btf_loc_proto_check_meta,
+ .resolve = btf_loc_proto_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_loc_proto_log,
+ .show = btf_df_show,
+};
+
+__printf(4, 5)
+static void btf_verifier_log_loc(struct btf_verifier_env *env,
+ const struct btf_type *locsec_type,
+ const struct btf_loc *loc,
+ const char *fmt, ...)
+{
+ struct bpf_verifier_log *log = &env->log;
+ va_list args;
+
+ if (!bpf_verifier_log_needed(log))
+ return;
+ if (log->level == BPF_LOG_KERNEL && !fmt)
+ return;
+ if (env->phase != CHECK_META)
+ btf_verifier_log_type(env, locsec_type, NULL);
+
+ __btf_verifier_log(log, "\t func=%u loc_proto=%u offset=%u",
+ loc->func, loc->loc_proto, loc->offset);
+ if (fmt && *fmt) {
+ __btf_verifier_log(log, " ");
+ va_start(args, fmt);
+ bpf_verifier_vlog(log, fmt, args);
+ va_end(args);
+ }
+
+ __btf_verifier_log(log, "\n");
+}
+
+static s32 btf_locsec_check_meta(struct btf_verifier_env *env,
+ const struct btf_type *t,
+ u32 meta_left)
+{
+ u32 i, meta_needed, vlen = btf_type_vlen(t);
+ const struct btf_loc *loc;
+
+ meta_needed = sizeof(struct btf_loc) * vlen;
+
+ if (meta_left < meta_needed) {
+ btf_verifier_log_basic(env, t,
+ "meta_left:%u meta_needed:%u",
+ meta_left, meta_needed);
+ return -EINVAL;
+ }
+
+ if (btf_type_kflag(t)) {
+ btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
+ return -EINVAL;
+ }
+
+ if (!t->name_off ||
+ !btf_name_valid_section(env->btf, t->name_off)) {
+ btf_verifier_log_type(env, t, "Invalid name");
+ return -EINVAL;
+ }
+
+ for_each_loc(i, t, loc) {
+ /* A loc func, loc proto cannot be in type void */
+ if (!loc->func || !BTF_TYPE_ID_VALID(loc->func)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid func");
+ return -EINVAL;
+ }
+ if (!loc->loc_proto || !BTF_TYPE_ID_VALID(loc->loc_proto)) {
+ btf_verifier_log_loc(env, t, loc, "Invalid loc_proto");
+ return -EINVAL;
+ }
+ btf_verifier_log_loc(env, t, loc, NULL);
+ }
+
+ return meta_needed;
+}
+
+static void btf_locsec_log(struct btf_verifier_env *env,
+ const struct btf_type *t)
+{
+ btf_verifier_log(env, "vlen=%u", btf_type_vlen(t));
+}
+
+static int btf_locsec_resolve(struct btf_verifier_env *env,
+ const struct resolve_vertex *v)
+{
+ const struct btf_type *t = v->t;
+ const struct btf_loc *loc;
+ struct btf *btf = env->btf;
+ u32 i;
+
+ if (t->type) {
+ btf_verifier_log_type(env, t, "Invalid locsec type");
+ return -EINVAL;
+ }
+
+ env->resolve_mode = RESOLVE_TBD;
+ for (i = v->next_member, loc = btf_type_loc_secinfo(t) + i;
+ i < btf_type_vlen(t); i++, loc++) {
+ const struct btf_type *func_type, *loc_proto_type;
+ u32 func_type_id = loc->func;
+ u32 loc_proto_type_id = loc->loc_proto;
+
+ func_type = btf_type_by_id(btf, func_type_id);
+ if (!func_type || !btf_type_is_func(func_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid func#%u", i + 1);
+ return -EINVAL;
+ }
+
+ if (!env_type_is_resolved(env, func_type_id)) {
+ env_stack_set_next_member(env, i);
+ return env_stack_push(env, func_type, func_type_id);
+ }
+
+ loc_proto_type = btf_type_by_id(btf, loc_proto_type_id);
+ if (!loc_proto_type || !btf_type_is_loc_proto(loc_proto_type)) {
+ btf_verifier_log_type(env, t,
+ "Invalid loc_proto#%u", i + 1);
+ return -EINVAL;
+ }
+
+ if (!env_type_is_resolved(env, loc_proto_type_id)) {
+ env_stack_set_next_member(env, i + 1);
+ return env_stack_push(env, loc_proto_type,
+ loc_proto_type_id);
+ }
+ }
+
+ env_stack_pop_resolved(env, 0, 0);
+ return 0;
+}
+
+static const struct btf_kind_operations locsec_ops = {
+ .check_meta = btf_locsec_check_meta,
+ .resolve = btf_locsec_resolve,
+ .check_member = btf_df_check_member,
+ .check_kflag_member = btf_df_check_kflag_member,
+ .log_details = btf_locsec_log,
+ .show = btf_df_show,
+};
+
static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
const struct btf_type *t,
u32 meta_left)
@@ -5398,6 +5692,9 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = &decl_tag_ops,
[BTF_KIND_TYPE_TAG] = &modifier_ops,
[BTF_KIND_ENUM64] = &enum64_ops,
+ [BTF_KIND_LOC_PARAM] = &loc_param_ops,
+ [BTF_KIND_LOC_PROTO] = &loc_proto_ops,
+ [BTF_KIND_LOCSEC] = &locsec_ops,
};
static s32 btf_check_meta(struct btf_verifier_env *env,
@@ -5472,7 +5769,8 @@ static bool btf_resolve_valid(struct btf_verifier_env *env,
if (!env_type_is_resolved(env, type_id))
return false;
- if (btf_type_is_struct(t) || btf_type_is_datasec(t))
+ if (btf_type_is_struct(t) || btf_type_is_datasec(t) ||
+ btf_type_is_loc_proto(t) || btf_type_is_locsec(t))
return !btf_resolved_type_id(btf, type_id) &&
!btf_resolved_type_size(btf, type_id);
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index 618167cab4e6..a85d293f4127 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -92,7 +92,9 @@ enum {
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
-
+ BTF_KIND_LOC_PARAM = 20, /* Location parameter information */
+ BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */
+ BTF_KIND_LOCSEC = 22, /* Location section */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
};
@@ -212,4 +214,65 @@ struct btf_enum64 {
__u32 val_hi32;
};
+/*
+ * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
+ * that contains flags specifying the contents of the vlen-specified
+ * number of 4-byte values that follow.
+ */
+struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+};
+
+/*
+ * The combination of size, vlen and flags gives us the means to interpret
+ * the following vlen-specified set of 4-byte values:
+ *
+ * - a BTF_LOC_PARAM_CONST is a constant value; combination
+ * of size, vlen and _SIGNED flag determines it. If the value requires
+ * 64 bits it is stored in {lo,hi} order.
+ * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
+ * normalized with respect to kernel/module base address.
+ * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
+ * with vlen 2 it is a multi-register parameter.
+ * - a _REG | OFFSET describes an address without dereferencing it.
+ * - a _REG | DEREF with vlen 1 dereferences the value in the register
+ * number specified.
+ * - a REG | DEREF | OFFSET with vlen specifies the register value in
+ * the first 4-byte value and the offset in the remainder.
+ */
+enum btf_loc_param_flags {
+ BTF_LOC_PARAM_SIGNED = 0x1,
+ BTF_LOC_PARAM_CONST = 0x2,
+ BTF_LOC_PARAM_ADDR = 0x4,
+ BTF_LOC_PARAM_REG = 0x8,
+ BTF_LOC_PARAM_DEREF = 0x10,
+ BTF_LOC_PARAM_OFFSET = 0x20,
+};
+
+/*
+ * BTF_KIND_LOC_PROTO specifies location prototypes; i.e. how locations relate
+ * to parameters; a struct btf_type of BTF_KIND_LOC_PROTO is followed by a
+ * vlen-specified number of __u32 BTF type ids which specify the associated
+ * BTF_KIND_LOC_PARAM for each function parameter associated with the
+ * location. The type should either be 0 (no location info) or point at
+ * a BTF_KIND_LOC_PARAM.
+ */
+
+/*
+ * BTF_KIND_LOCSEC consists of vlen-specified number of "struct btf_loc"
+ * containing location site-specific information for a specific ELF section;
+ * for example locations in ".text" are in a LOCSEC named "inline.text".
+ *
+ * - function (func)
+ * - location prototype type id (loc_proto)
+ * - address offset (offset) relative to kernel base address
+ */
+
+struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+};
+
#endif /* _UAPI__LINUX_BTF_H__ */
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info
2026-09-16 7:41 ` [PATCH v3 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
@ 2026-09-16 9:03 ` bot+bpf-ci
0 siblings, 0 replies; 42+ messages in thread
From: bot+bpf-ci @ 2026-09-16 9:03 UTC (permalink / raw)
To: alan.maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 4729 bytes --]
> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> index 618167cab4e61..a85d293f4127f 100644
> --- a/include/uapi/linux/btf.h
> +++ b/include/uapi/linux/btf.h
> @@ -92,7 +92,9 @@ enum {
> BTF_KIND_DECL_TAG = 17, /* Decl Tag */
> BTF_KIND_TYPE_TAG = 18, /* Type Tag */
> BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
> -
> + BTF_KIND_LOC_PARAM = 20, /* Location parameter information */
> + BTF_KIND_LOC_PROTO = 21, /* Location prototype for site */
> + BTF_KIND_LOCSEC = 22, /* Location section */
> NR_BTF_KINDS,
> BTF_KIND_MAX = NR_BTF_KINDS - 1,
> };
[ ... ]
> @@ -212,4 +214,65 @@ struct btf_enum64 {
> __u32 val_hi32;
> };
>
> +/*
> + * BTF_KIND_LOC_PARAM is followed by a single "struct btf_loc_param"
> + * that contains flags specifying the contents of the vlen-specified
> + * number of 4-byte values that follow.
> + */
> +struct btf_loc_param {
> + __u32 flags;
> + __u32 values[];
> +};
> +
> +/*
> + * The combination of size, vlen and flags gives us the means to interpret
> + * the following vlen-specified set of 4-byte values:
> + *
> + * - a BTF_LOC_PARAM_CONST is a constant value; combination
> + * of size, vlen and _SIGNED flag determines it. If the value requires
> + * 64 bits it is stored in {lo,hi} order.
> + * - a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
> + * normalized with respect to kernel/module base address.
> + * - a BTF_LOC_PARAM_REG with vlen 1 is a simple register number;
> + * with vlen 2 it is a multi-register parameter.
> + * - a _REG | OFFSET describes an address without dereferencing it.
> + * - a _REG | DEREF with vlen 1 dereferences the value in the register
> + * number specified.
> + * - a REG | DEREF | OFFSET with vlen specifies the register value in
> + * the first 4-byte value and the offset in the remainder.
The REG | DEREF | OFFSET bullet is missing the vlen count after "with
vlen". Looking at the other bullets in this list, each states the exact
count ("with vlen 1", "with vlen 2").
Would it help to specify the count here? Since the description mentions
"the first 4-byte value and the offset in the remainder", it appears the
intended count is 2.
> + */
> +enum btf_loc_param_flags {
> + BTF_LOC_PARAM_SIGNED = 0x1,
> + BTF_LOC_PARAM_CONST = 0x2,
> + BTF_LOC_PARAM_ADDR = 0x4,
> + BTF_LOC_PARAM_REG = 0x8,
> + BTF_LOC_PARAM_DEREF = 0x10,
> + BTF_LOC_PARAM_OFFSET = 0x20,
> +};
[ ... ]
Looking at the unchanged struct btf_type definition in include/uapi/linux/btf.h
around line 57:
__u32 info;
/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
* FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
* "type" is a type_id referring to another type.
*/
union {
__u32 size;
__u32 type;
};
Does the comment need updating for the new kinds? Looking at the
implementation in kernel/bpf/btf.c, btf_loc_param_check_meta() reads
t->size and validates it ("size = t->size; if (size > 16 ||
!is_power_of_2(size))"), so LOC_PARAM uses the size member.
For LOC_PROTO and LOCSEC, both btf_loc_proto_resolve() and
btf_locsec_resolve() require t->type == 0 ("if (t->type)... Invalid
loc_proto/locsec type").
Should the comment document which arm these new kinds use, the way it
already does for ENUM64, DECL_TAG, and TYPE_TAG?
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 7daf4c286c9b2..af5f161b5bddc 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
[ ... ]
> @@ -796,7 +815,9 @@ static bool btf_type_needs_resolve(const struct btf_type *t)
> btf_type_is_var(t) ||
> btf_type_is_func(t) ||
> btf_type_is_decl_tag(t) ||
> - btf_type_is_datasec(t);
> + btf_type_is_datasec(t) ||
> + btf_type_is_loc_proto(t) ||
> + btf_type_is_locsec(t);
> }
LOC_PROTO and LOCSEC are added to btf_type_needs_resolve() and
btf_type_nosize(), but I don't see them added to
btf_type_is_resolve_source_only() (kernel/bpf/btf.c:786-791).
DATASEC appears in both btf_type_needs_resolve and
btf_type_is_resolve_source_only. The two new section-like kinds follow
the same pattern as DATASEC (both are containers referencing other
types), so should they also be in btf_type_is_resolve_source_only?
Without this, a typedef or pointer to LOC_PROTO/LOCSEC would be handled
differently than a typedef or pointer to DATASEC.
[ ... ]
---
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/35072175008
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
2026-09-16 7:41 ` [PATCH v3 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 7:55 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
2026-09-16 7:41 ` [PATCH v3 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
` (8 subsequent siblings)
10 siblings, 2 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
Add support for new kinds to libbpf. BTF_KIND_LOC_PARAM and
BTF_KIND_LOC_PROTO are dedup-able so add support for their
deduplication, whereas since BTF_KIND_LOCSEC contains a unique
offset it is not. LOC_PARAM is considered a primary type
since it contains no external references; LOC_PROTO is a
reference type consisting of LOC_PARAM references so they
are handled in the primary and reference dedup phases
respectively.
For BTF field iteration, BTF_KIND_LOCSEC needs 2 m_offs[] values
for the associated KIND_FUNC and KIND_LOC_PROTO type ids in
each LOCSEC entry.
Add APIs to add location param, location prototypes and location
sections and btf_is_* tests, data accessors for each.
For BTF distillation we add location info to split BTF.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
---
tools/lib/bpf/btf.c | 373 +++++++++++++++++++++++++++++++-
tools/lib/bpf/btf.h | 50 +++++
tools/lib/bpf/btf_dump.c | 9 +
tools/lib/bpf/btf_iter.c | 18 ++
tools/lib/bpf/libbpf.map | 6 +
tools/lib/bpf/libbpf_internal.h | 2 +-
6 files changed, 453 insertions(+), 5 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 908bd344229d..332a8388fcc4 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -57,6 +57,9 @@ static struct btf_layout layouts[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = { sizeof(struct btf_decl_tag), 0, 0 },
[BTF_KIND_TYPE_TAG] = { 0, 0, 0 },
[BTF_KIND_ENUM64] = { 0, sizeof(struct btf_enum64), 0 },
+[BTF_KIND_LOC_PARAM] = { sizeof(struct btf_loc_param), sizeof(__u32), 0 },
+[BTF_KIND_LOC_PROTO] = { 0, sizeof(__u32), 0 },
+[BTF_KIND_LOCSEC] = { 0, sizeof(struct btf_loc), 0 },
};
struct btf {
@@ -486,6 +489,12 @@ static int btf_type_size(const struct btf *btf, const struct btf_type *t)
return base_size + vlen * sizeof(struct btf_var_secinfo);
case BTF_KIND_DECL_TAG:
return base_size + sizeof(struct btf_decl_tag);
+ case BTF_KIND_LOC_PARAM:
+ return base_size + sizeof(struct btf_loc_param) + vlen * sizeof(__u32);
+ case BTF_KIND_LOC_PROTO:
+ return base_size + vlen * sizeof(__u32);
+ case BTF_KIND_LOCSEC:
+ return base_size + vlen * sizeof(struct btf_loc);
default:
return btf_type_size_unknown(btf, t);
}
@@ -501,12 +510,15 @@ static void btf_bswap_type_base(struct btf_type *t)
static int btf_bswap_type_rest(struct btf_type *t)
{
struct btf_var_secinfo *v;
+ struct btf_loc_param *lp;
struct btf_enum64 *e64;
struct btf_member *m;
struct btf_array *a;
struct btf_param *p;
struct btf_enum *e;
+ struct btf_loc *l;
__u32 vlen = btf_vlen(t);
+ __u32 *d;
int i;
switch (btf_kind(t)) {
@@ -569,6 +581,23 @@ static int btf_bswap_type_rest(struct btf_type *t)
case BTF_KIND_DECL_TAG:
btf_decl_tag(t)->component_idx = bswap_32(btf_decl_tag(t)->component_idx);
return 0;
+ case BTF_KIND_LOC_PARAM:
+ lp = btf_loc_param(t);
+ lp->flags = bswap_32(lp->flags);
+ for (i = 0, d = (__u32 *)(lp + 1); i < vlen; i++, d++)
+ *d = bswap_32(*d);
+ return 0;
+ case BTF_KIND_LOC_PROTO:
+ for (i = 0, d = btf_loc_proto_params(t); i < vlen; i++, d++)
+ *d = bswap_32(*d);
+ return 0;
+ case BTF_KIND_LOCSEC:
+ for (i = 0, l = btf_locsec_locs(t); i < vlen; i++, l++) {
+ l->func = bswap_32(l->func);
+ l->loc_proto = bswap_32(l->loc_proto);
+ l->offset = bswap_32(l->offset);
+ }
+ return 0;
default:
pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
return -EINVAL;
@@ -745,6 +774,33 @@ static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __
}
break;
}
+ case BTF_KIND_LOC_PARAM:
+ break;
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *p = btf_loc_proto_params(t);
+
+ n = btf_vlen(t);
+ for (i = 0; i < n; i++, p++) {
+ err = btf_validate_id(btf, *p, id);
+ if (err)
+ return err;
+ }
+ break;
+ }
+ case BTF_KIND_LOCSEC: {
+ const struct btf_loc *l = btf_locsec_locs(t);
+
+ n = btf_vlen(t);
+ for (i = 0; i < n; i++, l++) {
+ if (!err)
+ err = btf_validate_id(btf, l->func, id);
+ if (!err)
+ err = btf_validate_id(btf, l->loc_proto, id);
+ if (err)
+ return err;
+ }
+ break;
+ }
default:
/* Kind may be represented in kind layout information. */
if (btf_type_size_unknown(btf, t) < 0) {
@@ -3344,6 +3400,208 @@ int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
return btf_add_decl_tag(btf, value, ref_type_id, component_idx, 1);
}
+/*
+ * Append new BTF_KIND_LOC_PARAM with specified size and flags. Values are
+ * added via btf__add_loc_param_value().
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags)
+{
+ struct btf_loc_param *p;
+ struct btf_type *t;
+ int sz, err;
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(struct btf_type) + sizeof(*p);
+ t = btf_add_type_mem(btf, sz);
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ t->name_off = 0;
+ t->info = btf_type_info(BTF_KIND_LOC_PARAM, 0, 0);
+ t->size = size;
+
+ p = btf_loc_param(t);
+ p->flags = flags;
+
+ return btf_commit_type(btf, sz);
+}
+
+int btf__add_loc_param_value(struct btf *btf, __u32 value)
+{
+ struct btf_type *t;
+ int sz, err;
+ __u32 *v;
+
+ /* last type should be BTF_KIND_LOC_PARAM */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_loc_param(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(value);
+ v = btf_add_type_mem(btf, sz);
+ if (!v)
+ return libbpf_err(-ENOMEM);
+ *v = value;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
+/*
+ * Append new BTF_KIND_LOC_PROTO
+ *
+ * The prototype is then populated with 0 or more BTF_KIND_LOC_PARAMs via
+ * btf__add_loc_proto_param(); similar to how btf__add_func_param() adds
+ * parameters to a FUNC_PROTO.
+ *
+ * Returns:
+ * - >0, type ID of newly added BTF type;
+ * - <0, on error.
+ */
+int btf__add_loc_proto(struct btf *btf)
+{
+ struct btf_type *t;
+ int err;
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ t = btf_add_type_mem(btf, sizeof(struct btf_type));
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ t->name_off = 0;
+ t->info = btf_type_info(BTF_KIND_LOC_PROTO, 0, 0);
+ t->size = 0;
+
+ return btf_commit_type(btf, sizeof(struct btf_type));
+}
+
+int btf__add_loc_proto_param(struct btf *btf, __u32 id)
+{
+ struct btf_type *t;
+ int sz, err;
+ __u32 *p;
+
+ if (validate_type_id(id))
+ return libbpf_err(-EINVAL);
+
+ /* last type should be BTF_KIND_LOC_PROTO */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_loc_proto(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(__u32);
+ p = btf_add_type_mem(btf, sz);
+ if (!p)
+ return libbpf_err(-ENOMEM);
+ *p = id;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
+int btf__add_locsec(struct btf *btf, const char *name)
+{
+ struct btf_type *t;
+ int name_off = 0;
+ int err;
+
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ t = btf_add_type_mem(btf, sizeof(struct btf_type));
+ if (!t)
+ return libbpf_err(-ENOMEM);
+
+ if (!str_is_empty(name)) {
+ name_off = btf__add_str(btf, name);
+ if (name_off < 0)
+ return libbpf_err(name_off);
+ }
+ t->name_off = name_off;
+ t->info = btf_type_info(BTF_KIND_LOCSEC, 0, 0);
+ t->size = 0;
+
+ return btf_commit_type(btf, sizeof(struct btf_type));
+}
+
+int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,
+ __u32 offset)
+{
+ struct btf_type *t;
+ struct btf_loc *l;
+ int sz, err;
+
+ if (validate_type_id(func) || validate_type_id(loc_proto))
+ return libbpf_err(-EINVAL);
+
+ /* last type should be BTF_KIND_LOCSEC */
+ if (btf->nr_types == 0)
+ return libbpf_err(-EINVAL);
+ t = btf_last_type(btf);
+ if (!btf_is_locsec(t))
+ return libbpf_err(-EINVAL);
+
+ /* decompose and invalidate raw data */
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
+
+ sz = sizeof(*l);
+ l = btf_add_type_mem(btf, sz);
+ if (!l)
+ return libbpf_err(-ENOMEM);
+
+ l->func = func;
+ l->loc_proto = loc_proto;
+ l->offset = offset;
+
+ /* update parent type's vlen */
+ t = btf_last_type(btf);
+ err = btf_type_inc_vlen(t);
+ if (err)
+ return libbpf_err(err);
+
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
+ return 0;
+}
+
struct btf_ext_sec_info_param {
__u32 off;
__u32 len;
@@ -4114,8 +4372,8 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, const struct btf_dedup_o
for (i = 1; i < type_cnt; i++) {
struct btf_type *t = btf_type_by_id(d->btf, i);
- /* VAR and DATASEC are never deduped and are self-canonical */
- if (btf_is_var(t) || btf_is_datasec(t))
+ /* VAR, DATASEC and LOCSEC are never deduped and are self-canonical */
+ if (btf_is_var(t) || btf_is_datasec(t) || btf_is_locsec(t))
d->map[i] = i;
else
d->map[i] = BTF_UNPROCESSED_ID;
@@ -4395,6 +4653,47 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
btf_is_any_enum(t1) && btf_is_any_enum(t2);
}
+static long btf_hash_loc_param(struct btf_type *t)
+{
+ struct btf_loc_param *p = btf_loc_param(t);
+ long h = btf_hash_common(t);
+ int i, vlen = btf_vlen(t);
+
+ h = hash_combine(h, p->flags);
+
+ for (i = 0; i < vlen; i++)
+ h = hash_combine(h, p->values[i]);
+ return h;
+}
+
+static long btf_hash_loc_proto(struct btf_type *t)
+{
+ __u32 *p = btf_loc_proto_params(t);
+ long h = btf_hash_common(t);
+ int i, vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++, p++)
+ h = hash_combine(h, *p);
+ return h;
+}
+
+static bool btf_equal_loc_param(struct btf_type *t1, struct btf_type *t2)
+{
+ struct btf_loc_param *p1 = btf_loc_param(t1);
+ struct btf_loc_param *p2 = btf_loc_param(t2);
+ int i, vlen = btf_vlen(t1);
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+ if (p1->flags != p2->flags)
+ return false;
+ for (i = 0; i < vlen; i++) {
+ if (p1->values[i] != p2->values[i])
+ return false;
+ }
+ return true;
+}
+
/*
* Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs,
* as referenced type IDs equivalence is established separately during type
@@ -4589,7 +4888,8 @@ static int btf_dedup_prep(struct btf_dedup *d)
switch (btf_kind(t)) {
case BTF_KIND_VAR:
case BTF_KIND_DATASEC:
- /* VAR and DATASEC are never hash/deduplicated */
+ case BTF_KIND_LOCSEC:
+ /* VAR DATASEC and LOCSEC are never hash/deduplicated */
continue;
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
@@ -4622,6 +4922,12 @@ static int btf_dedup_prep(struct btf_dedup *d)
case BTF_KIND_FUNC_PROTO:
h = btf_hash_fnproto(t);
break;
+ case BTF_KIND_LOC_PARAM:
+ h = btf_hash_loc_param(t);
+ break;
+ case BTF_KIND_LOC_PROTO:
+ h = btf_hash_loc_proto(t);
+ break;
default:
pr_debug("unknown kind %d for type [%d]\n", btf_kind(t), type_id);
return -EINVAL;
@@ -4664,6 +4970,8 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_DATASEC:
case BTF_KIND_DECL_TAG:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
return 0;
case BTF_KIND_INT:
@@ -4713,6 +5021,18 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
}
break;
+ case BTF_KIND_LOC_PARAM:
+ h = btf_hash_loc_param(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = hash_entry->value;
+ cand = btf_type_by_id(d->btf, cand_id);
+ if (btf_equal_loc_param(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+
default:
return -EINVAL;
}
@@ -5145,6 +5465,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
return 1;
}
+ case BTF_KIND_LOC_PARAM:
+ return btf_equal_loc_param(cand_type, canon_type);
+
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
+ return 0;
+
default:
return -EINVAL;
}
@@ -5489,6 +5816,37 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
break;
}
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *p1, *p2;
+ __u32 i, vlen;
+
+ p1 = btf_loc_proto_params(t);
+ vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++, p1++) {
+ ref_type_id = btf_dedup_ref_type(d, *p1);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ *p1 = ref_type_id;
+ }
+
+ h = btf_hash_loc_proto(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = hash_entry->value;
+ cand = btf_type_by_id(d->btf, cand_id);
+ if (!btf_equal_common(t, cand))
+ continue;
+ vlen = btf_vlen(cand);
+ p1 = btf_loc_proto_params(t);
+ p2 = btf_loc_proto_params(cand);
+ if (memcmp(p1, p2, vlen * sizeof(__u32)) == 0) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+ }
+
default:
return -EINVAL;
}
@@ -5970,8 +6328,11 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_VOLATILE:
+ case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
dist->id_map[*id] = *id;
break;
default:
@@ -5997,7 +6358,7 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i)
static int btf_add_distilled_types(struct btf_distill *dist)
{
- bool adding_to_base = dist->pipe.dst->start_id == 1;
+ bool adding_to_base = dist->pipe.dst->base_btf == NULL;
int id = btf__type_cnt(dist->pipe.dst);
struct btf_type *t;
int i, err = 0;
@@ -6065,8 +6426,12 @@ static int btf_add_distilled_types(struct btf_distill *dist)
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
case BTF_KIND_VOLATILE:
+ case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_TYPE_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
/* All other types are added to split BTF. */
if (adding_to_base)
continue;
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index 587172c0de08..57f12630c5d1 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -274,6 +274,20 @@ LIBBPF_API int btf__add_decl_tag(struct btf *btf, const char *value, int ref_typ
LIBBPF_API int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
int component_idx);
+/* location construction APIs */
+LIBBPF_API int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags);
+
+LIBBPF_API int btf__add_loc_param_value(struct btf *btf, __u32 value);
+
+LIBBPF_API int btf__add_loc_proto(struct btf *btf);
+
+LIBBPF_API int btf__add_loc_proto_param(struct btf *btf, __u32 id);
+
+LIBBPF_API int btf__add_locsec(struct btf *btf, const char *name);
+
+LIBBPF_API int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,
+ __u32 offset);
+
struct btf_dedup_opts {
size_t sz;
/* optional .BTF.ext info to dedup along the main BTF info */
@@ -431,6 +445,12 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enum for up-to 64bit values */
+#define BTF_KIND_LOC_PARAM 20 /* Parameter at location */
+#define BTF_KIND_LOC_PROTO 21 /* Parameter set at location */
+#define BTF_KIND_LOCSEC 22 /* Section containing location info */
+
+struct btf_loc_param;
+struct btf_loc;
static inline __u16 btf_kind(const struct btf_type *t)
{
@@ -569,6 +589,21 @@ static inline bool btf_is_any_enum(const struct btf_type *t)
return btf_is_enum(t) || btf_is_enum64(t);
}
+static inline bool btf_is_loc_param(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOC_PARAM;
+}
+
+static inline bool btf_is_loc_proto(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOC_PROTO;
+}
+
+static inline bool btf_is_locsec(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_LOCSEC;
+}
+
static inline bool btf_kind_core_compat(const struct btf_type *t1,
const struct btf_type *t2)
{
@@ -683,6 +718,21 @@ static inline struct btf_decl_tag *btf_decl_tag(const struct btf_type *t)
return (struct btf_decl_tag *)(t + 1);
}
+static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t)
+{
+ return (struct btf_loc_param *)(t + 1);
+}
+
+static inline __u32 *btf_loc_proto_params(const struct btf_type *t)
+{
+ return (__u32 *)(t + 1);
+}
+
+static inline struct btf_loc *btf_locsec_locs(const struct btf_type *t)
+{
+ return (struct btf_loc *)(t + 1);
+}
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index 123c448f20c7..fa995c02a170 100644
--- a/tools/lib/bpf/btf_dump.c
+++ b/tools/lib/bpf/btf_dump.c
@@ -328,6 +328,9 @@ static int btf_dump_mark_referenced(struct btf_dump *d)
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
case BTF_KIND_FLOAT:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
break;
case BTF_KIND_VOLATILE:
@@ -609,6 +612,9 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
case BTF_KIND_VAR:
case BTF_KIND_DATASEC:
case BTF_KIND_DECL_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
d->type_states[id].order_state = ORDERED;
return 0;
@@ -2525,6 +2531,9 @@ static int btf_dump_dump_type_data(struct btf_dump *d,
case BTF_KIND_FUNC:
case BTF_KIND_FUNC_PROTO:
case BTF_KIND_DECL_TAG:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
err = btf_dump_unsupported_data(d, t, id);
break;
case BTF_KIND_INT:
diff --git a/tools/lib/bpf/btf_iter.c b/tools/lib/bpf/btf_iter.c
index 9a6c822c2294..199063b55ebe 100644
--- a/tools/lib/bpf/btf_iter.c
+++ b/tools/lib/bpf/btf_iter.c
@@ -29,6 +29,7 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
+ case BTF_KIND_LOC_PARAM:
it->desc = (struct btf_field_desc) {};
break;
case BTF_KIND_FWD:
@@ -71,6 +72,20 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
1, {offsetof(struct btf_var_secinfo, type)}
};
break;
+ case BTF_KIND_LOC_PROTO:
+ it->desc = (struct btf_field_desc) {
+ 0, {},
+ sizeof(__u32),
+ 1, {0}};
+ break;
+ case BTF_KIND_LOCSEC:
+ it->desc = (struct btf_field_desc) {
+ 0, {},
+ sizeof(struct btf_loc),
+ 2, {offsetof(struct btf_loc, func),
+ offsetof(struct btf_loc, loc_proto)}};
+ break;
+
default:
return -EINVAL;
}
@@ -94,6 +109,9 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t,
case BTF_KIND_DECL_TAG:
case BTF_KIND_TYPE_TAG:
case BTF_KIND_DATASEC:
+ case BTF_KIND_LOC_PARAM:
+ case BTF_KIND_LOC_PROTO:
+ case BTF_KIND_LOCSEC:
it->desc = (struct btf_field_desc) {
1, {offsetof(struct btf_type, name_off)}
};
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 08ab2ea881fb..7f51783df129 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -460,6 +460,12 @@ LIBBPF_1.8.0 {
global:
bpf_program__attach_tracing_multi;
bpf_program__clone;
+ btf__add_loc_param;
+ btf__add_loc_param_value;
+ btf__add_loc_proto;
+ btf__add_loc_proto_param;
+ btf__add_locsec;
+ btf__add_locsec_loc;
btf__find_by_name_kind_own;
btf__new_empty_opts;
} LIBBPF_1.7.0;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index cb4d96233844..546f65b95cf4 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -580,7 +580,7 @@ struct btf_field_desc {
/* member struct size, or zero, if no members */
int m_sz;
/* repeated per-member offsets */
- int m_off_cnt, m_offs[1];
+ int m_off_cnt, m_offs[2];
};
struct btf_field_iter {
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
2026-09-16 7:41 ` [PATCH v3 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
@ 2026-09-16 7:55 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-09-16 7:55 UTC (permalink / raw)
To: Alan Maguire; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] bpf: Missing string representations for new BTF kinds
--
commit 98be00a89bec3663ddafbd1e828f7723cfe159cc
Author: Alan Maguire <alan.maguire@oracle.com>
libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
This commit adds libbpf support for the new BTF_KIND_LOC_PARAM,
BTF_KIND_LOC_PROTO, and BTF_KIND_LOCSEC kinds. It implements data
accessors, validation checks, and handles their deduplication during
the primary and reference dedup phases.
> diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
> index 587172c0de089..57f12630c5d15 100644
> --- a/tools/lib/bpf/btf.h
> +++ b/tools/lib/bpf/btf.h
[ ... ]
> @@ -431,6 +445,12 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
> #define BTF_KIND_DECL_TAG 17 /* Decl Tag */
> #define BTF_KIND_TYPE_TAG 18 /* Type Tag */
> #define BTF_KIND_ENUM64 19 /* Enum for up-to 64bit values */
> +#define BTF_KIND_LOC_PARAM 20 /* Parameter at location */
> +#define BTF_KIND_LOC_PROTO 21 /* Parameter set at location */
> +#define BTF_KIND_LOCSEC 22 /* Section containing location info */
> +
> +struct btf_loc_param;
> +struct btf_loc;
[Severity: Low]
Should these new BTF kinds also be added to the __btf_kind_str() string
mapping function in tools/lib/bpf/libbpf.c?
When libbpf encounters errors or needs to log information about these new
types, it relies on __btf_kind_str(). Because they are missing from the
switch statement in that function, any debug or error logs formatting these
types will print "unknown" instead of their actual string names.
>
> static inline __u16 btf_kind(const struct btf_type *t)
> {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260916074118.1007116-1-alan.maguire@oracle.com?part=2
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
2026-09-16 7:41 ` [PATCH v3 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
2026-09-16 7:55 ` sashiko-bot
@ 2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: bot+bpf-ci @ 2026-09-16 9:03 UTC (permalink / raw)
To: alan.maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 6360 bytes --]
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index 908bd344229d5..332a8388fcc41 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
[ ... ]
> @@ -3344,6 +3400,208 @@ int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
> return btf_add_decl_tag(btf, value, ref_type_id, component_idx, 1);
> }
>
> +/*
> + * Append new BTF_KIND_LOC_PARAM with specified size and flags. Values are
> + * added via btf__add_loc_param_value().
> + *
> + * Returns:
> + * - >0, type ID of newly added BTF type;
> + * - <0, on error.
> + */
> +int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags)
> +{
> + struct btf_loc_param *p;
> + struct btf_type *t;
> + int sz, err;
> +
> + err = btf_ensure_modifiable(btf);
> + if (err)
> + return libbpf_err(err);
> +
> + sz = sizeof(struct btf_type) + sizeof(*p);
> + t = btf_add_type_mem(btf, sz);
> + if (!t)
> + return libbpf_err(-ENOMEM);
> +
> + t->name_off = 0;
> + t->info = btf_type_info(BTF_KIND_LOC_PARAM, 0, 0);
> + t->size = size;
> +
> + p = btf_loc_param(t);
> + p->flags = flags;
> +
> + return btf_commit_type(btf, sz);
> +}
Does this API validate the size and flags arguments?
The kernel's btf_loc_param_check_meta() in kernel/bpf/btf.c requires size to
be a power of 2, between 1 and 16:
kernel/bpf/btf.c:btf_loc_param_check_meta() {
size = t->size;
if (size > 16 || !is_power_of_2(size)) {
btf_verifier_log_type(env, t, "Unexpected size");
return -EINVAL;
}
The equivalent API btf__add_int() validates these constraints up front:
tools/lib/bpf/btf.c:btf__add_int() {
/* byte_sz must be power of 2 */
if (!byte_sz || (byte_sz & (byte_sz - 1)) || byte_sz > 16)
return libbpf_err(-EINVAL);
if (encoding & ~(BTF_INT_SIGNED | BTF_INT_CHAR | BTF_INT_BOOL))
return libbpf_err(-EINVAL);
Without validation, btf__add_loc_param(btf, 0, ...) or
btf__add_loc_param(btf, 24, ...) succeed but only fail later at BTF_LOAD time.
Similarly, flags is unchecked against the enum btf_loc_param_flags mask
(0x3f). Undefined high bits would collide with future flag definitions.
> +
> +int btf__add_loc_param_value(struct btf *btf, __u32 value)
> +{
> + struct btf_type *t;
> + int sz, err;
> + __u32 *v;
> +
> + /* last type should be BTF_KIND_LOC_PARAM */
> + if (btf->nr_types == 0)
> + return libbpf_err(-EINVAL);
> + t = btf_last_type(btf);
> + if (!btf_is_loc_param(t))
> + return libbpf_err(-EINVAL);
> +
> + /* decompose and invalidate raw data */
> + err = btf_ensure_modifiable(btf);
> + if (err)
> + return libbpf_err(err);
> +
> + sz = sizeof(value);
> + v = btf_add_type_mem(btf, sz);
> + if (!v)
> + return libbpf_err(-ENOMEM);
> + *v = value;
> +
> + /* update parent type's vlen */
> + t = btf_last_type(btf);
> + err = btf_type_inc_vlen(t);
> + if (err)
> + return libbpf_err(err);
> +
> + btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
> + return 0;
> +}
> +
> +/*
> + * Append new BTF_KIND_LOC_PROTO
> + *
> + * The prototype is then populated with 0 or more BTF_KIND_LOC_PARAMs via
> + * btf__add_loc_proto_param(); similar to how btf__add_func_param() adds
> + * parameters to a FUNC_PROTO.
> + *
> + * Returns:
> + * - >0, type ID of newly added BTF type;
> + * - <0, on error.
> + */
> +int btf__add_loc_proto(struct btf *btf)
> +{
> + struct btf_type *t;
> + int err;
> +
> + err = btf_ensure_modifiable(btf);
> + if (err)
> + return libbpf_err(err);
> +
> + t = btf_add_type_mem(btf, sizeof(struct btf_type));
> + if (!t)
> + return libbpf_err(-ENOMEM);
> +
> + t->name_off = 0;
> + t->info = btf_type_info(BTF_KIND_LOC_PROTO, 0, 0);
> + t->size = 0;
> +
> + return btf_commit_type(btf, sizeof(struct btf_type));
> +}
> +
> +int btf__add_loc_proto_param(struct btf *btf, __u32 id)
> +{
> + struct btf_type *t;
> + int sz, err;
> + __u32 *p;
> +
> + if (validate_type_id(id))
> + return libbpf_err(-EINVAL);
> +
> + /* last type should be BTF_KIND_LOC_PROTO */
> + if (btf->nr_types == 0)
> + return libbpf_err(-EINVAL);
> + t = btf_last_type(btf);
> + if (!btf_is_loc_proto(t))
> + return libbpf_err(-EINVAL);
> +
> + /* decompose and invalidate raw data */
> + err = btf_ensure_modifiable(btf);
> + if (err)
> + return libbpf_err(err);
> +
> + sz = sizeof(__u32);
> + p = btf_add_type_mem(btf, sz);
> + if (!p)
> + return libbpf_err(-ENOMEM);
> + *p = id;
> +
> + /* update parent type's vlen */
> + t = btf_last_type(btf);
> + err = btf_type_inc_vlen(t);
> + if (err)
> + return libbpf_err(err);
> +
> + btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
> + return 0;
> +}
> +
> +int btf__add_locsec(struct btf *btf, const char *name)
> +{
> + struct btf_type *t;
> + int name_off = 0;
> + int err;
> +
> + err = btf_ensure_modifiable(btf);
> + if (err)
> + return libbpf_err(err);
> +
> + t = btf_add_type_mem(btf, sizeof(struct btf_type));
> + if (!t)
> + return libbpf_err(-ENOMEM);
> +
> + if (!str_is_empty(name)) {
> + name_off = btf__add_str(btf, name);
> + if (name_off < 0)
> + return libbpf_err(name_off);
> + }
> + t->name_off = name_off;
> + t->info = btf_type_info(BTF_KIND_LOCSEC, 0, 0);
> + t->size = 0;
> +
> + return btf_commit_type(btf, sizeof(struct btf_type));
> +}
Can btf__add_locsec() succeed with a NULL or empty name?
The kernel unconditionally rejects a LOCSEC with name_off == 0:
kernel/bpf/btf.c:btf_locsec_check_meta() {
if (!t->name_off ||
!btf_name_valid_section(env->btf, t->name_off)) {
btf_verifier_log_type(env, t, "Invalid name");
return -EINVAL;
}
btf__add_locsec(btf, NULL) succeeds and returns a valid type ID, but the BTF
blob becomes unloadable with BTF_LOAD -EINVAL only later when the caller
tries to load it.
The analogous API btf__add_datasec() validates this up front:
tools/lib/bpf/btf.c:btf__add_datasec() {
/* non-empty name */
if (str_is_empty(name))
return libbpf_err(-EINVAL);
name_off = btf__add_str(btf, name);
LOCSEC is validated by the same kernel helper, btf_name_valid_section(), and
there is no kind of LOCSEC the kernel will accept without a name.
[ ... ]
---
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/35072175008
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC]
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
2026-09-16 7:41 ` [PATCH v3 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
2026-09-16 7:41 ` [PATCH v3 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 8:44 ` bot+bpf-ci
2026-09-18 18:34 ` Eduard Zingerman
2026-09-16 7:41 ` [PATCH v3 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests Alan Maguire
` (7 subsequent siblings)
10 siblings, 2 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
Add support to dump, encode and validate new location-related kinds.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/testing/selftests/bpf/btf_helpers.c | 36 ++++++++++++++++++++++-
tools/testing/selftests/bpf/test_btf.h | 16 ++++++++++
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c
index 1c1c2c26690a..972403f6a743 100644
--- a/tools/testing/selftests/bpf/btf_helpers.c
+++ b/tools/testing/selftests/bpf/btf_helpers.c
@@ -27,11 +27,14 @@ static const char * const btf_kind_str_mapping[] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
static const char *btf_kind_str(__u16 kind)
{
- if (kind > BTF_KIND_ENUM64)
+ if (kind > BTF_KIND_LOCSEC)
return "UNKNOWN";
return btf_kind_str_mapping[kind];
}
@@ -203,6 +206,37 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
fprintf(out, " type_id=%u component_idx=%d",
t->type, btf_decl_tag(t)->component_idx);
break;
+ case BTF_KIND_LOC_PARAM: {
+ struct btf_loc_param *p = btf_loc_param(t);
+ __u32 *v = (__u32 *)(p + 1);
+
+ fprintf(out, " size=%u flags=0x%x vlen=%u", t->size, p->flags, vlen);
+ for (i = 0; i < vlen; i++, v++) {
+ if (p->flags & BTF_LOC_PARAM_SIGNED)
+ fprintf(out, "\n\tvalue=%d", (__s32)*v);
+ else
+ fprintf(out, "\n\tvalue=%u", *v);
+ }
+ break;
+ }
+ case BTF_KIND_LOC_PROTO: {
+ const __u32 *p = btf_loc_proto_params(t);
+
+ fprintf(out, " vlen=%u", vlen);
+ for (i = 0; i < vlen; i++, p++)
+ fprintf(out, "\n\ttype_id=%u", *p);
+ break;
+ }
+ case BTF_KIND_LOCSEC: {
+ const struct btf_loc *l = btf_locsec_locs(t);
+
+ fprintf(out, " vlen=%u", vlen);
+ for (i = 0; i < vlen; i++, l++) {
+ fprintf(out, "\n\tfunc_type_id=%u loc_proto_type_id=%u offset=%u",
+ l->func, l->loc_proto, l->offset);
+ }
+ break;
+ }
default:
break;
}
diff --git a/tools/testing/selftests/bpf/test_btf.h b/tools/testing/selftests/bpf/test_btf.h
index e7bc78108374..b99c04b4d26b 100644
--- a/tools/testing/selftests/bpf/test_btf.h
+++ b/tools/testing/selftests/bpf/test_btf.h
@@ -84,4 +84,20 @@
#define BTF_TYPE_TAG_ENC(value, type) \
BTF_TYPE_ENC(value, BTF_INFO_ENC(BTF_KIND_TYPE_TAG, 0, 0), type)
+#define BTF_LOC_PARAM_ENC(nvals, sz, flags) \
+ BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_LOC_PARAM, 0, nvals), sz), (flags)
+
+#define BTF_LOC_PARAM_VAL_ENCODE(val) (val)
+
+#define BTF_LOC_PROTO_ENC(nargs) \
+ BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_LOC_PROTO, 0, nargs), 0)
+
+#define BTF_LOC_PROTO_PARAM_ENCODE(param) (param)
+
+#define BTF_LOCSEC_ENC(name, nlocs) \
+ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_LOCSEC, 0, nlocs), 0)
+
+#define BTF_LOCSEC_LOC_ENCODE(func, loc_proto, offset) \
+ (func), (loc_proto), (offset)
+
#endif /* _TEST_BTF_H */
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC]
2026-09-16 7:41 ` [PATCH v3 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
@ 2026-09-16 8:44 ` bot+bpf-ci
2026-09-18 18:34 ` Eduard Zingerman
1 sibling, 0 replies; 42+ messages in thread
From: bot+bpf-ci @ 2026-09-16 8:44 UTC (permalink / raw)
To: alan.maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3483 bytes --]
> diff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c
> index 1c1c2c26690a7..972403f6a7438 100644
> --- a/tools/testing/selftests/bpf/btf_helpers.c
> +++ b/tools/testing/selftests/bpf/btf_helpers.c
[ ... ]
> @@ -203,6 +206,37 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
> fprintf(out, " type_id=%u component_idx=%d",
> t->type, btf_decl_tag(t)->component_idx);
> break;
> + case BTF_KIND_LOC_PARAM: {
> + struct btf_loc_param *p = btf_loc_param(t);
> + __u32 *v = (__u32 *)(p + 1);
> +
> + fprintf(out, " size=%u flags=0x%x vlen=%u", t->size, p->flags, vlen);
> + for (i = 0; i < vlen; i++, v++) {
> + if (p->flags & BTF_LOC_PARAM_SIGNED)
> + fprintf(out, "\n\tvalue=%d", (__s32)*v);
> + else
> + fprintf(out, "\n\tvalue=%u", *v);
> + }
> + break;
> + }
This isn't a bug, but could this use p->values directly rather than casting
past the struct?
struct btf_loc_param already has a flexible array member (__u32 values[]),
and the kernel side of the series prints them as p->values[i] in
btf_loc_param_log(). The neighbouring ARRAY, STRUCT, ENUM, ENUM64,
FUNC_PROTO, DATASEC and the two new LOC_PROTO/LOCSEC cases all declare
'const struct ... *' for their record pointers.
[ ... ]
> diff --git a/tools/testing/selftests/bpf/test_btf.h b/tools/testing/selftests/bpf/test_btf.h
> index e7bc781083745..b99c04b4d26bb 100644
> --- a/tools/testing/selftests/bpf/test_btf.h
> +++ b/tools/testing/selftests/bpf/test_btf.h
[ ... ]
> @@ -84,4 +84,20 @@
> #define BTF_TYPE_TAG_ENC(value, type) \
> BTF_TYPE_ENC(value, BTF_INFO_ENC(BTF_KIND_TYPE_TAG, 0, 0), type)
>
> +#define BTF_LOC_PARAM_ENC(nvals, sz, flags) \
> + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_LOC_PARAM, 0, nvals), sz), (flags)
> +
> +#define BTF_LOC_PARAM_VAL_ENCODE(val) (val)
> +
> +#define BTF_LOC_PROTO_ENC(nargs) \
> + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_LOC_PROTO, 0, nargs), 0)
> +
> +#define BTF_LOC_PROTO_PARAM_ENCODE(param) (param)
> +
> +#define BTF_LOCSEC_ENC(name, nlocs) \
> + BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_LOCSEC, 0, nlocs), 0)
> +
> +#define BTF_LOCSEC_LOC_ENCODE(func, loc_proto, offset) \
> + (func), (loc_proto), (offset)
> +
> #endif /* _TEST_BTF_H */
This isn't a bug, but are these macros used by a raw-BTF test somewhere, or
could they wait until the patch that needs them?
A git grep for BTF_LOC_PARAM_ENC, BTF_LOC_PARAM_VAL_ENCODE,
BTF_LOC_PROTO_ENC, BTF_LOC_PROTO_PARAM_ENCODE, BTF_LOCSEC_ENC and
BTF_LOCSEC_LOC_ENCODE across the tree at the last commit of the series
matches only test_btf.h itself. The later selftests that exercise the new
kinds (btf_field_iter.c, btf_dedup_split.c, btf_distill.c) all build their
BTF through the btf__add_loc_*() APIs instead.
This isn't a bug, but would BTF_LOC_PARAM_VAL_ENC /
BTF_LOC_PROTO_PARAM_ENC / BTF_LOCSEC_LOC_ENC line up better with the _ENC
naming used by the rest of this header?
The other three macros added by the same hunk (BTF_LOC_PARAM_ENC,
BTF_LOC_PROTO_ENC, BTF_LOCSEC_ENC) follow the _ENC suffix, and every other
macro in test_btf.h that fills the same role uses _ENC: BTF_MEMBER_ENC,
BTF_ENUM_ENC, BTF_ENUM64_ENC, BTF_ARRAY_ENC, BTF_VAR_SECINFO_ENC,
BTF_FUNC_PROTO_ARG_ENC.
---
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/35072175008
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC]
2026-09-16 7:41 ` [PATCH v3 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
2026-09-16 8:44 ` bot+bpf-ci
@ 2026-09-18 18:34 ` Eduard Zingerman
1 sibling, 0 replies; 42+ messages in thread
From: Eduard Zingerman @ 2026-09-18 18:34 UTC (permalink / raw)
To: Alan Maguire, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
> Add support to dump, encode and validate new location-related kinds.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
...
> diff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c
...
> @@ -203,6 +206,37 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
> fprintf(out, " type_id=%u component_idx=%d",
> t->type, btf_decl_tag(t)->component_idx);
> break;
> + case BTF_KIND_LOC_PARAM: {
> + struct btf_loc_param *p = btf_loc_param(t);
> + __u32 *v = (__u32 *)(p + 1);
Nit: use ->values[i] field here?
> +
> + fprintf(out, " size=%u flags=0x%x vlen=%u", t->size, p->flags, vlen);
> + for (i = 0; i < vlen; i++, v++) {
> + if (p->flags & BTF_LOC_PARAM_SIGNED)
> + fprintf(out, "\n\tvalue=%d", (__s32)*v);
> + else
> + fprintf(out, "\n\tvalue=%u", *v);
> + }
> + break;
> + }
...
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (2 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-18 18:43 ` Eduard Zingerman
2026-09-16 7:41 ` [PATCH v3 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests Alan Maguire
` (6 subsequent siblings)
10 siblings, 1 reply; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
BTF_KIND_LOC[_PARAM|_PROTO|SEC] need to work with field iteration, so
extend the selftest to cover these and ensure iteration over all types
and names succeeds.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
.../selftests/bpf/prog_tests/btf_field_iter.c | 31 +++++++++++++++++--
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
index 32159d3eb281..dcb5429d141d 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_field_iter.c
@@ -31,8 +31,11 @@ struct field_data {
{ .ids = { 11 }, .strs = { "decltag" } },
{ .ids = { 6 }, .strs = { "typetag" } },
{ .ids = {}, .strs = { "e64", "eval1", "eval2", "eval3" } },
- { .ids = { 15, 16 }, .strs = { "datasec1" } }
-
+ { .ids = { 15, 16 }, .strs = { "datasec1" } },
+ { .ids = {}, .strs = { "" } },
+ { .ids = {}, .strs = { "" } },
+ { .ids = { 22, 23 }, .strs = { "" } },
+ { .ids = { 14, 24 }, .strs = { ".loc" } }
};
/* Fabricate BTF with various types and check BTF field iteration finds types,
@@ -88,6 +91,19 @@ void test_btf_field_iter(void)
btf__add_datasec_var_info(btf, 15, 0, 4);
btf__add_datasec_var_info(btf, 16, 4, 8);
+ btf__add_loc_param(btf, 4, BTF_LOC_PARAM_CONST | BTF_LOC_PARAM_SIGNED);
+ /* [22] loc value -1 */
+ btf__add_loc_param_value(btf, -1);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG); /* [23] loc reg 1 */
+ btf__add_loc_param_value(btf, 1);
+
+ btf__add_loc_proto(btf); /* [24] loc proto */
+ btf__add_loc_proto_param(btf, 22); /* param value -1, */
+ btf__add_loc_proto_param(btf, 23); /* param reg 1 */
+
+ btf__add_locsec(btf, ".loc"); /* [25] locsec ".loc" */
+ btf__add_locsec_loc(btf, 14, 24, 128); /* "func" */
+
VALIDATE_RAW_BTF(
btf,
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
@@ -123,7 +139,16 @@ void test_btf_field_iter(void)
"\t'eval3' val=3000",
"[21] DATASEC 'datasec1' size=12 vlen=2\n"
"\ttype_id=15 offset=0 size=4\n"
- "\ttype_id=16 offset=4 size=8");
+ "\ttype_id=16 offset=4 size=8",
+ "[22] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[23] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[24] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=22\n"
+ "\ttype_id=23",
+ "[25] LOCSEC '.loc' vlen=1\n"
+ "\tfunc_type_id=14 loc_proto_type_id=24 offset=128");
for (id = 1; id < btf__type_cnt(btf); id++) {
struct btf_type *t = btf_type_by_id(btf, id);
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests
2026-09-16 7:41 ` [PATCH v3 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests Alan Maguire
@ 2026-09-18 18:43 ` Eduard Zingerman
0 siblings, 0 replies; 42+ messages in thread
From: Eduard Zingerman @ 2026-09-18 18:43 UTC (permalink / raw)
To: Alan Maguire, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
> BTF_KIND_LOC[_PARAM|_PROTO|SEC] need to work with field iteration, so
> extend the selftest to cover these and ensure iteration over all types
> and names succeeds.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
...
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (3 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-18 18:46 ` Eduard Zingerman
2026-09-16 7:41 ` [PATCH v3 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
` (5 subsequent siblings)
10 siblings, 1 reply; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
Ensure that location params/protos are deduplicated and location
sections are not, and that references to deduplicated locations within
location prototypes and sections are updated after deduplication.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
.../bpf/prog_tests/btf_dedup_split.c | 116 ++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
index 9d6161151593..5c15a6b7f4fb 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dedup_split.c
@@ -554,6 +554,120 @@ static void test_split_module(void)
btf__free(vmlinux_btf);
}
+static void test_split_loc(void)
+{
+ struct btf *btf1, *btf2;
+ int err;
+
+ btf1 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+ return;
+
+ btf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */
+
+
+ btf__add_int(btf1, "long", 8, BTF_INT_SIGNED); /* [1] long */
+ btf__add_ptr(btf1, 1); /* [2] ptr to long */
+ btf__add_func_proto(btf1, 1); /* [3] long (*)(long, long *); */
+ btf__add_func_param(btf1, "p1", 1);
+ btf__add_func_param(btf1, "p2", 2);
+ btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 3); /* [4] long foo(long, long *); */
+ btf__add_loc_param(btf1, 8, BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf1, 3735928559);
+ btf__add_loc_param_value(btf1, 4277009102); /* [5] loc value */
+ btf__add_loc_param(btf1, 8, BTF_LOC_PARAM_REG); /* [6] loc reg 1 */
+ btf__add_loc_param_value(btf1, 1);
+ btf__add_loc_proto(btf1); /* [7] loc proto */
+ btf__add_loc_proto_param(btf1, 5); /* param value */
+ btf__add_loc_proto_param(btf1, 6); /* param reg 1 */
+
+ VALIDATE_RAW_BTF(
+ btf1,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6");
+
+ btf2 = btf__new_empty_split(btf1);
+ if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+ goto cleanup;
+ btf__add_loc_param(btf2, 8, BTF_LOC_PARAM_REG);
+ btf__add_loc_param_value(btf2, 1); /* [8] loc reg 1 */
+ btf__add_loc_proto(btf2); /* [9] loc proto */
+ btf__add_loc_proto_param(btf2, 5); /* param value */
+ btf__add_loc_proto_param(btf2, 8); /* param reg 1 */
+ btf__add_locsec(btf2, "inline.text"); /* [10] locsec "inline.text" */
+ /* add duplicate locsec */
+ btf__add_locsec_loc(btf2, 4, 9, 128);
+ btf__add_locsec(btf2, "inline.text"); /* [11] locsec "inline.text" */
+ btf__add_locsec_loc(btf2, 4, 9, 128);
+
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6",
+ "[8] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[9] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=8",
+ "[10] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=9 offset=128",
+ "[11] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=9 offset=128");
+
+ err = btf__dedup(btf2, NULL);
+ if (!ASSERT_OK(err, "btf_dedup"))
+ goto cleanup;
+
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'long' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED",
+ "[2] PTR '(anon)' type_id=1",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'p1' type_id=1\n"
+ "\t'p2' type_id=2",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2\n"
+ "\tvalue=3735928559\n"
+ "\tvalue=4277009102",
+ "[6] LOC_PARAM '(anon)' size=8 flags=0x8 vlen=1\n"
+ "\tvalue=1",
+ "[7] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=5\n"
+ "\ttype_id=6",
+ "[8] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=7 offset=128",
+ "[9] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=7 offset=128");
+
+cleanup:
+ btf__free(btf2);
+ btf__free(btf1);
+}
+
void test_btf_dedup_split()
{
if (test__start_subtest("split_simple"))
@@ -566,4 +680,6 @@ void test_btf_dedup_split()
test_split_dup_struct_in_cu();
if (test__start_subtest("split_module"))
test_split_module();
+ if (test__start_subtest("split_loc"))
+ test_split_loc();
}
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests
2026-09-16 7:41 ` [PATCH v3 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests Alan Maguire
@ 2026-09-18 18:46 ` Eduard Zingerman
0 siblings, 0 replies; 42+ messages in thread
From: Eduard Zingerman @ 2026-09-18 18:46 UTC (permalink / raw)
To: Alan Maguire, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
> Ensure that location params/protos are deduplicated and location
> sections are not, and that references to deduplicated locations within
> location prototypes and sections are updated after deduplication.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
...
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (4 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-18 19:59 ` Eduard Zingerman
2026-09-16 7:41 ` [PATCH v3 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
` (4 subsequent siblings)
10 siblings, 1 reply; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
When creating distilled BTF, BTF_KIND_FUNC, _LOC_PARAM and _LOC_PROTO
should be added to split BTF. This means potentially some duplication
of location information, but only for out-of-tree modules that use
distilled base/split BTF.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
.../selftests/bpf/prog_tests/btf_distill.c | 101 ++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
index fb67ae195a73..141a4cb1f405 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
@@ -671,6 +671,105 @@ static void test_distilled_base_embedded_err(void)
btf__free(btf1);
}
+/* LOC_PARAM, LOC_PROTO should be added to split BTF. */
+static void test_distilled_loc(void)
+{
+ struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL;
+ struct btf *btf4 = NULL, *btf5 = NULL;
+
+ btf1 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+ return;
+
+ btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
+ btf__add_func_proto(btf1, 1); /* [2] int (*)(int); */
+ btf__add_func_param(btf1, "p1", 1);
+ btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 2); /* [3] int foo(int); */
+ btf__add_loc_param(btf1, 4, BTF_LOC_PARAM_SIGNED | BTF_LOC_PARAM_CONST);
+ btf__add_loc_param_value(btf1, -1); /* [4] loc value */
+ btf__add_loc_proto(btf1); /* [5] loc proto */
+ btf__add_loc_proto_param(btf1, 4); /* param value */
+
+ VALIDATE_RAW_BTF(
+ btf1,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[3] FUNC 'foo' type_id=2 linkage=static",
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[5] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=4");
+
+ btf2 = btf__new_empty_split(btf1);
+ if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+ goto cleanup;
+
+ btf__add_locsec(btf2, "inline.text"); /* [6] locsec */
+ btf__add_locsec_loc(btf2, 3, 5, 256); /* "foo" offset 256 */
+ VALIDATE_RAW_BTF(
+ btf2,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[3] FUNC 'foo' type_id=2 linkage=static",
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[5] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=4",
+ "[6] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=3 loc_proto_type_id=5 offset=256");
+
+ if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+ "distilled_base") ||
+ !ASSERT_OK_PTR(btf3, "distilled_base") ||
+ !ASSERT_OK_PTR(btf4, "distilled_split") ||
+ !ASSERT_EQ(2, btf__type_cnt(btf3), "distilled_base_type_cnt"))
+ goto cleanup;
+
+ VALIDATE_RAW_BTF(
+ btf4,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ /* remainder is split BTF */
+ "[2] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=6 offset=256",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[6] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=5");
+
+ btf5 = btf__new_empty();
+ if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
+ goto cleanup;
+ btf__add_int(btf5, "int", 4, BTF_INT_SIGNED); /* [1] int */
+
+ if (!ASSERT_EQ(btf__relocate(btf4, btf5), 0, "relocate_split"))
+ goto cleanup;
+ VALIDATE_RAW_BTF(
+ btf4,
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+ /* remainder is split BTF */
+ "[2] LOCSEC 'inline.text' vlen=1\n"
+ "\tfunc_type_id=4 loc_proto_type_id=6 offset=256",
+ "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+ "\t'p1' type_id=1",
+ "[4] FUNC 'foo' type_id=3 linkage=static",
+ "[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+ "\tvalue=-1",
+ "[6] LOC_PROTO '(anon)' vlen=1\n"
+ "\ttype_id=5");
+
+cleanup:
+ btf__free(btf5);
+ btf__free(btf4);
+ btf__free(btf3);
+ btf__free(btf2);
+ btf__free(btf1);
+}
+
void test_btf_distill(void)
{
if (test__start_subtest("distilled_base"))
@@ -689,4 +788,6 @@ void test_btf_distill(void)
test_distilled_base_vmlinux();
if (test__start_subtest("distilled_endianness"))
test_distilled_endianness();
+ if (test__start_subtest("distilled_loc"))
+ test_distilled_loc();
}
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF
2026-09-16 7:41 ` [PATCH v3 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
@ 2026-09-18 19:59 ` Eduard Zingerman
2026-09-21 18:36 ` Alan Maguire
0 siblings, 1 reply; 42+ messages in thread
From: Eduard Zingerman @ 2026-09-18 19:59 UTC (permalink / raw)
To: Alan Maguire, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
> When creating distilled BTF, BTF_KIND_FUNC, _LOC_PARAM and _LOC_PROTO
> should be added to split BTF. This means potentially some duplication
> of location information, but only for out-of-tree modules that use
> distilled base/split BTF.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
> .../selftests/bpf/prog_tests/btf_distill.c | 101 ++++++++++++++++++
> 1 file changed, 101 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
> index fb67ae195a73..141a4cb1f405 100644
> --- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
> +++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
> @@ -671,6 +671,105 @@ static void test_distilled_base_embedded_err(void)
> btf__free(btf1);
> }
>
> +/* LOC_PARAM, LOC_PROTO should be added to split BTF. */
> +static void test_distilled_loc(void)
> +{
> + struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL;
> + struct btf *btf4 = NULL, *btf5 = NULL;
> +
> + btf1 = btf__new_empty();
> + if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
> + return;
> +
> + btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
> + btf__add_func_proto(btf1, 1); /* [2] int (*)(int); */
> + btf__add_func_param(btf1, "p1", 1);
> + btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 2); /* [3] int foo(int); */
> + btf__add_loc_param(btf1, 4, BTF_LOC_PARAM_SIGNED | BTF_LOC_PARAM_CONST);
> + btf__add_loc_param_value(btf1, -1); /* [4] loc value */
> + btf__add_loc_proto(btf1); /* [5] loc proto */
> + btf__add_loc_proto_param(btf1, 4); /* param value */
> +
> + VALIDATE_RAW_BTF(
> + btf1,
> + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
> + "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
> + "\t'p1' type_id=1",
> + "[3] FUNC 'foo' type_id=2 linkage=static",
> + "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
> + "\tvalue=-1",
> + "[5] LOC_PROTO '(anon)' vlen=1\n"
> + "\ttype_id=4");
Maybe also add an unrelated type to the base, such that effects of the
distill are visible?
> + btf2 = btf__new_empty_split(btf1);
> + if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
> + goto cleanup;
> +
> + btf__add_locsec(btf2, "inline.text"); /* [6] locsec */
> + btf__add_locsec_loc(btf2, 3, 5, 256); /* "foo" offset 256 */
> + VALIDATE_RAW_BTF(
> + btf2,
> + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
> + "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
> + "\t'p1' type_id=1",
> + "[3] FUNC 'foo' type_id=2 linkage=static",
> + "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
> + "\tvalue=-1",
> + "[5] LOC_PROTO '(anon)' vlen=1\n"
> + "\ttype_id=4",
> + "[6] LOCSEC 'inline.text' vlen=1\n"
> + "\tfunc_type_id=3 loc_proto_type_id=5 offset=256");
> +
> + if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
> + "distilled_base") ||
> + !ASSERT_OK_PTR(btf3, "distilled_base") ||
> + !ASSERT_OK_PTR(btf4, "distilled_split") ||
> + !ASSERT_EQ(2, btf__type_cnt(btf3), "distilled_base_type_cnt"))
> + goto cleanup;
> +
> + VALIDATE_RAW_BTF(
> + btf4,
> + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
> + /* remainder is split BTF */
> + "[2] LOCSEC 'inline.text' vlen=1\n"
> + "\tfunc_type_id=4 loc_proto_type_id=6 offset=256",
> + "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
> + "\t'p1' type_id=1",
> + "[4] FUNC 'foo' type_id=3 linkage=static",
> + "[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
> + "\tvalue=-1",
> + "[6] LOC_PROTO '(anon)' vlen=1\n"
> + "\ttype_id=5");
> +
> + btf5 = btf__new_empty();
> + if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
> + goto cleanup;
I'd add another btf__add_<something> here to shift the ides and make
sure that relocate actually did something.
> + btf__add_int(btf5, "int", 4, BTF_INT_SIGNED); /* [1] int */
> +
> + if (!ASSERT_EQ(btf__relocate(btf4, btf5), 0, "relocate_split"))
> + goto cleanup;
> + VALIDATE_RAW_BTF(
> + btf4,
> + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
> + /* remainder is split BTF */
> + "[2] LOCSEC 'inline.text' vlen=1\n"
> + "\tfunc_type_id=4 loc_proto_type_id=6 offset=256",
> + "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
> + "\t'p1' type_id=1",
> + "[4] FUNC 'foo' type_id=3 linkage=static",
> + "[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
> + "\tvalue=-1",
> + "[6] LOC_PROTO '(anon)' vlen=1\n"
> + "\ttype_id=5");
> +
> +cleanup:
> + btf__free(btf5);
> + btf__free(btf4);
> + btf__free(btf3);
> + btf__free(btf2);
> + btf__free(btf1);
> +}
> +
> void test_btf_distill(void)
> {
> if (test__start_subtest("distilled_base"))
> @@ -689,4 +788,6 @@ void test_btf_distill(void)
> test_distilled_base_vmlinux();
> if (test__start_subtest("distilled_endianness"))
> test_distilled_endianness();
> + if (test__start_subtest("distilled_loc"))
> + test_distilled_loc();
> }
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF
2026-09-18 19:59 ` Eduard Zingerman
@ 2026-09-21 18:36 ` Alan Maguire
0 siblings, 0 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-21 18:36 UTC (permalink / raw)
To: Eduard Zingerman, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On 18/09/2026 20:59, Eduard Zingerman wrote:
> On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
>> When creating distilled BTF, BTF_KIND_FUNC, _LOC_PARAM and _LOC_PROTO
>> should be added to split BTF. This means potentially some duplication
>> of location information, but only for out-of-tree modules that use
>> distilled base/split BTF.
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>> .../selftests/bpf/prog_tests/btf_distill.c | 101 ++++++++++++++++++
>> 1 file changed, 101 insertions(+)
>>
>> diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
>> index fb67ae195a73..141a4cb1f405 100644
>> --- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
>> +++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
>> @@ -671,6 +671,105 @@ static void test_distilled_base_embedded_err(void)
>> btf__free(btf1);
>> }
>>
>> +/* LOC_PARAM, LOC_PROTO should be added to split BTF. */
>> +static void test_distilled_loc(void)
>> +{
>> + struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL;
>> + struct btf *btf4 = NULL, *btf5 = NULL;
>> +
>> + btf1 = btf__new_empty();
>> + if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
>> + return;
>> +
>> + btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
>> + btf__add_func_proto(btf1, 1); /* [2] int (*)(int); */
>> + btf__add_func_param(btf1, "p1", 1);
>> + btf__add_func(btf1, "foo", BTF_FUNC_STATIC, 2); /* [3] int foo(int); */
>> + btf__add_loc_param(btf1, 4, BTF_LOC_PARAM_SIGNED | BTF_LOC_PARAM_CONST);
>> + btf__add_loc_param_value(btf1, -1); /* [4] loc value */
>> + btf__add_loc_proto(btf1); /* [5] loc proto */
>> + btf__add_loc_proto_param(btf1, 4); /* param value */
>> +
>> + VALIDATE_RAW_BTF(
>> + btf1,
>> + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
>> + "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
>> + "\t'p1' type_id=1",
>> + "[3] FUNC 'foo' type_id=2 linkage=static",
>> + "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
>> + "\tvalue=-1",
>> + "[5] LOC_PROTO '(anon)' vlen=1\n"
>> + "\ttype_id=4");
>
> Maybe also add an unrelated type to the base, such that effects of the
> distill are visible?
>
yep, good idea, will do.
>> + btf2 = btf__new_empty_split(btf1);
>> + if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
>> + goto cleanup;
>> +
>> + btf__add_locsec(btf2, "inline.text"); /* [6] locsec */
>> + btf__add_locsec_loc(btf2, 3, 5, 256); /* "foo" offset 256 */
>> + VALIDATE_RAW_BTF(
>> + btf2,
>> + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
>> + "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
>> + "\t'p1' type_id=1",
>> + "[3] FUNC 'foo' type_id=2 linkage=static",
>> + "[4] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
>> + "\tvalue=-1",
>> + "[5] LOC_PROTO '(anon)' vlen=1\n"
>> + "\ttype_id=4",
>> + "[6] LOCSEC 'inline.text' vlen=1\n"
>> + "\tfunc_type_id=3 loc_proto_type_id=5 offset=256");
>> +
>> + if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
>> + "distilled_base") ||
>> + !ASSERT_OK_PTR(btf3, "distilled_base") ||
>> + !ASSERT_OK_PTR(btf4, "distilled_split") ||
>> + !ASSERT_EQ(2, btf__type_cnt(btf3), "distilled_base_type_cnt"))
>> + goto cleanup;
>> +
>> + VALIDATE_RAW_BTF(
>> + btf4,
>> + "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
>> + /* remainder is split BTF */
>> + "[2] LOCSEC 'inline.text' vlen=1\n"
>> + "\tfunc_type_id=4 loc_proto_type_id=6 offset=256",
>> + "[3] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
>> + "\t'p1' type_id=1",
>> + "[4] FUNC 'foo' type_id=3 linkage=static",
>> + "[5] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
>> + "\tvalue=-1",
>> + "[6] LOC_PROTO '(anon)' vlen=1\n"
>> + "\ttype_id=5");
>> +
>> + btf5 = btf__new_empty();
>> + if (!ASSERT_OK_PTR(btf5, "empty_reloc_btf"))
>> + goto cleanup;
>
> I'd add another btf__add_<something> here to shift the ides and make
> sure that relocate actually did something.
>
makes sense. Will add for v4. Thanks!
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (5 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 7:54 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
2026-09-16 7:41 ` [PATCH v3 bpf-next 08/11] bpftool: Document support for multi-split BTF Alan Maguire
` (3 subsequent siblings)
10 siblings, 2 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
For bpftool to be able to dump .BTF.inline data in
/sys/kernel/btf/foo.inline for module foo, it needs to support
multi-split BTF because the parent-child relationship of BTF
inline data for modules is
vmlinux BTF data
module BTF data
module BTF inline data
So for example to dump BTF inline info for xfs we would run
$ bpftool btf dump -B /sys/kernel/btf/vmlinux -B /sys/kernel/btf/xfs file /sys/kernel/btf/xfs.inline
Multiple bases are specified with the vmlinux base BTF first (parent)
followed by the xfs BTF (child), and finally the XFS BTF extra.
Update help text accordingly.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/bpf/bpftool/btf.c | 2 +-
tools/bpf/bpftool/main.c | 22 +++++++++++++++++++---
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index bca0a3982f09..bbe8f9ea144f 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -1545,7 +1545,7 @@ static int do_help(int argc, char **argv)
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS " |\n"
- " {-B|--base-btf} }\n"
+ " {[{-B|--base-btf} FILE]... }\n"
"",
bin_name, "btf");
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index 5ababd8f7d0a..cff76be7d2e8 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -466,6 +466,7 @@ int main(int argc, char **argv)
{ "base-btf", required_argument, NULL, 'B' },
{ 0 }
};
+ struct btf *new_base_btf = NULL, *root_base_btf = NULL;
bool version_requested = false;
int opt, ret;
@@ -515,12 +516,16 @@ int main(int argc, char **argv)
verifier_logs = true;
break;
case 'B':
- base_btf = btf__parse(optarg, NULL);
- if (!base_btf) {
+ /* handle multi-split BTF */
+ new_base_btf = btf__parse_split(optarg, base_btf);
+ if (!new_base_btf) {
p_err("failed to parse base BTF at '%s': %d\n",
optarg, -errno);
return -1;
}
+ base_btf = new_base_btf;
+ if (!root_base_btf)
+ root_base_btf = base_btf;
break;
case 'L':
use_loader = true;
@@ -567,7 +572,18 @@ int main(int argc, char **argv)
if (json_output)
jsonw_destroy(&json_wtr);
- btf__free(base_btf);
+ while (base_btf) {
+ new_base_btf = (struct btf *)btf__base_btf(base_btf);
+ btf__free(base_btf);
+ /*
+ * Do not free base BTF that is an owned .BTF.base ; leads
+ * to a double-free, so only free as far as the root base
+ * we explicitly read with -B above.
+ */
+ if (base_btf == root_base_btf)
+ break;
+ base_btf = new_base_btf;
+ }
return ret;
}
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs
2026-09-16 7:41 ` [PATCH v3 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
@ 2026-09-16 7:54 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-09-16 7:54 UTC (permalink / raw)
To: Alan Maguire; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] bpf: evaluation of freed pointer in bpftool cleanup
- [Low] bpf: unbalanced braces in bpftool btf help string
--
commit 43f26ee1c8d2e52438682ffa46a3b61f16ff7512
Author: Alan Maguire <alan.maguire@oracle.com>
bpftool: Handle multi-split BTF by supporting multiple base BTFs
This commit updates bpftool to support multi-split BTF by allowing multiple
base BTF files to be specified via the -B argument. It updates the help text
and handles loading and freeing multiple nested BTF bases.
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index bca0a3982f09b..bbe8f9ea144fa 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
> @@ -1545,7 +1545,7 @@ static int do_help(int argc, char **argv)
> " " HELP_SPEC_MAP "\n"
> " " HELP_SPEC_PROGRAM "\n"
> " " HELP_SPEC_OPTIONS " |\n"
> - " {-B|--base-btf} }\n"
> + " {[{-B|--base-btf} FILE]... }\n"
[Severity: Low]
Does this leave the opening brace from the HELP_SPEC_OPTIONS block unclosed?
The HELP_SPEC_OPTIONS macro starts a brace-enclosed block, but replacing the
single closing brace with a balanced group {[...]} might leave the overarching
list unclosed when the help output is printed.
> diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
> index 5ababd8f7d0a0..cff76be7d2e86 100644
> --- a/tools/bpf/bpftool/main.c
> +++ b/tools/bpf/bpftool/main.c
[ ... ]
> @@ -567,7 +572,18 @@ int main(int argc, char **argv)
> if (json_output)
> jsonw_destroy(&json_wtr);
>
> - btf__free(base_btf);
> + while (base_btf) {
> + new_base_btf = (struct btf *)btf__base_btf(base_btf);
> + btf__free(base_btf);
> + /*
> + * Do not free base BTF that is an owned .BTF.base ; leads
> + * to a double-free, so only free as far as the root base
> + * we explicitly read with -B above.
> + */
> + if (base_btf == root_base_btf)
> + break;
[Severity: Low]
Are we evaluating a freed pointer in this break condition?
Since btf__free(base_btf) is called just before this check, evaluating
base_btf against root_base_btf could invoke undefined behavior by reading the
dangling pointer, even if it is just an address comparison in a teardown path.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260916074118.1007116-1-alan.maguire@oracle.com?part=7
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs
2026-09-16 7:41 ` [PATCH v3 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
2026-09-16 7:54 ` sashiko-bot
@ 2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: bot+bpf-ci @ 2026-09-16 9:03 UTC (permalink / raw)
To: alan.maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2694 bytes --]
> commit ce1a2ae119f4c77324c56fee2aa1fa0305ae09f5
> Author: Alan Maguire <alan.maguire@oracle.com>
> Date: Wed Sep 16 08:41:14 2026 +0100
>
> bpftool: Handle multi-split BTF by supporting multiple base BTFs
>
> For bpftool to be able to dump .BTF.inline data in
> /sys/kernel/btf/foo.inline for module foo, it needs to support
> multi-split BTF because the parent-child relationship of BTF
> inline data for modules is
>
> vmlinux BTF data
> module BTF data
> module BTF inline data
>
> So for example to dump BTF inline info for xfs we would run
>
> $ bpftool btf dump -B /sys/kernel/btf/vmlinux -B /sys/kernel/btf/xfs file /sys/kernel/btf/xfs.inline
>
> Multiple bases are specified with the vmlinux base BTF first (parent)
> followed by the xfs BTF (child), and finally the XFS BTF extra.
The sentence here doesn't match the example command. The example passes two
-B options (vmlinux and xfs) and one target file (xfs.inline). Could the
wording be clearer? Perhaps: "Bases are given outermost first: the vmlinux
BTF (parent), then the xfs module BTF (split on vmlinux); the .BTF.inline
data is then passed as the target FILE."
The term "XFS BTF extra" doesn't appear elsewhere in the message or code,
but the .inline data was called "module BTF inline data" in the hierarchy
diagram above.
>
> Update help text accordingly.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index bca0a3982f09b..bbe8f9ea144fa 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
[ ... ]
> diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
> index 5ababd8f7d0a0..cff76be7d2e86 100644
> --- a/tools/bpf/bpftool/main.c
> +++ b/tools/bpf/bpftool/main.c
[ ... ]
Andrii Nakryiko raised a documentation concern in the RFC/v1 review
(https://lore.kernel.org/bpf/CAEf4BzYMsLc+BHHEOg7iXj_DqCMoj1WR_gBk_8MYUdd1+WnpKQ@mail.gmail.com/):
The help text syntax now shows {[{-B|--base-btf} FILE]... } which indicates
repetition, but neither the help text nor tools/bpf/bpftool/Documentation/
bpftool-btf.rst explains that the order of multiple -B arguments matters
(vmlinux first as parent, then module BTF as child) or how the parent-child
chaining works. The commit message explains this, but the user-facing
documentation doesn't. Should the documentation include this information?
---
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/35072175008
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 08/11] bpftool: Document support for multi-split BTF
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (6 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 7:41 ` [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
` (2 subsequent siblings)
10 siblings, 0 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
Document the ability to pass multiple levels of split BTF, using
"-B base-btf" options.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/bpf/bpftool/Documentation/bpftool-btf.rst | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index cf75a7fa2d6b..c6dc445cd4a1 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -16,7 +16,7 @@ SYNOPSIS
**bpftool** [*OPTIONS*] **btf** *COMMAND*
-*OPTIONS* := { |COMMON_OPTIONS| | { **-B** | **--base-btf** } }
+*OPTIONS* := { |COMMON_OPTIONS| | [{ **-B** | **--base-btf**} *FILE*]... }
*COMMANDS* := { **dump** | **help** }
@@ -87,7 +87,10 @@ OPTIONS
objects for kernel modules. To avoid duplicating all kernel symbols
required by modules, BTF objects for modules are "split", they are
built incrementally on top of the kernel (vmlinux) BTF object. So the
- base BTF reference should usually point to the kernel BTF.
+ base BTF reference should usually point to the kernel BTF. Multiple
+ base BTF objects can be passed, where the first is assumed to be the
+ root BTF, followed by split BTF based upon it, followed by split
+ BTF based upon the first split BTF and so on.
When the main BTF object to process (for example, the module BTF to
dump) is passed as a *FILE*, bpftool attempts to autodetect the path
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (7 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 08/11] bpftool: Document support for multi-split BTF Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 7:55 ` sashiko-bot
` (4 more replies)
2026-09-16 7:41 ` [PATCH v3 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
2026-09-16 7:41 ` [PATCH v3 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
10 siblings, 5 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
In raw mode ensure we can dump new BTF kinds in normal/json format.
BTF_KIND_LOC_PARAMs are rendered as strings, for example a
const value of 0x2a and a dereference of r10 + 0x20:
[12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
[13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
LOC_PROTOs render the associated values of each of their
LOC_PARAMs for easier readability:
[14] LOC_PROTO '(anon)' vlen=2
type_id=12 value='r1'
type_id=13 value='*(r2 + 0x10)'
and LOCSEC shows function name associated with site:
[15] LOCSEC 'inline.text' vlen=1
name=foo func_type_id=5 loc_proto_type_id=14 offset=64
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/bpf/bpftool/btf.c | 169 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index bbe8f9ea144f..5e0cb5862811 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -51,6 +51,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
struct sort_datum {
@@ -117,6 +120,83 @@ static int btf_kind_safe(int kind)
return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
}
+static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
+{
+ const struct btf_loc_param *p;
+ __u32 i = 0, vlen;
+ __u64 value;
+ bool negative = false;
+ char regs[32] = {};
+ char num[32] = {};
+ const char *op = "";
+
+ if (!t || !btf_is_loc_param(t)) {
+ snprintf(str, sz, "<invalid>");
+ return;
+ }
+
+ p = btf_loc_param(t);
+ vlen = btf_vlen(t);
+
+ if (p->flags & BTF_LOC_PARAM_REG) {
+ __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
+
+ if (nregs > vlen) {
+ snprintf(str, sz, "?");
+ return;
+ }
+
+ switch (nregs) {
+ case 2:
+ snprintf(regs, sizeof(regs), "r%u, r%u",
+ p->values[0], p->values[1]);
+ break;
+ case 1:
+ snprintf(regs, sizeof(regs), "r%u", p->values[0]);
+ break;
+ default:
+ snprintf(regs, sizeof(regs), "?");
+ break;
+ }
+ i += nregs;
+ }
+ if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
+ switch (vlen - i) {
+ case 1:
+ value = p->values[i];
+ break;
+ case 2:
+ value = ((__u64)p->values[i + 1] << 32) | p->values[i];
+ break;
+ default:
+ snprintf(num, sizeof(num), "?");
+ goto done;
+ }
+ if ((p->flags & BTF_LOC_PARAM_SIGNED) && t->size &&
+ t->size <= sizeof(value)) {
+ __u32 bits = t->size * 8;
+
+ if (t->size < sizeof(value))
+ value &= (1ULL << bits) - 1;
+ negative = value & (1ULL << (bits - 1));
+ if (negative)
+ value = t->size == sizeof(value) ? -value :
+ (1ULL << bits) - value;
+ }
+ snprintf(num, sizeof(num), "0x%llx", (unsigned long long)value);
+ }
+ if (num[0])
+ op = regs[0] ? (negative ? " - " : " + ") : negative ? "-" : "";
+
+done:
+ snprintf(str, sz, "%s%s%s%s%s",
+ p->flags & BTF_LOC_PARAM_DEREF ? "*(" : "",
+ regs,
+ op,
+ num,
+ p->flags & BTF_LOC_PARAM_DEREF ? ")" : "");
+}
+
static int dump_btf_type(const struct btf *btf, __u32 id,
const struct btf_type *t)
{
@@ -415,6 +495,95 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
}
break;
}
+ case BTF_KIND_LOC_PARAM: {
+ const struct btf_loc_param *p = btf_loc_param(t);
+ __u32 vlen = btf_vlen(t);
+ char param_str[256] = {};
+
+ btf_loc_param_str(t, param_str, sizeof(param_str));
+
+ if (json_output) {
+ jsonw_uint_field(w, "size", t->size);
+ jsonw_uint_field(w, "flags", p->flags);
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_string_field(w, "values", param_str);
+ } else {
+ printf(" size=%u flags=0x%x vlen=%u values='%s'", t->size, p->flags, vlen, param_str);
+ }
+ break;
+ }
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *params = btf_loc_proto_params(t);
+ __u16 vlen = btf_vlen(t);
+ int i;
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "params");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, params++) {
+ const struct btf_type *p;
+ char param_str[256] = {};
+
+ if (*params) {
+ p = btf__type_by_id(btf, *params);
+ btf_loc_param_str(p, param_str, sizeof(param_str));
+ } else {
+ snprintf(param_str, sizeof(param_str), "<unavailable>");
+ }
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "type_id", *params);
+ jsonw_string_field(w, "value", param_str);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\ttype_id=%u value='%s'", *params, param_str);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
+
+ case BTF_KIND_LOCSEC: {
+ struct btf_loc *locs = btf_locsec_locs(t);
+ __u32 i, vlen = btf_vlen(t);
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "locs");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, locs++) {
+ const struct btf_type *f = btf__type_by_id(btf, locs->func);
+ const char *name = "<invalid>";
+
+ if (f && btf_is_func(f))
+ name = btf_str(btf, f->name_off);
+
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "func_type_id", locs->func);
+ jsonw_string_field(w, "name", name);
+ jsonw_uint_field(w, "loc_proto_type_id", locs->loc_proto);
+ jsonw_uint_field(w, "offset", locs->offset);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u",
+ name, locs->func, locs->loc_proto, locs->offset);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
default:
break;
}
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-16 7:41 ` [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
@ 2026-09-16 7:55 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
` (3 subsequent siblings)
4 siblings, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-09-16 7:55 UTC (permalink / raw)
To: Alan Maguire; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] bpftool: truncation of 24-bit BTF vlen to 16-bit in BTF_KIND_LOC_PROTO
--
commit 2e08d4509f6018d2621350930f039568aa3418e3
Author: Alan Maguire <alan.maguire@oracle.com>
bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
This commit adds support to bpftool for dumping new BTF kinds (LOC_PARAM,
LOC_PROTO, and LOCSEC) in both normal and JSON formats. It provides string
rendering for LOC_PARAM parameters, associated values for LOC_PROTOs, and
function names for LOCSEC sites.
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index bbe8f9ea144fa..5e0cb58628117 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
[ ... ]
> @@ -415,6 +495,95 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
[ ... ]
> + case BTF_KIND_LOC_PROTO: {
> + __u32 *params = btf_loc_proto_params(t);
> + __u16 vlen = btf_vlen(t);
[Severity: Low]
Does this code unintentionally truncate the 24-bit variable length count
returned by btf_vlen() into a 16-bit unsigned integer?
While a function's parameter count is unlikely to exceed 65535 in practice,
a maliciously crafted BTF file could potentially trigger this truncation
when parsing a BTF_KIND_LOC_PROTO entry. Other BTF kinds like
BTF_KIND_LOC_PARAM in this patch appear to use __u32 for vlen.
> + int i;
> +
> + if (json_output) {
> + jsonw_uint_field(w, "vlen", vlen);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260916074118.1007116-1-alan.maguire@oracle.com?part=9
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-16 7:41 ` [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
2026-09-16 7:55 ` sashiko-bot
@ 2026-09-16 9:03 ` bot+bpf-ci
2026-09-16 22:07 ` Jiri Olsa
` (2 subsequent siblings)
4 siblings, 0 replies; 42+ messages in thread
From: bot+bpf-ci @ 2026-09-16 9:03 UTC (permalink / raw)
To: alan.maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1041 bytes --]
> and LOCSEC shows function name associated with site:
>
> [15] LOCSEC 'inline.text' vlen=1
> name=foo func_type_id=5 loc_proto_type_id=14 offset=64
This isn't a bug, but the sample output doesn't match what the code emits.
tools/bpf/bpftool/btf.c:579 prints:
printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u", ...);
so the real output is name='foo' with the name quoted. The changelog sample
drops the quotes.
Also, the LOC_PROTO sample in the same message is internally inconsistent with
the LOC_PARAM sample above it: types [12] and [13] are shown as values='0x2a'
and values='*(r10 + 0x20)', but the [14] LOC_PROTO sample renders the same ids
12 and 13 as 'r1' and '*(r2 + 0x10)'. Worth fixing the pasted output so a
reader can match it against a real dump?
---
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/35072175008
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-16 7:41 ` [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
2026-09-16 7:55 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
@ 2026-09-16 22:07 ` Jiri Olsa
2026-09-17 8:27 ` Alan Maguire
2026-09-17 16:06 ` Quentin Monnet
2026-09-18 20:51 ` Eduard Zingerman
4 siblings, 1 reply; 42+ messages in thread
From: Jiri Olsa @ 2026-09-16 22:07 UTC (permalink / raw)
To: Alan Maguire
Cc: ast, andrii, eddyz87, daniel, ihor.solodrai, yonghong.song, song,
qmo, martin.lau, memxor, emil, bpf, nsc, puranjay, yatsenko
On Wed, Sep 16, 2026 at 08:41:16AM +0100, Alan Maguire wrote:
> In raw mode ensure we can dump new BTF kinds in normal/json format.
> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
> const value of 0x2a and a dereference of r10 + 0x20:
>
> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
>
> LOC_PROTOs render the associated values of each of their
> LOC_PARAMs for easier readability:
>
> [14] LOC_PROTO '(anon)' vlen=2
> type_id=12 value='r1'
> type_id=13 value='*(r2 + 0x10)'
nit, AFAICT these are dwarf's registers numbers? could we output arch's register names?
jirka
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-16 22:07 ` Jiri Olsa
@ 2026-09-17 8:27 ` Alan Maguire
2026-09-17 22:00 ` Jiri Olsa
0 siblings, 1 reply; 42+ messages in thread
From: Alan Maguire @ 2026-09-17 8:27 UTC (permalink / raw)
To: Jiri Olsa
Cc: ast, andrii, eddyz87, daniel, ihor.solodrai, yonghong.song, song,
qmo, martin.lau, memxor, emil, bpf, nsc, puranjay, yatsenko
On 16/09/2026 23:07, Jiri Olsa wrote:
>
> On Wed, Sep 16, 2026 at 08:41:16AM +0100, Alan Maguire wrote:
>> In raw mode ensure we can dump new BTF kinds in normal/json format.
>> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
>> const value of 0x2a and a dereference of r10 + 0x20:
>>
>> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
>> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
>>
>> LOC_PROTOs render the associated values of each of their
>> LOC_PARAMs for easier readability:
>>
>> [14] LOC_PROTO '(anon)' vlen=2
>> type_id=12 value='r1'
>> type_id=13 value='*(r2 + 0x10)'
>
> nit, AFAICT these are dwarf's registers numbers? could we output arch's register names?
>
thanks for taking a look Jiri! You're right they are DWARF register numbers alright;
the problem is to render them as arch register names we'd need to maintain per-arch
tables in bpftool, which seems like the wrong place to host those. I'd suggest instead
we augment pfunct [1] to support this; it already has an option to display inline expansions
from DWARF, so having a BTF-based expansion of inline sites there seems like it would
be a more natural fit, what do you think? Thanks!
Alan
[1] https://github.com/acmel/dwarves/blob/master/man-pages/pfunct.1
> jirka
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-17 8:27 ` Alan Maguire
@ 2026-09-17 22:00 ` Jiri Olsa
2026-09-18 9:19 ` Alan Maguire
0 siblings, 1 reply; 42+ messages in thread
From: Jiri Olsa @ 2026-09-17 22:00 UTC (permalink / raw)
To: Alan Maguire
Cc: Jiri Olsa, ast, andrii, eddyz87, daniel, ihor.solodrai,
yonghong.song, song, qmo, martin.lau, memxor, emil, bpf, nsc,
puranjay, yatsenko
On Thu, Sep 17, 2026 at 09:27:42AM +0100, Alan Maguire wrote:
>
>
> On 16/09/2026 23:07, Jiri Olsa wrote:
> >
> > On Wed, Sep 16, 2026 at 08:41:16AM +0100, Alan Maguire wrote:
> >> In raw mode ensure we can dump new BTF kinds in normal/json format.
> >> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
> >> const value of 0x2a and a dereference of r10 + 0x20:
> >>
> >> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
> >> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
> >>
> >> LOC_PROTOs render the associated values of each of their
> >> LOC_PARAMs for easier readability:
> >>
> >> [14] LOC_PROTO '(anon)' vlen=2
> >> type_id=12 value='r1'
> >> type_id=13 value='*(r2 + 0x10)'
> >
> > nit, AFAICT these are dwarf's registers numbers? could we output arch's register names?
> >
>
> thanks for taking a look Jiri! You're right they are DWARF register numbers alright;
> the problem is to render them as arch register names we'd need to maintain per-arch
> tables in bpftool, which seems like the wrong place to host those. I'd suggest instead
is it that bad? I guess it's just simple fixed table
not sure how useful that output is with dwarf registers, you'll need to convert
it to arch regs anyway to make some sense of it.. we could save some tokens ;-)
jirka
> we augment pfunct [1] to support this; it already has an option to display inline expansions
> from DWARF, so having a BTF-based expansion of inline sites there seems like it would
> be a more natural fit, what do you think? Thanks!
>
> Alan
>
> [1] https://github.com/acmel/dwarves/blob/master/man-pages/pfunct.1
>
>
> > jirka
> >
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-17 22:00 ` Jiri Olsa
@ 2026-09-18 9:19 ` Alan Maguire
2026-09-18 13:01 ` Jiri Olsa
0 siblings, 1 reply; 42+ messages in thread
From: Alan Maguire @ 2026-09-18 9:19 UTC (permalink / raw)
To: Jiri Olsa
Cc: ast, andrii, eddyz87, daniel, ihor.solodrai, yonghong.song, song,
qmo, martin.lau, memxor, emil, bpf, nsc, puranjay, yatsenko
On 17/09/2026 23:00, Jiri Olsa wrote:
> On Thu, Sep 17, 2026 at 09:27:42AM +0100, Alan Maguire wrote:
>>
>>
>> On 16/09/2026 23:07, Jiri Olsa wrote:
>> >
>> > On Wed, Sep 16, 2026 at 08:41:16AM +0100, Alan Maguire wrote:
>> >> In raw mode ensure we can dump new BTF kinds in normal/json format.
>> >> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
>> >> const value of 0x2a and a dereference of r10 + 0x20:
>> >>
>> >> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
>> >> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
>> >>
>> >> LOC_PROTOs render the associated values of each of their
>> >> LOC_PARAMs for easier readability:
>> >>
>> >> [14] LOC_PROTO '(anon)' vlen=2
>> >> type_id=12 value='r1'
>> >> type_id=13 value='*(r2 + 0x10)'
>> >
>> > nit, AFAICT these are dwarf's registers numbers? could we output arch's register names?
>> >
>>
>> thanks for taking a look Jiri! You're right they are DWARF register numbers alright;
>> the problem is to render them as arch register names we'd need to maintain per-arch
>> tables in bpftool, which seems like the wrong place to host those. I'd suggest instead
>
> is it that bad? I guess it's just simple fixed table
>
> not sure how useful that output is with dwarf registers, you'll need to convert
> it to arch regs anyway to make some sense of it.. we could save some tokens ;-)
>
Well it is a raw dump; to draw the analogy with functions, we don't we print a C
function prototype for a BTF_KIND_FUNC_PROTO; instead we print a set of BTF ids
that comprise the parameters. The problem with converting it is it then becomes
hard to relate the raw dump output back to what the BTF actually was, which is
often what you want to know when you're doing a raw dump. Now that pfunct supports
split BTF I can roll support for printing per-site info including registers etc into
it as part of the pahole changes respin.
> jirka
>
>> we augment pfunct [1] to support this; it already has an option to display inline expansions
>> from DWARF, so having a BTF-based expansion of inline sites there seems like it would
>> be a more natural fit, what do you think? Thanks!
>>
>> Alan
>>
>> [1] https://github.com/acmel/dwarves/blob/master/man-pages/pfunct.1
>>
>>
>> > jirka
>> >
>>
>
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-18 9:19 ` Alan Maguire
@ 2026-09-18 13:01 ` Jiri Olsa
0 siblings, 0 replies; 42+ messages in thread
From: Jiri Olsa @ 2026-09-18 13:01 UTC (permalink / raw)
To: Alan Maguire
Cc: Jiri Olsa, ast, andrii, eddyz87, daniel, ihor.solodrai,
yonghong.song, song, qmo, martin.lau, memxor, emil, bpf, nsc,
puranjay, yatsenko
On Fri, Sep 18, 2026 at 10:19:33AM +0100, Alan Maguire wrote:
> On 17/09/2026 23:00, Jiri Olsa wrote:
> > On Thu, Sep 17, 2026 at 09:27:42AM +0100, Alan Maguire wrote:
> >>
> >>
> >> On 16/09/2026 23:07, Jiri Olsa wrote:
> >> >
> >> > On Wed, Sep 16, 2026 at 08:41:16AM +0100, Alan Maguire wrote:
> >> >> In raw mode ensure we can dump new BTF kinds in normal/json format.
> >> >> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
> >> >> const value of 0x2a and a dereference of r10 + 0x20:
> >> >>
> >> >> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
> >> >> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
> >> >>
> >> >> LOC_PROTOs render the associated values of each of their
> >> >> LOC_PARAMs for easier readability:
> >> >>
> >> >> [14] LOC_PROTO '(anon)' vlen=2
> >> >> type_id=12 value='r1'
> >> >> type_id=13 value='*(r2 + 0x10)'
> >> >
> >> > nit, AFAICT these are dwarf's registers numbers? could we output arch's register names?
> >> >
> >>
> >> thanks for taking a look Jiri! You're right they are DWARF register numbers alright;
> >> the problem is to render them as arch register names we'd need to maintain per-arch
> >> tables in bpftool, which seems like the wrong place to host those. I'd suggest instead
> >
> > is it that bad? I guess it's just simple fixed table
> >
> > not sure how useful that output is with dwarf registers, you'll need to convert
> > it to arch regs anyway to make some sense of it.. we could save some tokens ;-)
> >
>
> Well it is a raw dump; to draw the analogy with functions, we don't we print a C
> function prototype for a BTF_KIND_FUNC_PROTO; instead we print a set of BTF ids
> that comprise the parameters. The problem with converting it is it then becomes
> hard to relate the raw dump output back to what the BTF actually was, which is
> often what you want to know when you're doing a raw dump. Now that pfunct supports
> split BTF I can roll support for printing per-site info including registers etc into
> it as part of the pahole changes respin.
ok, sounds good, thanks
jirka
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-16 7:41 ` [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
` (2 preceding siblings ...)
2026-09-16 22:07 ` Jiri Olsa
@ 2026-09-17 16:06 ` Quentin Monnet
2026-09-17 17:32 ` Alan Maguire
2026-09-18 7:29 ` Alan Maguire
2026-09-18 20:51 ` Eduard Zingerman
4 siblings, 2 replies; 42+ messages in thread
From: Quentin Monnet @ 2026-09-17 16:06 UTC (permalink / raw)
To: Alan Maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, martin.lau, memxor,
emil, bpf, nsc, puranjay, yatsenko
2026-09-16 08:41 UTC+0100 ~ Alan Maguire <alan.maguire@oracle.com>
> In raw mode ensure we can dump new BTF kinds in normal/json format.
> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
> const value of 0x2a and a dereference of r10 + 0x20:
>
> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
>
> LOC_PROTOs render the associated values of each of their
> LOC_PARAMs for easier readability:
>
> [14] LOC_PROTO '(anon)' vlen=2
> type_id=12 value='r1'
> type_id=13 value='*(r2 + 0x10)'
>
> and LOCSEC shows function name associated with site:
>
> [15] LOCSEC 'inline.text' vlen=1
> name=foo func_type_id=5 loc_proto_type_id=14 offset=64
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
> tools/bpf/bpftool/btf.c | 169 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 169 insertions(+)
>
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index bbe8f9ea144f..5e0cb5862811 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
> @@ -51,6 +51,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
> [BTF_KIND_DECL_TAG] = "DECL_TAG",
> [BTF_KIND_TYPE_TAG] = "TYPE_TAG",
> [BTF_KIND_ENUM64] = "ENUM64",
> + [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
> + [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
> + [BTF_KIND_LOCSEC] = "LOCSEC",
> };
>
> struct sort_datum {
> @@ -117,6 +120,83 @@ static int btf_kind_safe(int kind)
> return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
> }
>
> +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
> +{
> + const struct btf_loc_param *p;
> + __u32 i = 0, vlen;
> + __u64 value;
> + bool negative = false;
> + char regs[32] = {};
> + char num[32] = {};
> + const char *op = "";
> +
> + if (!t || !btf_is_loc_param(t)) {
> + snprintf(str, sz, "<invalid>");
> + return;
> + }
> +
> + p = btf_loc_param(t);
> + vlen = btf_vlen(t);
> +
> + if (p->flags & BTF_LOC_PARAM_REG) {
> + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
> +
> + if (nregs > vlen) {
> + snprintf(str, sz, "?");
> + return;
> + }
> +
> + switch (nregs) {
> + case 2:
> + snprintf(regs, sizeof(regs), "r%u, r%u",
> + p->values[0], p->values[1]);
> + break;
> + case 1:
> + snprintf(regs, sizeof(regs), "r%u", p->values[0]);
> + break;
> + default:
> + snprintf(regs, sizeof(regs), "?");
> + break;
> + }
> + i += nregs;
> + }
> + if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
> + switch (vlen - i) {
> + case 1:
> + value = p->values[i];
> + break;
> + case 2:
> + value = ((__u64)p->values[i + 1] << 32) | p->values[i];
> + break;
> + default:
> + snprintf(num, sizeof(num), "?");
> + goto done;
> + }
> + if ((p->flags & BTF_LOC_PARAM_SIGNED) && t->size &&
> + t->size <= sizeof(value)) {
> + __u32 bits = t->size * 8;
Hi Alan, thanks!
In the case of BTF_LOC_PARAM_OFFSET, what do we need "negative" for
exactly, is this supposed to be the sign for the parameter or for the
associated offset? My understanding is that "t->size" refers to the
parameter itself, not the offset, so we would pick the wrong bit if
trying to find the sign for the offset? But I'm not sure I read it
correctly.
> +
> + if (t->size < sizeof(value))
> + value &= (1ULL << bits) - 1;
> + negative = value & (1ULL << (bits - 1));
> + if (negative)
> + value = t->size == sizeof(value) ? -value :
> + (1ULL << bits) - value;
> + }
> + snprintf(num, sizeof(num), "0x%llx", (unsigned long long)value);
Looking at the different existing flags and their docs, I see:
"a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
normalized with respect to kernel/module base address."
But I don't see the output accounting for BTF_LOC_PARAM_ADDR, is this
expected or is that an omission?
[...]
Please also look at Sashiko and bpf-ci's reviews for patch 7.
Thanks,
Quentin
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-17 16:06 ` Quentin Monnet
@ 2026-09-17 17:32 ` Alan Maguire
2026-09-18 7:29 ` Alan Maguire
1 sibling, 0 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-17 17:32 UTC (permalink / raw)
To: Quentin Monnet, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, martin.lau, memxor,
emil, bpf, nsc, puranjay, yatsenko
On 17/09/2026 17:06, Quentin Monnet wrote:
> 2026-09-16 08:41 UTC+0100 ~ Alan Maguire <alan.maguire@oracle.com>
>> In raw mode ensure we can dump new BTF kinds in normal/json format.
>> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
>> const value of 0x2a and a dereference of r10 + 0x20:
>>
>> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
>> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
>>
>> LOC_PROTOs render the associated values of each of their
>> LOC_PARAMs for easier readability:
>>
>> [14] LOC_PROTO '(anon)' vlen=2
>> type_id=12 value='r1'
>> type_id=13 value='*(r2 + 0x10)'
>>
>> and LOCSEC shows function name associated with site:
>>
>> [15] LOCSEC 'inline.text' vlen=1
>> name=foo func_type_id=5 loc_proto_type_id=14 offset=64
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>> tools/bpf/bpftool/btf.c | 169 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 169 insertions(+)
>>
>> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
>> index bbe8f9ea144f..5e0cb5862811 100644
>> --- a/tools/bpf/bpftool/btf.c
>> +++ b/tools/bpf/bpftool/btf.c
>> @@ -51,6 +51,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
>> [BTF_KIND_DECL_TAG] = "DECL_TAG",
>> [BTF_KIND_TYPE_TAG] = "TYPE_TAG",
>> [BTF_KIND_ENUM64] = "ENUM64",
>> + [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
>> + [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
>> + [BTF_KIND_LOCSEC] = "LOCSEC",
>> };
>>
>> struct sort_datum {
>> @@ -117,6 +120,83 @@ static int btf_kind_safe(int kind)
>> return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
>> }
>>
>> +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
>> +{
>> + const struct btf_loc_param *p;
>> + __u32 i = 0, vlen;
>> + __u64 value;
>> + bool negative = false;
>> + char regs[32] = {};
>> + char num[32] = {};
>> + const char *op = "";
>> +
>> + if (!t || !btf_is_loc_param(t)) {
>> + snprintf(str, sz, "<invalid>");
>> + return;
>> + }
>> +
>> + p = btf_loc_param(t);
>> + vlen = btf_vlen(t);
>> +
>> + if (p->flags & BTF_LOC_PARAM_REG) {
>> + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
>> +
>> + if (nregs > vlen) {
>> + snprintf(str, sz, "?");
>> + return;
>> + }
>> +
>> + switch (nregs) {
>> + case 2:
>> + snprintf(regs, sizeof(regs), "r%u, r%u",
>> + p->values[0], p->values[1]);
>> + break;
>> + case 1:
>> + snprintf(regs, sizeof(regs), "r%u", p->values[0]);
>> + break;
>> + default:
>> + snprintf(regs, sizeof(regs), "?");
>> + break;
>> + }
>> + i += nregs;
>> + }
>> + if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
>> + switch (vlen - i) {
>> + case 1:
>> + value = p->values[i];
>> + break;
>> + case 2:
>> + value = ((__u64)p->values[i + 1] << 32) | p->values[i];
>> + break;
>> + default:
>> + snprintf(num, sizeof(num), "?");
>> + goto done;
>> + }
>> + if ((p->flags & BTF_LOC_PARAM_SIGNED) && t->size &&
>> + t->size <= sizeof(value)) {
>> + __u32 bits = t->size * 8;
>
>
> Hi Alan, thanks!
>
> In the case of BTF_LOC_PARAM_OFFSET, what do we need "negative" for
> exactly, is this supposed to be the sign for the parameter or for the
> associated offset? My understanding is that "t->size" refers to the
> parameter itself, not the offset, so we would pick the wrong bit if
> trying to find the sign for the offset? But I'm not sure I read it
> correctly.
>
hi Quentin, great catch! This was a leftover from a previous iteration
where the sign of the size was used to connote whether the constant
value is signed or not; we use the flags to identify that now so that
the size can be a valid (>= 0) size, so the negative size needs to go
away; will fix for v4.
>
>> +
>> + if (t->size < sizeof(value))
>> + value &= (1ULL << bits) - 1;
>> + negative = value & (1ULL << (bits - 1));
>> + if (negative)
>> + value = t->size == sizeof(value) ? -value :
>> + (1ULL << bits) - value;
>> + }
>> + snprintf(num, sizeof(num), "0x%llx", (unsigned long long)value);
>
>
> Looking at the different existing flags and their docs, I see:
>
> "a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
> normalized with respect to kernel/module base address."
>
> But I don't see the output accounting for BTF_LOC_PARAM_ADDR, is this
> expected or is that an omission?
>
In general an address will have both _CONST and _ADDR specified so it will
be captured by const display, but I should make that more explicit in the
code.
> [...]
>
> Please also look at Sashiko and bpf-ci's reviews for patch 7.
>
Will do; thanks again!
Alan
> Thanks,
> Quentin
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-17 16:06 ` Quentin Monnet
2026-09-17 17:32 ` Alan Maguire
@ 2026-09-18 7:29 ` Alan Maguire
1 sibling, 0 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-18 7:29 UTC (permalink / raw)
To: Quentin Monnet, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, martin.lau, memxor,
emil, bpf, nsc, puranjay, yatsenko
On 17/09/2026 17:06, Quentin Monnet wrote:
> 2026-09-16 08:41 UTC+0100 ~ Alan Maguire <alan.maguire@oracle.com>
>> In raw mode ensure we can dump new BTF kinds in normal/json format.
>> BTF_KIND_LOC_PARAMs are rendered as strings, for example a
>> const value of 0x2a and a dereference of r10 + 0x20:
>>
>> [12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
>> [13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r10 + 0x20)'
>>
>> LOC_PROTOs render the associated values of each of their
>> LOC_PARAMs for easier readability:
>>
>> [14] LOC_PROTO '(anon)' vlen=2
>> type_id=12 value='r1'
>> type_id=13 value='*(r2 + 0x10)'
>>
>> and LOCSEC shows function name associated with site:
>>
>> [15] LOCSEC 'inline.text' vlen=1
>> name=foo func_type_id=5 loc_proto_type_id=14 offset=64
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>> tools/bpf/bpftool/btf.c | 169 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 169 insertions(+)
>>
>> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
>> index bbe8f9ea144f..5e0cb5862811 100644
>> --- a/tools/bpf/bpftool/btf.c
>> +++ b/tools/bpf/bpftool/btf.c
>> @@ -51,6 +51,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
>> [BTF_KIND_DECL_TAG] = "DECL_TAG",
>> [BTF_KIND_TYPE_TAG] = "TYPE_TAG",
>> [BTF_KIND_ENUM64] = "ENUM64",
>> + [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
>> + [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
>> + [BTF_KIND_LOCSEC] = "LOCSEC",
>> };
>>
>> struct sort_datum {
>> @@ -117,6 +120,83 @@ static int btf_kind_safe(int kind)
>> return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
>> }
>>
>> +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
>> +{
>> + const struct btf_loc_param *p;
>> + __u32 i = 0, vlen;
>> + __u64 value;
>> + bool negative = false;
>> + char regs[32] = {};
>> + char num[32] = {};
>> + const char *op = "";
>> +
>> + if (!t || !btf_is_loc_param(t)) {
>> + snprintf(str, sz, "<invalid>");
>> + return;
>> + }
>> +
>> + p = btf_loc_param(t);
>> + vlen = btf_vlen(t);
>> +
>> + if (p->flags & BTF_LOC_PARAM_REG) {
>> + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
>> +
>> + if (nregs > vlen) {
>> + snprintf(str, sz, "?");
>> + return;
>> + }
>> +
>> + switch (nregs) {
>> + case 2:
>> + snprintf(regs, sizeof(regs), "r%u, r%u",
>> + p->values[0], p->values[1]);
>> + break;
>> + case 1:
>> + snprintf(regs, sizeof(regs), "r%u", p->values[0]);
>> + break;
>> + default:
>> + snprintf(regs, sizeof(regs), "?");
>> + break;
>> + }
>> + i += nregs;
>> + }
>> + if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
>> + switch (vlen - i) {
>> + case 1:
>> + value = p->values[i];
>> + break;
>> + case 2:
>> + value = ((__u64)p->values[i + 1] << 32) | p->values[i];
>> + break;
>> + default:
>> + snprintf(num, sizeof(num), "?");
>> + goto done;
>> + }
>> + if ((p->flags & BTF_LOC_PARAM_SIGNED) && t->size &&
>> + t->size <= sizeof(value)) {
>> + __u32 bits = t->size * 8;
>
>
> Hi Alan, thanks!
>
> In the case of BTF_LOC_PARAM_OFFSET, what do we need "negative" for
> exactly, is this supposed to be the sign for the parameter or for the
> associated offset? My understanding is that "t->size" refers to the
> parameter itself, not the offset, so we would pick the wrong bit if
> trying to find the sign for the offset? But I'm not sure I read it
> correctly.
sorry, my previous reply had this wrong, I was mixing up an earlier
iteration. The idea here is we display the offset in hex but since we
can compute the sign it makes it clearer if we fix up the value for
display based on its signedness.
The code above has a bug tho; the size actually represents the size of
the final value rather than the size of the offset; the two can differ
for a case say where I have a REG|DEREF|OFFSET|SIGNED which has size 8 but
only has a 4-byte signed offset. We need to compute the signed value size
based on the type vlen, not the t->size since it is the overall size of
the result of dereferencing via the register value + offset. I'm fixing
this and adding a test covering this in v4 and updating the UAPI to explain
it more clearly.
Again thanks for catching this!
>
>
>> +
>> + if (t->size < sizeof(value))
>> + value &= (1ULL << bits) - 1;
>> + negative = value & (1ULL << (bits - 1));
>> + if (negative)
>> + value = t->size == sizeof(value) ? -value :
>> + (1ULL << bits) - value;
>> + }
>> + snprintf(num, sizeof(num), "0x%llx", (unsigned long long)value);
>
>
> Looking at the different existing flags and their docs, I see:
>
> "a BTF_LOC_PARAM_ADDR|BTF_LOC_PARAM_CONST is an address that should be
> normalized with respect to kernel/module base address."
>
> But I don't see the output accounting for BTF_LOC_PARAM_ADDR, is this
> expected or is that an omission?
>
> [...]
>
> Please also look at Sashiko and bpf-ci's reviews for patch 7.
>
> Thanks,
> Quentin
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-16 7:41 ` [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
` (3 preceding siblings ...)
2026-09-17 16:06 ` Quentin Monnet
@ 2026-09-18 20:51 ` Eduard Zingerman
2026-09-21 18:47 ` Alan Maguire
4 siblings, 1 reply; 42+ messages in thread
From: Eduard Zingerman @ 2026-09-18 20:51 UTC (permalink / raw)
To: Alan Maguire, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
...
> +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
> +{
> + const struct btf_loc_param *p;
> + __u32 i = 0, vlen;
> + __u64 value;
> + bool negative = false;
> + char regs[32] = {};
> + char num[32] = {};
> + const char *op = "";
> +
> + if (!t || !btf_is_loc_param(t)) {
> + snprintf(str, sz, "<invalid>");
> + return;
> + }
> +
> + p = btf_loc_param(t);
> + vlen = btf_vlen(t);
> +
> + if (p->flags & BTF_LOC_PARAM_REG) {
> + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
> +
> + if (nregs > vlen) {
> + snprintf(str, sz, "?");
> + return;
> + }
> +
> + switch (nregs) {
> + case 2:
> + snprintf(regs, sizeof(regs), "r%u, r%u",
> + p->values[0], p->values[1]);
I agree with Jiri regarding the register names. It's not a huge table,
e.g. [1], 20 lines for x86 ~> 100-200 lines that would not really
change to handle all architectures that have BPF jits.
And it would be very convenient for those using the tool.
Also, it appears that simply enumerating all possible flag
combinations in a switch would make this function easier to audit for
not-handled expressions (like ADDR), or plainly reporting that the
flags are unknown for this version of the tool.
[1] https://github.com/eddyz87/inline-address-printer/blob/master/main.c#L107
> + break;
> + case 1:
> + snprintf(regs, sizeof(regs), "r%u", p->values[0]);
> + break;
> + default:
> + snprintf(regs, sizeof(regs), "?");
> + break;
> + }
> + i += nregs;
> + }
> + if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
...
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-18 20:51 ` Eduard Zingerman
@ 2026-09-21 18:47 ` Alan Maguire
2026-09-21 21:46 ` Eduard Zingerman
0 siblings, 1 reply; 42+ messages in thread
From: Alan Maguire @ 2026-09-21 18:47 UTC (permalink / raw)
To: Eduard Zingerman, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On 18/09/2026 21:51, Eduard Zingerman wrote:
> On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
>
> ...
>
>> +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
>> +{
>> + const struct btf_loc_param *p;
>> + __u32 i = 0, vlen;
>> + __u64 value;
>> + bool negative = false;
>> + char regs[32] = {};
>> + char num[32] = {};
>> + const char *op = "";
>> +
>> + if (!t || !btf_is_loc_param(t)) {
>> + snprintf(str, sz, "<invalid>");
>> + return;
>> + }
>> +
>> + p = btf_loc_param(t);
>> + vlen = btf_vlen(t);
>> +
>> + if (p->flags & BTF_LOC_PARAM_REG) {
>> + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
>> +
>> + if (nregs > vlen) {
>> + snprintf(str, sz, "?");
>> + return;
>> + }
>> +
>> + switch (nregs) {
>> + case 2:
>> + snprintf(regs, sizeof(regs), "r%u, r%u",
>> + p->values[0], p->values[1]);
>
> I agree with Jiri regarding the register names. It's not a huge table,
> e.g. [1], 20 lines for x86 ~> 100-200 lines that would not really
> change to handle all architectures that have BPF jits.
> And it would be very convenient for those using the tool.
>
Yeah, it's doable, it's just that it is more portable when done in
pfunct; we can use dwfl interface to get register names [1]. With
pfunct changes in that tree we get output that is either arch-independent
(standalone BTF) or when combined with ELF info from vmlinux
gives us the arch-specific register names, containing function etc.
To see the inline site for ip_send_skb for example:
$ pfunct --inline_sites -f ip_send_skb --elf vmlinux /sys/kernel/btf/vmlinux.inline
0xffffffff8201f241 [ip_push_pending_frames+0x31, .text +0x101f241] ip_send_skb(struct net * net [%rbx], struct sk_buff * skb [%rax])
(above does not need DWARF at all, just BTF + ELF info, so will work with a
debuginfo-stripped vmlinux)
So for me the tool to reach for in understanding the raw BTF is
bpftool, whereas to apply the arch-specific transformations, locate the
absolute addresses etc I'd use pfunct. But that's just me; I'm happy to
go with the consensus here. Given the current library support, I think
maintaining per-arch tables in bpftool (rather than introducing a new
library dependency) would be the way to go if we do add it.
Quentin, what do you think? Are per-arch tables for registers in bpftool
ok from your side? Thanks!
Alan
[1] https://github.com/alan-maguire/dwarves/blob/0da899665b04f16c3f68c0b736834a15e045c3c9/pfunct.c#L95
> Also, it appears that simply enumerating all possible flag
> combinations in a switch would make this function easier to audit for
> not-handled expressions (like ADDR), or plainly reporting that the
> flags are unknown for this version of the tool.
>
> [1] https://github.com/eddyz87/inline-address-printer/blob/master/main.c#L107
>
>> + break;
>> + case 1:
>> + snprintf(regs, sizeof(regs), "r%u", p->values[0]);
>> + break;
>> + default:
>> + snprintf(regs, sizeof(regs), "?");
>> + break;
>> + }
>> + i += nregs;
>> + }
>> + if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
>
> ...
>
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-21 18:47 ` Alan Maguire
@ 2026-09-21 21:46 ` Eduard Zingerman
2026-09-22 11:59 ` Quentin Monnet
0 siblings, 1 reply; 42+ messages in thread
From: Eduard Zingerman @ 2026-09-21 21:46 UTC (permalink / raw)
To: Alan Maguire, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko
On Mon, 2026-09-21 at 19:47 +0100, Alan Maguire wrote:
> On 18/09/2026 21:51, Eduard Zingerman wrote:
> > On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
> >
> > ...
> >
> > > +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
> > > +{
> > > + const struct btf_loc_param *p;
> > > + __u32 i = 0, vlen;
> > > + __u64 value;
> > > + bool negative = false;
> > > + char regs[32] = {};
> > > + char num[32] = {};
> > > + const char *op = "";
> > > +
> > > + if (!t || !btf_is_loc_param(t)) {
> > > + snprintf(str, sz, "<invalid>");
> > > + return;
> > > + }
> > > +
> > > + p = btf_loc_param(t);
> > > + vlen = btf_vlen(t);
> > > +
> > > + if (p->flags & BTF_LOC_PARAM_REG) {
> > > + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
> > > +
> > > + if (nregs > vlen) {
> > > + snprintf(str, sz, "?");
> > > + return;
> > > + }
> > > +
> > > + switch (nregs) {
> > > + case 2:
> > > + snprintf(regs, sizeof(regs), "r%u, r%u",
> > > + p->values[0], p->values[1]);
> >
> > I agree with Jiri regarding the register names. It's not a huge table,
> > e.g. [1], 20 lines for x86 ~> 100-200 lines that would not really
> > change to handle all architectures that have BPF jits.
> > And it would be very convenient for those using the tool.
> >
>
> Yeah, it's doable, it's just that it is more portable when done in
> pfunct; we can use dwfl interface to get register names [1]. With
> pfunct changes in that tree we get output that is either arch-independent
> (standalone BTF) or when combined with ELF info from vmlinux
> gives us the arch-specific register names, containing function etc.
> To see the inline site for ip_send_skb for example:
>
> $ pfunct --inline_sites -f ip_send_skb --elf vmlinux /sys/kernel/btf/vmlinux.inline
> 0xffffffff8201f241 [ip_push_pending_frames+0x31, .text +0x101f241] ip_send_skb(struct net * net [%rbx], struct sk_buff * skb [%rax])
>
> (above does not need DWARF at all, just BTF + ELF info, so will work with a
> debuginfo-stripped vmlinux)
>
> So for me the tool to reach for in understanding the raw BTF is
> bpftool, whereas to apply the arch-specific transformations, locate the
> absolute addresses etc I'd use pfunct. But that's just me; I'm happy to
> go with the consensus here. Given the current library support, I think
> maintaining per-arch tables in bpftool (rather than introducing a new
> library dependency) would be the way to go if we do add it.
>
> Quentin, what do you think? Are per-arch tables for registers in bpftool
> ok from your side? Thanks!
>
> Alan
>
> [1] https://github.com/alan-maguire/dwarves/blob/0da899665b04f16c3f68c0b736834a15e045c3c9/pfunct.c#L95
Adding dwfl as an (optional?) dependency for bpftool and reusing the
same code as [1] might be an option as well.
...
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-21 21:46 ` Eduard Zingerman
@ 2026-09-22 11:59 ` Quentin Monnet
2026-09-23 8:47 ` Alan Maguire
0 siblings, 1 reply; 42+ messages in thread
From: Quentin Monnet @ 2026-09-22 11:59 UTC (permalink / raw)
To: Eduard Zingerman, Alan Maguire, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, martin.lau, memxor,
emil, bpf, nsc, puranjay, yatsenko
2026-09-21 14:46 UTC-0700 ~ Eduard Zingerman <eddyz87@gmail.com>
> On Mon, 2026-09-21 at 19:47 +0100, Alan Maguire wrote:
>> On 18/09/2026 21:51, Eduard Zingerman wrote:
>>> On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
>>>
>>> ...
>>>
>>>> +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
>>>> +{
>>>> + const struct btf_loc_param *p;
>>>> + __u32 i = 0, vlen;
>>>> + __u64 value;
>>>> + bool negative = false;
>>>> + char regs[32] = {};
>>>> + char num[32] = {};
>>>> + const char *op = "";
>>>> +
>>>> + if (!t || !btf_is_loc_param(t)) {
>>>> + snprintf(str, sz, "<invalid>");
>>>> + return;
>>>> + }
>>>> +
>>>> + p = btf_loc_param(t);
>>>> + vlen = btf_vlen(t);
>>>> +
>>>> + if (p->flags & BTF_LOC_PARAM_REG) {
>>>> + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
>>>> +
>>>> + if (nregs > vlen) {
>>>> + snprintf(str, sz, "?");
>>>> + return;
>>>> + }
>>>> +
>>>> + switch (nregs) {
>>>> + case 2:
>>>> + snprintf(regs, sizeof(regs), "r%u, r%u",
>>>> + p->values[0], p->values[1]);
>>>
>>> I agree with Jiri regarding the register names. It's not a huge table,
>>> e.g. [1], 20 lines for x86 ~> 100-200 lines that would not really
>>> change to handle all architectures that have BPF jits.
>>> And it would be very convenient for those using the tool.
>>>
>>
>> Yeah, it's doable, it's just that it is more portable when done in
>> pfunct; we can use dwfl interface to get register names [1]. With
>> pfunct changes in that tree we get output that is either arch-independent
>> (standalone BTF) or when combined with ELF info from vmlinux
>> gives us the arch-specific register names, containing function etc.
>> To see the inline site for ip_send_skb for example:
>>
>> $ pfunct --inline_sites -f ip_send_skb --elf vmlinux /sys/kernel/btf/vmlinux.inline
>> 0xffffffff8201f241 [ip_push_pending_frames+0x31, .text +0x101f241] ip_send_skb(struct net * net [%rbx], struct sk_buff * skb [%rax])
>>
>> (above does not need DWARF at all, just BTF + ELF info, so will work with a
>> debuginfo-stripped vmlinux)
>>
>> So for me the tool to reach for in understanding the raw BTF is
>> bpftool, whereas to apply the arch-specific transformations, locate the
>> absolute addresses etc I'd use pfunct. But that's just me; I'm happy to
>> go with the consensus here. Given the current library support, I think
>> maintaining per-arch tables in bpftool (rather than introducing a new
>> library dependency) would be the way to go if we do add it.
>>
>> Quentin, what do you think? Are per-arch tables for registers in bpftool
>> ok from your side? Thanks!
>>
>> Alan
>>
>> [1] https://github.com/alan-maguire/dwarves/blob/0da899665b04f16c3f68c0b736834a15e045c3c9/pfunct.c#L95
>
> Adding dwfl as an (optional?) dependency for bpftool and reusing the
> same code as [1] might be an option as well.
>
> ...
If we decide to go with arch-specific transformation, I think I'd rather
go with local tables rather than adding a dependency, unless it turns
out to be necessary. But Alan's argument makes sense to me, bpftool
prints the literal BTF encoding, and resolving to per-arch instructions
is probably best left to pfunct.
Quentin
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
2026-09-22 11:59 ` Quentin Monnet
@ 2026-09-23 8:47 ` Alan Maguire
0 siblings, 0 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-23 8:47 UTC (permalink / raw)
To: Quentin Monnet, Eduard Zingerman, ast, andrii, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, martin.lau, memxor,
emil, bpf, nsc, puranjay, yatsenko
[-- Attachment #1: Type: text/plain, Size: 4564 bytes --]
On 22/09/2026 12:59, Quentin Monnet wrote:
> 2026-09-21 14:46 UTC-0700 ~ Eduard Zingerman <eddyz87@gmail.com>
>> On Mon, 2026-09-21 at 19:47 +0100, Alan Maguire wrote:
>>> On 18/09/2026 21:51, Eduard Zingerman wrote:
>>>> On Wed, 2026-09-16 at 08:41 +0100, Alan Maguire wrote:
>>>>
>>>> ...
>>>>
>>>>> +static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
>>>>> +{
>>>>> + const struct btf_loc_param *p;
>>>>> + __u32 i = 0, vlen;
>>>>> + __u64 value;
>>>>> + bool negative = false;
>>>>> + char regs[32] = {};
>>>>> + char num[32] = {};
>>>>> + const char *op = "";
>>>>> +
>>>>> + if (!t || !btf_is_loc_param(t)) {
>>>>> + snprintf(str, sz, "<invalid>");
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + p = btf_loc_param(t);
>>>>> + vlen = btf_vlen(t);
>>>>> +
>>>>> + if (p->flags & BTF_LOC_PARAM_REG) {
>>>>> + __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
>>>>> +
>>>>> + if (nregs > vlen) {
>>>>> + snprintf(str, sz, "?");
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + switch (nregs) {
>>>>> + case 2:
>>>>> + snprintf(regs, sizeof(regs), "r%u, r%u",
>>>>> + p->values[0], p->values[1]);
>>>>
>>>> I agree with Jiri regarding the register names. It's not a huge table,
>>>> e.g. [1], 20 lines for x86 ~> 100-200 lines that would not really
>>>> change to handle all architectures that have BPF jits.
>>>> And it would be very convenient for those using the tool.
>>>>
>>>
>>> Yeah, it's doable, it's just that it is more portable when done in
>>> pfunct; we can use dwfl interface to get register names [1]. With
>>> pfunct changes in that tree we get output that is either arch-independent
>>> (standalone BTF) or when combined with ELF info from vmlinux
>>> gives us the arch-specific register names, containing function etc.
>>> To see the inline site for ip_send_skb for example:
>>>
>>> $ pfunct --inline_sites -f ip_send_skb --elf vmlinux /sys/kernel/btf/vmlinux.inline
>>> 0xffffffff8201f241 [ip_push_pending_frames+0x31, .text +0x101f241] ip_send_skb(struct net * net [%rbx], struct sk_buff * skb [%rax])
>>>
>>> (above does not need DWARF at all, just BTF + ELF info, so will work with a
>>> debuginfo-stripped vmlinux)
>>>
>>> So for me the tool to reach for in understanding the raw BTF is
>>> bpftool, whereas to apply the arch-specific transformations, locate the
>>> absolute addresses etc I'd use pfunct. But that's just me; I'm happy to
>>> go with the consensus here. Given the current library support, I think
>>> maintaining per-arch tables in bpftool (rather than introducing a new
>>> library dependency) would be the way to go if we do add it.
>>>
>>> Quentin, what do you think? Are per-arch tables for registers in bpftool
>>> ok from your side? Thanks!
>>>
>>> Alan
>>>
>>> [1] https://github.com/alan-maguire/dwarves/blob/0da899665b04f16c3f68c0b736834a15e045c3c9/pfunct.c#L95
>>
>> Adding dwfl as an (optional?) dependency for bpftool and reusing the
>> same code as [1] might be an option as well.
>>
>> ...
>
>
> If we decide to go with arch-specific transformation, I think I'd rather
> go with local tables rather than adding a dependency, unless it turns
> out to be necessary. But Alan's argument makes sense to me, bpftool
> prints the literal BTF encoding, and resolving to per-arch instructions
> is probably best left to pfunct.
>
Thanks Quentin! To investigate I implemented the table for i386, x86_64,
aarch64 and s390 (attached). So while it is technically straightforward
what I realized however is that we have to make an architecture
choice that isn't available from /sys/kernel/btf data directly. So we
are stuck using the arch bpftool has been compiled to (unless we add
another parameter to pass to bpftool raw dump). So while "use the same
arch as bpftool" is likely often the right answer, we might sometimes
want to examine raw BTF for another architecture. That feels to me like
an argument for more neutral raw output too.
I've updated pfunct a bit to handle a few situations
- raw BTF only; no reg interpretation, no absolute address resolution
- raw BTF + --elf file option ; adds per-arch reg interpretation, containing function and
absolute address resolution
- raw BTF + --running option ; uses reg interpretation, kallsyms/module data
to do address and containing function resolution that is kASLR-friendly
So the logical split is bpftool tells me what the BTF is; pfunct tells
me what it means in the context of the associated ELF file or running kernel.
Anyway we can go either way, but FWIW my vote is to keep bpftool
arch-neutral. Thanks!
Alan
[-- Attachment #2: 0009-bpftool-Add-ability-to-dump-LOC_PARAM-LOC_PROTO-and-.patch --]
[-- Type: text/x-patch, Size: 9569 bytes --]
From 2929a894efff4e60be5c712345e36dc16bdd2b7e Mon Sep 17 00:00:00 2001
From: Alan Maguire <alan.maguire@oracle.com>
Date: Thu, 18 Sep 2025 09:19:04 +0000
Subject: [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM,
LOC_PROTO and LOCSEC
In raw mode ensure we can dump new BTF kinds in normal/json format.
BTF_KIND_LOC_PARAMs are rendered as strings, for example a
const value of 0x2a and a dereference of %r10 + 0x20:
[12] LOC_PARAM '(anon)' size=4 flags=0x2 vlen=1 values='0x2a'
[13] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(%r10 + 0x20)'
LOC_PROTOs render the associated values of each of their
LOC_PARAMs for easier readability:
[14] LOC_PROTO '(anon)' vlen=2
type_id=12 value='0x2a'
type_id=13 value='*(%r10 + 0x20)'
and LOCSEC shows function name associated with site:
[15] LOCSEC 'inline.text' vlen=1
name='foo' func_type_id=5 loc_proto_type_id=14 offset=64
Registers are displayed as their arch-specific DW_OP_reg*
equivalents where available.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/bpf/bpftool/btf.c | 264 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 264 insertions(+)
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 65e8a29277e6..384fef2dadff 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -29,6 +29,40 @@
#define MAX_ROOT_IDS 16
#define MAX_BTF_FILES 64
+#define MAX_LOC_PARAM_WORDS 8
+
+/* DWARF register numbers used by BTF_KIND_LOC_PARAM. */
+#if defined(__x86_64__)
+static const char * const btf_dwarf_reg_names[] = {
+ "rax", "rdx", "rcx", "rbx", "rsi", "rdi", "rbp", "rsp",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "rip",
+};
+#elif defined(__i386__)
+static const char * const btf_dwarf_reg_names[] = {
+ "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "eip",
+};
+#elif defined(__aarch64__)
+static const char * const btf_dwarf_reg_names[] = {
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+ "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
+ "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp",
+};
+#elif defined(__s390x__) || defined(__s390__)
+static const char * const btf_dwarf_reg_names[] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "f0", "f2", "f4", "f6", "f1", "f3", "f5", "f7",
+ "f8", "f10", "f12", "f14", "f9", "f11", "f13", "f15",
+ "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7",
+ "c8", "c9", "c10", "c11", "c12", "c13", "c14", "c15",
+ "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
+ "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15",
+ "pswm", "pswa",
+};
+#else
+static const char * const btf_dwarf_reg_names[] = {};
+#endif
static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_UNKN] = "UNKNOWN",
@@ -51,6 +85,9 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
+ [BTF_KIND_LOC_PARAM] = "LOC_PARAM",
+ [BTF_KIND_LOC_PROTO] = "LOC_PROTO",
+ [BTF_KIND_LOCSEC] = "LOCSEC",
};
struct sort_datum {
@@ -117,6 +154,144 @@ static int btf_kind_safe(int kind)
return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
}
+static void btf_loc_param_reg_str(__u32 reg, char *str, size_t sz)
+{
+ const char *name = NULL;
+
+ if (reg < ARRAY_SIZE(btf_dwarf_reg_names))
+ name = btf_dwarf_reg_names[reg];
+ if (name)
+ snprintf(str, sz, "%%%s", name);
+ else
+ snprintf(str, sz, "r%u", reg);
+}
+
+static void btf_loc_param_raw_str(const struct btf_loc_param *p, __u32 vlen,
+ char *str, size_t sz)
+{
+ __u32 i, nr_words = min(vlen, (__u32)MAX_LOC_PARAM_WORDS);
+ size_t off = 0;
+
+ if (!sz)
+ return;
+
+ off += snprintf(str + off, sz - off, "raw=[");
+ for (i = 0; i < nr_words && off < sz; i++)
+ off += snprintf(str + off, sz - off, "%s0x%08x",
+ i ? ", " : "", p->values[i]);
+ if (vlen > nr_words && off < sz)
+ off += snprintf(str + off, sz - off, ", ...");
+ if (off < sz)
+ snprintf(str + off, sz - off, "]");
+}
+
+static void btf_loc_param_str(const struct btf_type *t, char *str, size_t sz)
+{
+ const struct btf_loc_param *p;
+ __u32 i = 0, vlen;
+ __u64 value;
+ __u32 value_size;
+ bool negative = false;
+ char regs[32] = {};
+ char num[32] = {};
+ const char *op = "";
+
+ if (!t || !btf_is_loc_param(t)) {
+ snprintf(str, sz, "<invalid>");
+ return;
+ }
+
+ p = btf_loc_param(t);
+ vlen = btf_vlen(t);
+
+ if (p->flags & BTF_LOC_PARAM_REG) {
+ __u32 nregs = (p->flags == BTF_LOC_PARAM_REG) ? vlen : 1;
+
+ if (nregs > vlen) {
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+
+ switch (nregs) {
+ case 2:
+ btf_loc_param_reg_str(p->values[0], regs, sizeof(regs));
+ snprintf(regs + strlen(regs), sizeof(regs) - strlen(regs),
+ ", ");
+ btf_loc_param_reg_str(p->values[1],
+ regs + strlen(regs),
+ sizeof(regs) - strlen(regs));
+ break;
+ case 1:
+ if (p->values[0] == BTF_LOC_PARAM_FBREG)
+ snprintf(regs, sizeof(regs), "fbreg");
+ else
+ btf_loc_param_reg_str(p->values[0], regs, sizeof(regs));
+ break;
+ default:
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ i += nregs;
+ }
+ if (p->flags & (BTF_LOC_PARAM_CONST|BTF_LOC_PARAM_OFFSET)) {
+ switch (vlen - i) {
+ case 1:
+ value_size = sizeof(p->values[0]);
+ value = p->values[i];
+ break;
+ case 2:
+ value_size = 2 * sizeof(p->values[0]);
+ value = ((__u64)p->values[i + 1] << 32) | p->values[i];
+ break;
+ default:
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ i = vlen;
+ if (p->flags & BTF_LOC_PARAM_SIGNED) {
+ /*
+ * size describes the represented parameter, so it
+ * describes a constant's signed width. An offset
+ * is determined by its value words after the register
+ * number.
+ */
+ __u32 size = p->flags & BTF_LOC_PARAM_OFFSET ?
+ value_size : t->size;
+ __u32 bits = size * 8;
+
+ /*
+ * Since we represent constant values in hex, we
+ * need to determine if the value is negative so
+ * we can prepend a "-", and also fix the value
+ * to be positive so we can have - 0x<value>.
+ */
+ if (size && size <= sizeof(value)) {
+ if (size < sizeof(value))
+ value &= (1ULL << bits) - 1;
+ negative = value & (1ULL << (bits - 1));
+ if (negative)
+ value = size == sizeof(value) ? -value :
+ (1ULL << bits) - value;
+ }
+ }
+ snprintf(num, sizeof(num), "0x%llx%s", (unsigned long long)value,
+ p->flags & BTF_LOC_PARAM_ADDR ? " (addr)" : "");
+ }
+ if (i != vlen) {
+ btf_loc_param_raw_str(p, vlen, str, sz);
+ return;
+ }
+ if (num[0])
+ op = regs[0] ? (negative ? " - " : " + ") : negative ? "-" : "";
+
+ snprintf(str, sz, "%s%s%s%s%s",
+ p->flags & BTF_LOC_PARAM_DEREF ? "*(" : "",
+ regs,
+ op,
+ num,
+ p->flags & BTF_LOC_PARAM_DEREF ? ")" : "");
+}
+
static int dump_btf_type(const struct btf *btf, __u32 id,
const struct btf_type *t)
{
@@ -415,6 +590,95 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
}
break;
}
+ case BTF_KIND_LOC_PARAM: {
+ const struct btf_loc_param *p = btf_loc_param(t);
+ __u32 vlen = btf_vlen(t);
+ char param_str[256] = {};
+
+ btf_loc_param_str(t, param_str, sizeof(param_str));
+
+ if (json_output) {
+ jsonw_uint_field(w, "size", t->size);
+ jsonw_uint_field(w, "flags", p->flags);
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_string_field(w, "values", param_str);
+ } else {
+ printf(" size=%u flags=0x%x vlen=%u values='%s'",
+ t->size, p->flags, vlen, param_str);
+ }
+ break;
+ }
+ case BTF_KIND_LOC_PROTO: {
+ __u32 *params = btf_loc_proto_params(t);
+ __u32 i, vlen = btf_vlen(t);
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "params");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, params++) {
+ const struct btf_type *p;
+ char param_str[256] = {};
+
+ if (*params) {
+ p = btf__type_by_id(btf, *params);
+ btf_loc_param_str(p, param_str, sizeof(param_str));
+ } else {
+ snprintf(param_str, sizeof(param_str), "<unavailable>");
+ }
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "type_id", *params);
+ jsonw_string_field(w, "value", param_str);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\ttype_id=%u value='%s'", *params, param_str);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
+
+ case BTF_KIND_LOCSEC: {
+ struct btf_loc *locs = btf_locsec_locs(t);
+ __u32 i, vlen = btf_vlen(t);
+
+ if (json_output) {
+ jsonw_uint_field(w, "vlen", vlen);
+ jsonw_name(w, "locs");
+ jsonw_start_array(w);
+ } else {
+ printf(" vlen=%u", vlen);
+ }
+
+ for (i = 0; i < vlen; i++, locs++) {
+ const struct btf_type *f = btf__type_by_id(btf, locs->func);
+ const char *name = "<invalid>";
+
+ if (f && btf_is_func(f))
+ name = btf_str(btf, f->name_off);
+
+ if (json_output) {
+ jsonw_start_object(w);
+ jsonw_uint_field(w, "func_type_id", locs->func);
+ jsonw_string_field(w, "name", name);
+ jsonw_uint_field(w, "loc_proto_type_id", locs->loc_proto);
+ jsonw_uint_field(w, "offset", locs->offset);
+ jsonw_end_object(w);
+ } else {
+ printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u",
+ name, locs->func, locs->loc_proto, locs->offset);
+ }
+ }
+ if (json_output)
+ jsonw_end_array(w);
+ break;
+ }
default:
break;
}
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (8 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 7:56 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
2026-09-16 7:41 ` [PATCH v3 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
10 siblings, 2 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
Add a bpftool raw BTF dump test covering LOC_PARAM, LOC_PROTO, and
LOCSEC types. Verify LOC_PARAM expressions, LOC_PROTO parameter values,
and LOCSEC function name, type id, and offset output.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Assisted-by: OpenAI Codex (GPT 5.6)
---
.../bpf/prog_tests/bpftool_btf_dump.c | 84 +++++++++++++++++++
1 file changed, 84 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 d5b25302b0c8..f8ea9dfd0780 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,35 @@ static struct btf *mk_btf(void)
return btf;
}
+static struct btf *mk_loc_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_func_proto(btf, 1);
+ btf__add_func_param(btf, "arg1", 1);
+ btf__add_func_param(btf, "arg2", 1);
+ btf__add_func(btf, "foo", BTF_FUNC_STATIC, 2);
+
+ btf__add_loc_param(btf, 4, BTF_LOC_PARAM_REG);
+ btf__add_loc_param_value(btf, 1);
+ btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+ BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET);
+ btf__add_loc_param_value(btf, 2);
+ btf__add_loc_param_value(btf, 0x10);
+ btf__add_loc_proto(btf);
+ btf__add_loc_proto_param(btf, 4);
+ btf__add_loc_proto_param(btf, 5);
+ btf__add_locsec(btf, "inline.text");
+ btf__add_locsec_loc(btf, 3, 6, 64);
+
+ return btf;
+}
+
static int btf_to_tmpfile(const struct btf *btf, char *path)
{
ssize_t written;
@@ -105,6 +134,26 @@ static char *dump_c(const char *btf_path, bool sorted)
return buf;
}
+static char *dump_raw(const char *btf_path)
+{
+ char args[MAX_BPFTOOL_CMD_LEN];
+ char *buf;
+ int err;
+
+ buf = malloc(DUMP_BUF_SZ);
+ if (!ASSERT_OK_PTR(buf, "alloc_dump"))
+ return NULL;
+
+ snprintf(args, sizeof(args), "btf dump file %s", btf_path);
+ err = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);
+ if (!ASSERT_OK(err, "btf_dump_raw")) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
static char *read_expected(const char *path)
{
char *buf = NULL;
@@ -155,6 +204,31 @@ static void test_dump(const char *btf_path, bool sorted)
free(dump);
}
+static void test_loc_dump(const char *btf_path)
+{
+ const char expected[] =
+ "[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED\n"
+ "[2] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
+ "\t'arg1' type_id=1\n"
+ "\t'arg2' type_id=1\n"
+ "[3] FUNC 'foo' type_id=2 linkage=static\n"
+ "[4] LOC_PARAM '(anon)' size=4 flags=0x8 vlen=1 values='r1'\n"
+ "[5] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(r2 + 0x10)'\n"
+ "[6] LOC_PROTO '(anon)' vlen=2\n"
+ "\ttype_id=4 value='r1'\n"
+ "\ttype_id=5 value='*(r2 + 0x10)'\n"
+ "[7] LOCSEC 'inline.text' vlen=1\n"
+ "\tname='foo' func_type_id=3 loc_proto_type_id=6 offset=64\n";
+ char *dump;
+
+ dump = dump_raw(btf_path);
+ if (!dump)
+ return;
+
+ ASSERT_OK(compare_text_to_expected(dump, expected), "cmp_loc_dump");
+ free(dump);
+}
+
void test_bpftool_btf_dump(void)
{
char path[PATH_MAX];
@@ -175,4 +249,14 @@ void test_bpftool_btf_dump(void)
unlink(path);
out_btf:
btf__free(btf);
+
+ btf = mk_loc_btf();
+ if (!btf)
+ return;
+
+ if (!btf_to_tmpfile(btf, path) && test__start_subtest("loc_dump"))
+ test_loc_dump(path);
+
+ unlink(path);
+ btf__free(btf);
}
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info
2026-09-16 7:41 ` [PATCH v3 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
@ 2026-09-16 7:56 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-09-16 7:56 UTC (permalink / raw)
To: Alan Maguire; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] bpf: uninitialized stack variable path passed to unlink() on error path
--
commit 7571df89a1b4928d95fd5e997d48f8637c83a473
Author: Alan Maguire <alan.maguire@oracle.com>
selftests/bpf: Test bpftool dump of BTF location info
Add a bpftool raw BTF dump test covering LOC_PARAM, LOC_PROTO, and
LOCSEC types.
> diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
> --- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
> +++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
> @@ -175,4 +249,14 @@ void test_bpftool_btf_dump(void)
> unlink(path);
> out_btf:
> btf__free(btf);
> +
> + btf = mk_loc_btf();
> + if (!btf)
> + return;
> +
> + if (!btf_to_tmpfile(btf, path) && test__start_subtest("loc_dump"))
> + test_loc_dump(path);
> +
> + unlink(path);
[Severity: Medium]
Is there a risk of passing uninitialized stack memory to unlink() here?
If btf_to_tmpfile() fails early, for example if btf__raw_data() returns NULL:
tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c:btf_to_tmpfile() {
...
raw = btf__raw_data(btf, &sz);
if (!ASSERT_OK_PTR(raw, "raw_data"))
return -1;
...
}
The path buffer remains uninitialized stack memory. The short-circuit
evaluation in test_bpftool_btf_dump() will skip test_loc_dump(), but it
looks like the execution will then proceed directly to this unconditional
unlink(path) call.
> + btf__free(btf);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260916074118.1007116-1-alan.maguire@oracle.com?part=10
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info
2026-09-16 7:41 ` [PATCH v3 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
2026-09-16 7:56 ` sashiko-bot
@ 2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: bot+bpf-ci @ 2026-09-16 9:03 UTC (permalink / raw)
To: alan.maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2482 bytes --]
> 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 d5b25302b0c89..f8ea9dfd0780b 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,35 @@ static struct btf *mk_btf(void)
> return btf;
> }
>
> +static struct btf *mk_loc_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_func_proto(btf, 1);
> + btf__add_func_param(btf, "arg1", 1);
> + btf__add_func_param(btf, "arg2", 1);
> + btf__add_func(btf, "foo", BTF_FUNC_STATIC, 2);
> +
> + btf__add_loc_param(btf, 4, BTF_LOC_PARAM_REG);
> + btf__add_loc_param_value(btf, 1);
> + btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
> + BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET);
> + btf__add_loc_param_value(btf, 2);
> + btf__add_loc_param_value(btf, 0x10);
> + btf__add_loc_proto(btf);
> + btf__add_loc_proto_param(btf, 4);
> + btf__add_loc_proto_param(btf, 5);
> + btf__add_locsec(btf, "inline.text");
> + btf__add_locsec_loc(btf, 3, 6, 64);
> +
> + return btf;
> +}
This isn't a bug, but mk_btf() ends with a VALIDATE_RAW_BTF() sanity
check -- would it be worth adding one to mk_loc_btf() too (or capturing
the btf__add_*() return ids), so a construction failure shows up as
itself rather than as a dump diff?
> @@ -105,6 +134,26 @@ static char *dump_c(const char *btf_path, bool sorted)
> return buf;
> }
>
> +static char *dump_raw(const char *btf_path)
> +{
> + char args[MAX_BPFTOOL_CMD_LEN];
> + char *buf;
> + int err;
> +
> + buf = malloc(DUMP_BUF_SZ);
> + if (!ASSERT_OK_PTR(buf, "alloc_dump"))
> + return NULL;
> +
> + snprintf(args, sizeof(args), "btf dump file %s", btf_path);
> + err = get_bpftool_command_output(args, buf, DUMP_BUF_SZ);
> + if (!ASSERT_OK(err, "btf_dump_raw")) {
> + free(buf);
> + return NULL;
> + }
> +
> + return buf;
> +}
This isn't a bug, but dump_raw() looks like dump_c() with a different
args string and tag -- would a shared helper that takes the extra
bpftool arguments and an assert tag cover both?
[ ... ]
---
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/35072175008
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH v3 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds
2026-09-16 7:41 [PATCH v3 bpf-next 00/11] Support inline functions in BTF Alan Maguire
` (9 preceding siblings ...)
2026-09-16 7:41 ` [PATCH v3 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
@ 2026-09-16 7:41 ` Alan Maguire
2026-09-16 8:01 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
10 siblings, 2 replies; 42+ messages in thread
From: Alan Maguire @ 2026-09-16 7:41 UTC (permalink / raw)
To: ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire
Update BTF specification to describe encoding schemes for
BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO and BTF_KIND_LOCSEC.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
Documentation/bpf/btf.rst | 80 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 2 deletions(-)
diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
index 004aa1058d85..70ab6ad608ae 100644
--- a/Documentation/bpf/btf.rst
+++ b/Documentation/bpf/btf.rst
@@ -88,6 +88,9 @@ sequentially and type id is assigned to each recognized type starting from id
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enumeration up to 64-bit values */
+ #define BTF_KIND_LOC_PARAM 20 /* Location description (register, const etc) */
+ #define BTF_KIND_LOC_PROTO 21 /* Set of location parameters for site */
+ #define BTF_KIND_LOCSEC 22 /* Section with site descriptions */
Note that the type section encodes debug info, not just pure types.
``BTF_KIND_FUNC`` is not a type, and it represents a defined subprogram.
@@ -104,11 +107,13 @@ Each type contains the following common data::
* decl_tag and type_tag
*/
__u32 info;
- /* "size" is used by INT, ENUM, STRUCT, UNION and ENUM64.
+ /* "size" is used by INT, ENUM, STRUCT, UNION, ENUM64 and
+ * LOC_PARAM.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
- * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG.
+ * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG. It is unused by
+ * LOC_PROTO and LOCSEC.
* "type" is a type_id referring to another type.
*/
union {
@@ -563,6 +568,77 @@ The ``btf_enum64`` encoding:
If the original enum value is signed and the size is less than 8,
that value will be sign extended into 8 bytes.
+2.2.20 BTF_KIND_LOC_PARAM
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: 0
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOC_PARAM
+ * ``info.vlen``: number of 32-bit location value words
+ * ``size``: size in bytes of the represented parameter: 1, 2, 4, 8 or 16
+
+``btf_type`` is followed by a ``struct btf_loc_param`` and ``info.vlen``
+number of 32-bit value words.::
+
+ struct btf_loc_param {
+ __u32 flags;
+ __u32 values[];
+ };
+
+The ``flags`` field describes how to interpret ``values``:
+
+ * ``BTF_LOC_PARAM_CONST`` describes a constant; the value is stored in
+ low-word, high-word order when it requires 64 bits.
+ * ``BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST`` describes an address to be
+ normalized relative to the kernel or module base address.
+ * ``BTF_LOC_PARAM_REG`` with one word describes a register number; with two
+ words it describes a multi-register parameter.
+ * ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_OFFSET`` describes an address held in
+ a register plus an offset. Adding ``BTF_LOC_PARAM_DEREF`` dereferences
+ that address. ``BTF_LOC_PARAM_REG | BTF_LOC_PARAM_DEREF`` with one word
+ dereferences the value held in the register.
+ * ``BTF_LOC_PARAM_SIGNED`` makes a constant or offset signed.
+
+2.2.21 BTF_KIND_LOC_PROTO
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: 0
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOC_PROTO
+ * ``info.vlen``: number of function parameter locations
+ * ``type``: 0
+
+``btf_type`` is followed by ``info.vlen`` number of ``__u32`` BTF type IDs.
+Each entry corresponds to a function parameter at an inline site. An entry is
+either 0, meaning that no location information is available, or the type ID
+of a ``BTF_KIND_LOC_PARAM``.
+
+2.2.22 BTF_KIND_LOCSEC
+~~~~~~~~~~~~~~~~~~~~~~
+
+``struct btf_type`` encoding requirement:
+ * ``name_off``: offset to a valid ELF section name
+ * ``info.kind_flag``: 0
+ * ``info.kind``: BTF_KIND_LOCSEC
+ * ``info.vlen``: number of inline sites in the section
+ * ``type``: 0
+
+``btf_type`` is followed by ``info.vlen`` number of ``struct btf_loc``.::
+
+ struct btf_loc {
+ __u32 func;
+ __u32 loc_proto;
+ __u32 offset;
+ };
+
+The ``func`` field is the non-zero type ID of the ``BTF_KIND_FUNC`` being
+described. ``loc_proto`` is the non-zero type ID of the associated
+``BTF_KIND_LOC_PROTO``. ``offset`` is the location address offset relative to
+the kernel or module base address. A LOCSEC named ``inline.text``, for example,
+contains inline-site records for the ``.text`` section.
+
2.3 Constant Values
-------------------
--
2.43.5
^ permalink raw reply related [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds
2026-09-16 7:41 ` [PATCH v3 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
@ 2026-09-16 8:01 ` sashiko-bot
2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: sashiko-bot @ 2026-09-16 8:01 UTC (permalink / raw)
To: Alan Maguire; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] bpf: BTF_KIND_LOCSEC offset definition is broken for kernel modules
- [Medium] bpf: BTF_KIND_LOC_PARAM size restricts representing larger function parameters
--
commit 2c604400821fbb8c84a44304f76a074c263fd25e
Author: Alan Maguire <alan.maguire@oracle.com>
Documentation/bpf: Describe new location-related BTF kinds
Update BTF specification to describe encoding schemes for
BTF_KIND_LOC_PARAM, BTF_KIND_LOC_PROTO and BTF_KIND_LOCSEC.
> diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
> index 004aa1058d857..70ab6ad608ae5 100644
> --- a/Documentation/bpf/btf.rst
> +++ b/Documentation/bpf/btf.rst
[ ... ]
> @@ -563,6 +568,77 @@ The ``btf_enum64`` encoding:
> If the original enum value is signed and the size is less than 8,
> that value will be sign extended into 8 bytes.
>
> +2.2.20 BTF_KIND_LOC_PARAM
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +``struct btf_type`` encoding requirement:
> + * ``name_off``: 0
> + * ``info.kind_flag``: 0
> + * ``info.kind``: BTF_KIND_LOC_PARAM
> + * ``info.vlen``: number of 32-bit location value words
> + * ``size``: size in bytes of the represented parameter: 1, 2, 4, 8 or 16
[Severity: Medium]
Does this limitation restrict representing parameters larger than 16 bytes?
If the UAPI restricts the size to a maximum of 16 bytes, it prevents BPF from
accurately describing functions with large by-value parameters. For example,
structures passed by value on the stack, or ARM64 Homogeneous Floating-point
Aggregates that can be up to 64 bytes.
[ ... ]
> +2.2.22 BTF_KIND_LOCSEC
> +~~~~~~~~~~~~~~~~~~~~~~
> +
> +``struct btf_type`` encoding requirement:
> + * ``name_off``: offset to a valid ELF section name
> + * ``info.kind_flag``: 0
> + * ``info.kind``: BTF_KIND_LOCSEC
> + * ``info.vlen``: number of inline sites in the section
> + * ``type``: 0
> +
> +``btf_type`` is followed by ``info.vlen`` number of ``struct btf_loc``.::
> +
> + struct btf_loc {
> + __u32 func;
> + __u32 loc_proto;
> + __u32 offset;
> + };
> +
> +The ``func`` field is the non-zero type ID of the ``BTF_KIND_FUNC`` being
> +described. ``loc_proto`` is the non-zero type ID of the associated
> +``BTF_KIND_LOC_PROTO``. ``offset`` is the location address offset relative to
> +the kernel or module base address. A LOCSEC named ``inline.text``, for example,
> +contains inline-site records for the ``.text`` section.
[Severity: Medium]
Is this definition of the offset field correct for kernel modules?
Modern kernel module loading splits executable sections (e.g., .text,
.init.text) into independently allocated memory regions. An offset relative
to a single module base address cannot reliably resolve to the correct memory
address.
Should this be defined as an in-section offset instead? Tools relying on this
documentation will calculate incorrect absolute addresses for inline sites in
loadable modules.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260916074118.1007116-1-alan.maguire@oracle.com?part=11
^ permalink raw reply [flat|nested] 42+ messages in thread* Re: [PATCH v3 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds
2026-09-16 7:41 ` [PATCH v3 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
2026-09-16 8:01 ` sashiko-bot
@ 2026-09-16 9:03 ` bot+bpf-ci
1 sibling, 0 replies; 42+ messages in thread
From: bot+bpf-ci @ 2026-09-16 9:03 UTC (permalink / raw)
To: alan.maguire, ast, andrii, eddyz87, jolsa
Cc: daniel, ihor.solodrai, yonghong.song, song, qmo, martin.lau,
memxor, emil, bpf, nsc, puranjay, yatsenko, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1630 bytes --]
> diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
> index 004aa1058d857..70ab6ad608ae5 100644
> --- a/Documentation/bpf/btf.rst
> +++ b/Documentation/bpf/btf.rst
[ ... ]
> @@ -104,11 +107,13 @@ Each type contains the following common data::
> * decl_tag and type_tag
> */
> __u32 info;
> - /* "size" is used by INT, ENUM, STRUCT, UNION and ENUM64.
> + /* "size" is used by INT, ENUM, STRUCT, UNION, ENUM64 and
> + * LOC_PARAM.
> * "size" tells the size of the type it is describing.
> *
> * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
> - * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG.
> + * FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG. It is unused by
> + * LOC_PROTO and LOCSEC.
> * "type" is a type_id referring to another type.
> */
> union {
The comment is being updated to add LOC_PARAM to the list of kinds that use
'size', but should BTF_KIND_FLOAT be included as well?
Section 2.2.16 documents FLOAT as using size ('size: the size of the float
type in bytes: 2, 4, 8, 12 or 16.'), and kernel/bpf/btf.c btf_float_check_meta()
validates t->size. The omission is pre-existing (and mirrored in
include/uapi/linux/btf.h), but since the line is being rewritten, adding FLOAT
here would make the list complete.
---
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/35072175008
^ permalink raw reply [flat|nested] 42+ messages in thread