BPF List
 help / color / mirror / Atom feed
* [PATCH v4 bpf-next 00/11] Support inline functions in BTF
@ 2026-09-24 11:14 Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
                   ` (11 more replies)
  0 siblings, 12 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, martin.lau,
	memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire

This series adds support to facilitate tracing of inline function
sites using BPF Type Format (BTF) information. An excellent overview
of the problem and proposed solution presented at LSF/MM/BPF is
available at [1].

The aim is to produce a compact representation providing sufficient
information to a tracer wishing to instrument an inline site via a
kprobe.  The challenge to solve is compact representation - my
local bpf-next builds show nearly 600,000 inline sites for approximately
100,000 functions.  Any BTF representation should utilize deduplication
where possible to minimize overheads.

The approach used here is to encode a series of inline sites in
a BTF DATASEC-like LOCSEC named for the associated section (like
"inline.text", where each entry consists of a

<function type id, location prototype id, offset from base address>

triple. The function type id is the BTF_KIND_FUNC that was inlined,
the location prototype is a BTF_KIND_LOC_PROTO which tells us
for each function parameter how it is represented at the site.
It is a collection of either type id 0 (parameter not available)
or BTF_KIND_LOC_PARAM ids, the latter encoding a register number,
a dereference, a constant etc.  Finally the offset is relative to
the base address, so in the case of the kernel this allows for
kASLR, and modules addresses are relative to module base address.

Full location parameter information is available for ~78% of
inlined functions; in other words all function parameters are
expressed via BTF_KIND_LOC_PARAM and can be retrieved.  Those
remaining have more complex multi-expression encoding or are
not available at all.

This series focuses on the underlying libbpf/kernel/bpftool support
for handling location data; with it in place pahole can utilize
the provided interfaces to generate inline data in combination with
general BTF data; it will then be separated out via resolve_btfids
into non-inline and inline information.

Support in pahole for generation of inline info using the libbpf
interfaces in this series is available in [2].

Patch 1 consists of the UAPI changes and associated basic support
for KIND_LOC[SEC|PARAM|PROTO].

Patch 2 adds associated libbpf support, covering dedup, field
iteration and distillation.

Patches 3-6 test various aspects of these features.

Patches 7-9 update bpftool to handle multi-split BTF (where
we potentially have module inline info sitting atop module
BTF info which in turn has vmlinux BTF as its base), and add
support for displaying location info in raw BTF dump.
Patch 10 tests bpftool raw dump of locations.

Finally patch 11 documents the location kinds.

Changes since v3 [3]:

- Fix up UAPI descriptions for btf_type (bots, patch 1)
- Add LOC_PROTO/LOCSEC to btf_type_is_resolve_source_only()
  (bots, patch 2)
- Fix LOCSEC validation to reject empty name (bots, patch 2)
- Make use of values[] array in helper (Eduard, patch 3)
- Improved testing of distill by adding unrelated types
  to make relocate harder (Eduard, patch 6)
- Use BTF_LOC_PARAM_FBREG instead of 33 as register number
  for fbreg; placing it out of range of DWARF register numbers
  is safer since DW_OP_regx allows numbers > 31.
- Improve set of tested LOC_PARAM/PROTO in bpftool test

Changes since v2 [4]:

- Split out location representation/handling (this series) from
  additions to kbuild machinery to add inline info.  This series
  provides the needed libbpf interfaces to pahole for inline location
  generation. A follow-up series will add libbpf permute support
  for splitting inline from non-inline info such that resolve_btfids
  can consume it, kbuild support for pahole inline flags and sysfs
  exposure of inline BTF.
- Updated location parameter API name, function to use flex array
  for parameter values (Eduard, patches 1, 2)
- Fixed dedup issues (Eduard, patch 2)
- Improved dedup/distill tests to cover more complex cases
  (patches 5, 6)
- Updated bpftool dump output to be more expressive for location
  parameters, prototypes and location sections (Alexei, patch 9).

Changes since RFC [5]:

- Support for distilled base BTF
- Support for BTF_INLINE=m on-demand loading
- Reworked inline support to handle new resolve_btfids model
- .BTF.inline sections host FUNCs/FUNC_PROTOs/strings that are
  needed for inline info only, avoiding polluting standard
  vmlinux/module BTFs

[1] https://lwn.net/Articles/1083985/
[2] https://github.com/alan-maguire/dwarves/tree/btf-inline-v4
[3] https://lore.kernel.org/bpf/20260916074118.1007116-1-alan.maguire@oracle.com/
[4] https://lore.kernel.org/bpf/20260901165757.801449-1-alan.maguire@oracle.com/
[5] https://lore.kernel.org/bpf/20251008173512.731801-1-alan.maguire@oracle.com/

Alan Maguire (11):
  btf: Extend UAPI to support BTF location (inline site) info
  libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
  selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC]
  selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests
  selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests
  selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to
    split BTF
  bpftool: Handle multi-split BTF by supporting multiple base BTFs
  bpftool: Document support for multi-split BTF
  bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
  selftests/bpf: Test bpftool dump of BTF location info
  Documentation/bpf: Describe new location-related BTF kinds

 Documentation/bpf/btf.rst                     |  83 +++-
 include/linux/btf.h                           |  22 +-
 include/uapi/linux/btf.h                      |  83 +++-
 kernel/bpf/btf.c                              | 322 +++++++++++++-
 .../bpf/bpftool/Documentation/bpftool-btf.rst |   7 +-
 tools/bpf/bpftool/btf.c                       | 226 +++++++++-
 tools/bpf/bpftool/main.c                      |  24 +-
 tools/include/uapi/linux/btf.h                |  83 +++-
 tools/lib/bpf/btf.c                           | 408 +++++++++++++++++-
 tools/lib/bpf/btf.h                           |  50 +++
 tools/lib/bpf/btf_dump.c                      |   9 +
 tools/lib/bpf/btf_iter.c                      |  18 +
 tools/lib/bpf/libbpf.c                        |   3 +
 tools/lib/bpf/libbpf.map                      |   6 +
 tools/lib/bpf/libbpf_internal.h               |   2 +-
 tools/testing/selftests/bpf/btf_helpers.c     |  35 +-
 .../bpf/prog_tests/bpftool_btf_dump.c         | 121 +++++-
 .../bpf/prog_tests/btf_dedup_split.c          | 116 +++++
 .../selftests/bpf/prog_tests/btf_distill.c    | 106 +++++
 .../selftests/bpf/prog_tests/btf_field_iter.c |  31 +-
 20 files changed, 1726 insertions(+), 29 deletions(-)

-- 
2.43.5


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

* [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 12:12   ` bot+bpf-ci
  2026-09-24 15:18   ` Alexei Starovoitov
  2026-09-24 11:14 ` [PATCH v4 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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       |  83 ++++++++-
 kernel/bpf/btf.c               | 322 ++++++++++++++++++++++++++++++++-
 tools/include/uapi/linux/btf.h |  83 ++++++++-
 4 files changed, 499 insertions(+), 11 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..11d65871d163 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -54,8 +54,8 @@ struct btf_type {
 	 *             decl_tag and type_tag
 	 */
 	__u32 info;
-	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
-	 * "size" tells the size of the type it is describing.
+	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC, 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, VAR, DECL_TAG and TYPE_TAG.
@@ -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,79 @@ 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.  Register numbers are
+ *   numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since
+ *   DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer
+ *   to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for
+ *   the frame base register to avoid collisions.
+ * - 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 > 1 specifies the register number
+ *   in the first 4-byte value and the offset in the remainder.
+ *   In the case of REG and OFFSET combinations, the OFFSET has the width
+ *   of the value words while the type size describes the represented parameter,
+ *   so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be
+ *   an 8-byte register dereference with signed 4-byte offset, since the vlen 2
+ *   values consist of a register value and the signed value.
+ */
+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,
+};
+
+enum {
+	BTF_LOC_PARAM_FBREG		=	0xffffffff
+};
+
+/*
+ * 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 the runtime base address of the
+ *   ELF section associated with the LOCSEC
+ */
+
+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 314ecb0e593b..2ef28c6f14f9 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -346,6 +346,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)
@@ -518,11 +521,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)
@@ -769,7 +788,9 @@ static bool btf_type_is_resolve_source_only(const struct btf_type *t)
 {
 	return btf_type_is_var(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);
 }
 
 /* What types need to be resolved?
@@ -797,7 +818,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 */
@@ -4730,6 +4753,293 @@ 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 || size > 16) {
+		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, *func_proto_type;
+		const struct btf_type *loc_proto_type;
+		u32 func_type_id = loc->func;
+		u32 loc_proto_type_id = loc->loc_proto;
+		u32 proto_vlen;
+
+		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;
+		}
+		func_proto_type = btf_type_by_id(btf, func_type->type);
+		if (!func_proto_type || !btf_type_is_func_proto(func_proto_type)) {
+			btf_verifier_log_type(env, t,
+					      "Invalid func#%u", i + 1);
+			return -EINVAL;
+		}
+		proto_vlen = btf_vlen(func_proto_type);
+
+		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 (proto_vlen != btf_vlen(loc_proto_type)) {
+			btf_verifier_log_type(env, t,
+					      "Mismatched vlen for 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)
@@ -5399,6 +5709,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,
@@ -5473,7 +5786,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..11d65871d163 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -54,8 +54,8 @@ struct btf_type {
 	 *             decl_tag and type_tag
 	 */
 	__u32 info;
-	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
-	 * "size" tells the size of the type it is describing.
+	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC, 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, VAR, DECL_TAG and TYPE_TAG.
@@ -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,79 @@ 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.  Register numbers are
+ *   numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since
+ *   DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer
+ *   to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for
+ *   the frame base register to avoid collisions.
+ * - 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 > 1 specifies the register number
+ *   in the first 4-byte value and the offset in the remainder.
+ *   In the case of REG and OFFSET combinations, the OFFSET has the width
+ *   of the value words while the type size describes the represented parameter,
+ *   so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be
+ *   an 8-byte register dereference with signed 4-byte offset, since the vlen 2
+ *   values consist of a register value and the signed value.
+ */
+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,
+};
+
+enum {
+	BTF_LOC_PARAM_FBREG		=	0xffffffff
+};
+
+/*
+ * 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 the runtime base address of the
+ *   ELF section associated with the LOCSEC
+ */
+
+struct btf_loc {
+	__u32 func;
+	__u32 loc_proto;
+	__u32 offset;
+};
+
 #endif /* _UAPI__LINUX_BTF_H__ */
-- 
2.43.5


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

* [PATCH v4 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 12:12   ` bot+bpf-ci
  2026-09-24 11:14 ` [PATCH v4 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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             | 408 +++++++++++++++++++++++++++++++-
 tools/lib/bpf/btf.h             |  50 ++++
 tools/lib/bpf/btf_dump.c        |   9 +
 tools/lib/bpf/btf_iter.c        |  18 ++
 tools/lib/bpf/libbpf.c          |   3 +
 tools/lib/bpf/libbpf.map        |   6 +
 tools/lib/bpf/libbpf_internal.h |   2 +-
 7 files changed, 491 insertions(+), 5 deletions(-)

diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 908bd344229d..512b3962f75f 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,243 @@ 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().
+ *
+ * - *byte_sz* - size of the type, in bytes (1 through 16);
+ * - *flags* - combination of enum btf_loc_param_flags values
+ *
+ * Returns:
+ *   -  >0, type ID of newly added BTF type;
+ *   - <0, on error.
+ */
+int btf__add_loc_param(struct btf *btf, __u32 byte_sz, __u32 flags)
+{
+	struct btf_loc_param *p;
+	struct btf_type *t;
+	int sz, err;
+
+	if (!byte_sz || byte_sz > 16)
+		return libbpf_err(-EINVAL);
+
+	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 = byte_sz;
+
+	p = btf_loc_param(t);
+	p->flags = flags;
+
+	return btf_commit_type(btf, sz);
+}
+
+/*
+ * Append *value* to existing BTF_KIND_LOC_PARAM.
+ *
+ * Returns:
+ *   - 0 on success
+ *   - <0, on error.
+ */
+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));
+}
+
+/*
+ * Append a BTF_KIND_LOC_PARAM id/0 to BTF_KIND_LOC_PROTO.
+ *
+ * Returns:
+ *   - 0 on success;
+ *   - <0, on error.
+
+ */
+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;
+}
+
+/*
+ * Append new BTF_KIND_LOCSEC type with:
+ *   - *name* - non-empty/non-NULL name;
+ *
+ * Location section is initially empty. Location info can be added with
+ * btf__add_locsec_loc() calls, after btf__add_locsec() succeeds.
+ *
+ * Returns:
+ *   - >0, type ID of newly added BTF type;
+ *   - <0, on error.
+ */
+
+int btf__add_locsec(struct btf *btf, const char *name)
+{
+	struct btf_type *t;
+	int name_off = 0;
+	int err;
+
+	/* non-empty name */
+	if (str_is_empty(name))
+		return libbpf_err(-EINVAL);
+
+	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);
+
+	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 +4407,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 +4688,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 +4923,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 +4957,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 +5005,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 +5056,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 +5500,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 +5851,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 +6363,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 +6393,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 +6461,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.c b/tools/lib/bpf/libbpf.c
index 2f53afc985cf..930588950ab3 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2483,6 +2483,9 @@ static const char *__btf_kind_str(__u16 kind)
 	case BTF_KIND_DECL_TAG: return "decl_tag";
 	case BTF_KIND_TYPE_TAG: return "type_tag";
 	case BTF_KIND_ENUM64: return "enum64";
+	case BTF_KIND_LOC_PARAM: return "loc_param";
+	case BTF_KIND_LOC_PROTO: return "loc_proto";
+	case BTF_KIND_LOCSEC: return "locsec";
 	default: return "unknown";
 	}
 }
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 7a84dd00ce95..18d27d20102e 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -463,6 +463,12 @@ LIBBPF_1.8.0 {
 		bpf_program__attach_tracing_multi;
 		bpf_program__clear_flags;
 		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] 23+ messages in thread

* [PATCH v4 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC]
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests Alan Maguire
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, martin.lau,
	memxor, emil, bpf, nsc, puranjay, yatsenko, Alan Maguire

Add support to dump and validate new location-related kinds.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
---
 tools/testing/selftests/bpf/btf_helpers.c | 35 ++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/btf_helpers.c b/tools/testing/selftests/bpf/btf_helpers.c
index 1c1c2c26690a..85a4d15d1dd1 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,36 @@ 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);
+
+		fprintf(out, " size=%u flags=0x%x vlen=%u", t->size, p->flags, vlen);
+		for (i = 0; i < vlen; i++) {
+			if (p->flags & BTF_LOC_PARAM_SIGNED)
+				fprintf(out, "\n\tvalue=%d", (__s32)p->values[i]);
+			else
+				fprintf(out, "\n\tvalue=%u", p->values[i]);
+		}
+		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;
 	}
-- 
2.43.5


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

* [PATCH v4 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (2 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests Alan Maguire
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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>
Acked-by: Eduard Zingerman <eddyz87@gmail.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] 23+ messages in thread

* [PATCH v4 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (3 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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>
Acked-by: Eduard Zingerman <eddyz87@gmail.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] 23+ messages in thread

* [PATCH v4 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (4 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:56   ` bot+bpf-ci
  2026-09-24 11:14 ` [PATCH v4 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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    | 106 ++++++++++++++++++
 1 file changed, 106 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..f0900b47fd48 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
@@ -671,6 +671,110 @@ 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 */
+	btf__add_int(btf1, "unsigned int", 4, 0);	/* [6] unsigned int */
+
+	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",
+		"[6] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)");
+
+	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] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
+		"[7] 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 */
+	btf__add_int(btf5, "char", 1, 0);		/* [2] char */
+
+	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",
+		"[2] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=(none)",
+		/* remainder is split BTF */
+		"[3] LOCSEC 'inline.text' vlen=1\n"
+		"\tfunc_type_id=5 loc_proto_type_id=7 offset=256",
+		"[4] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+		"\t'p1' type_id=1",
+		"[5] FUNC 'foo' type_id=4 linkage=static",
+		"[6] LOC_PARAM '(anon)' size=4 flags=0x3 vlen=1\n"
+		"\tvalue=-1",
+		"[7] LOC_PROTO '(anon)' vlen=1\n"
+		"\ttype_id=6");
+
+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 +793,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] 23+ messages in thread

* [PATCH v4 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (5 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 08/11] bpftool: Document support for multi-split BTF Alan Maguire
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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 inline info.

Update help text accordingly to reflect the ability to specify multiple
in-order root-to-branch bases.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 tools/bpf/bpftool/btf.c  |  2 +-
 tools/bpf/bpftool/main.c | 24 +++++++++++++++++++++---
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index bca0a3982f09..65e8a29277e6 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..83884b21e708 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,20 @@ int main(int argc, char **argv)
 	if (json_output)
 		jsonw_destroy(&json_wtr);
 
-	btf__free(base_btf);
+	while (base_btf) {
+		bool is_root = base_btf == root_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 (is_root)
+			break;
+		base_btf = new_base_btf;
+	}
 
 	return ret;
 }
-- 
2.43.5


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

* [PATCH v4 bpf-next 08/11] bpftool: Document support for multi-split BTF
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (6 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:14 ` [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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] 23+ messages in thread

* [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (7 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 08/11] bpftool: Document support for multi-split BTF Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 15:16   ` Alexei Starovoitov
  2026-09-24 11:14 ` [PATCH v4 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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='*(reg10 + 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='*(reg10 + 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 in an architecture-neutral form;
'regN' where N is the DWARF register number, or 'fbreg'
for the stack frame base.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 tools/bpf/bpftool/btf.c | 224 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 224 insertions(+)

diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 65e8a29277e6..e29c8a84e224 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -29,6 +29,7 @@
 
 #define MAX_ROOT_IDS		16
 #define MAX_BTF_FILES		64
+#define MAX_LOC_PARAM_WORDS	8
 
 static const char * const btf_kind_str[NR_BTF_KINDS] = {
 	[BTF_KIND_UNKN]		= "UNKNOWN",
@@ -51,6 +52,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 +121,137 @@ 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)
+{
+	if (reg == BTF_LOC_PARAM_FBREG)
+		snprintf(str, sz, "fbreg");
+	else
+		snprintf(str, sz, "reg%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:
+			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 +550,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] 23+ messages in thread

* [PATCH v4 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (8 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:56   ` bot+bpf-ci
  2026-09-24 11:14 ` [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
  2026-09-24 16:10 ` [PATCH v4 bpf-next 00/11] Support inline functions in BTF patchwork-bot+netdevbpf
  11 siblings, 1 reply; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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         | 121 +++++++++++++++++-
 1 file changed, 120 insertions(+), 1 deletion(-)

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..bed506badc75 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,57 @@ 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_param(btf, "arg3", 1);
+	btf__add_func_param(btf, "arg4", 1);
+	btf__add_func_param(btf, "arg5", 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_param(btf, 8, BTF_LOC_PARAM_REG |
+			   BTF_LOC_PARAM_OFFSET | BTF_LOC_PARAM_SIGNED);
+	btf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);
+	btf__add_loc_param_value(btf, -0x10);
+	btf__add_loc_param(btf, 8, BTF_LOC_PARAM_ADDR | BTF_LOC_PARAM_CONST);
+	btf__add_loc_param_value(btf, 0x9abcdef0);
+	btf__add_loc_param_value(btf, 0x12345678);
+	btf__add_loc_param(btf, 8, BTF_LOC_PARAM_CONST);
+	btf__add_loc_param_value(btf, 0xfeedface);
+	btf__add_loc_param_value(btf, 0xdeadbeef);
+	btf__add_loc_param(btf, 8, BTF_LOC_PARAM_REG |
+			    BTF_LOC_PARAM_DEREF | BTF_LOC_PARAM_OFFSET |
+			    BTF_LOC_PARAM_SIGNED);
+	btf__add_loc_param_value(btf, BTF_LOC_PARAM_FBREG);
+	btf__add_loc_param_value(btf, -0x20);
+	btf__add_loc_proto(btf);
+	btf__add_loc_proto_param(btf, 4);
+	btf__add_loc_proto_param(btf, 5);
+	btf__add_loc_proto_param(btf, 6);
+	btf__add_loc_proto_param(btf, 7);
+	btf__add_loc_proto_param(btf, 8);
+	btf__add_loc_proto_param(btf, 9);
+	btf__add_locsec(btf, "inline.text");
+	btf__add_locsec_loc(btf, 3, 10, 64);
+
+	return btf;
+}
+
 static int btf_to_tmpfile(const struct btf *btf, char *path)
 {
 	ssize_t written;
@@ -105,6 +156,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,10 +226,46 @@ 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=5\n"
+		"\t'arg1' type_id=1\n"
+		"\t'arg2' type_id=1\n"
+		"\t'arg3' type_id=1\n"
+		"\t'arg4' type_id=1\n"
+		"\t'arg5' type_id=1\n"
+		"[3] FUNC 'foo' type_id=2 linkage=static\n"
+		"[4] LOC_PARAM '(anon)' size=4 flags=0x8 vlen=1 values='reg1'\n"
+		"[5] LOC_PARAM '(anon)' size=8 flags=0x38 vlen=2 values='*(reg2 + 0x10)'\n"
+		"[6] LOC_PARAM '(anon)' size=8 flags=0x29 vlen=2 values='fbreg - 0x10'\n"
+		"[7] LOC_PARAM '(anon)' size=8 flags=0x6 vlen=2 values='0x123456789abcdef0 (addr)'\n"
+		"[8] LOC_PARAM '(anon)' size=8 flags=0x2 vlen=2 values='0xdeadbeeffeedface'\n"
+		"[9] LOC_PARAM '(anon)' size=8 flags=0x39 vlen=2 values='*(fbreg - 0x20)'\n"
+		"[10] LOC_PROTO '(anon)' vlen=6\n"
+		"\ttype_id=4 value='reg1'\n"
+		"\ttype_id=5 value='*(reg2 + 0x10)'\n"
+		"\ttype_id=6 value='fbreg - 0x10'\n"
+		"\ttype_id=7 value='0x123456789abcdef0 (addr)'\n"
+		"\ttype_id=8 value='0xdeadbeeffeedface'\n"
+		"\ttype_id=9 value='*(fbreg - 0x20)'\n"
+		"[11] LOCSEC 'inline.text' vlen=1\n"
+		"\tname='foo' func_type_id=3 loc_proto_type_id=10 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];
-	struct btf *btf;
+	struct btf *btf = NULL;
 
 	btf = mk_btf();
 	if (!btf)
@@ -171,7 +278,19 @@ void test_bpftool_btf_dump(void)
 		test_dump(path, true);
 	if (test__start_subtest("c_unsorted"))
 		test_dump(path, false);
+	unlink(path);
+
+	btf__free(btf);
+
+	btf = mk_loc_btf();
+	if (!btf)
+		return;
+
+	if (btf_to_tmpfile(btf, path))
+		goto out_btf;
 
+	if (test__start_subtest("loc_dump"))
+		test_loc_dump(path);
 	unlink(path);
 out_btf:
 	btf__free(btf);
-- 
2.43.5


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

* [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (9 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
@ 2026-09-24 11:14 ` Alan Maguire
  2026-09-24 11:56   ` bot+bpf-ci
  2026-09-24 12:19   ` sashiko-bot
  2026-09-24 16:10 ` [PATCH v4 bpf-next 00/11] Support inline functions in BTF patchwork-bot+netdevbpf
  11 siblings, 2 replies; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 11:14 UTC (permalink / raw)
  To: ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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 | 83 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 81 insertions(+), 2 deletions(-)

diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
index 004aa1058d85..29de1222c3e7 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,80 @@ 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 through 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 offset
+    relative to the runtime base address of the kernel or module image.
+  * ``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. A constant's
+    signed width is ``size``. For a register-relative offset, its signed
+    width is the number of offset value words times 32 bits.
+
+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 runtime base address of the ELF section associated with the LOCSEC.
+For example, a LOCSEC named ``inline.text`` contains records for ``.text``
+whose offsets are relative to the runtime base of that section.
+
 2.3 Constant Values
 -------------------
 
-- 
2.43.5


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

* Re: [PATCH v4 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF
  2026-09-24 11:14 ` [PATCH v4 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
@ 2026-09-24 11:56   ` bot+bpf-ci
  0 siblings, 0 replies; 23+ messages in thread
From: bot+bpf-ci @ 2026-09-24 11:56 UTC (permalink / raw)
  To: alan.maguire, ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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: 2501 bytes --]

> diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
> index fb67ae195a73..f0900b47fd48 100644
> --- a/tools/testing/selftests/bpf/prog_tests/btf_distill.c
> +++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c

[ ... ]

> +/* 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 */
> +	btf__add_int(btf1, "unsigned int", 4, 0);	/* [6] unsigned int */

[ ... ]

> +	btf2 = btf__new_empty_split(btf1);
> +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
> +		goto cleanup;
> +
> +	btf__add_locsec(btf2, "inline.text");		/* [6] locsec */
                                                            ^^^

Should this comment be [7] instead of [6]? The btf1 base already contains
types [1]..[6], so the first type added to split BTF btf2 gets ID 7. The
VALIDATE_RAW_BTF block below confirms this with "[7] LOCSEC 'inline.text'".

> +	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] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
> +		"[7] LOCSEC 'inline.text' vlen=1\n"
> +		"\tfunc_type_id=3 loc_proto_type_id=5 offset=256");

[ ... ]


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

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

* Re: [PATCH v4 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info
  2026-09-24 11:14 ` [PATCH v4 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
@ 2026-09-24 11:56   ` bot+bpf-ci
  0 siblings, 0 replies; 23+ messages in thread
From: bot+bpf-ci @ 2026-09-24 11:56 UTC (permalink / raw)
  To: alan.maguire, ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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: 1444 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 d5b25302b0c8..bed506badc75 100644
> --- a/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c
> +++ b/tools/testing/selftests/bpf/prog_tests/bpftool_btf_dump.c

[ ... ]

> @@ -155,10 +226,46 @@ static void test_dump(const char *btf_path, bool sorted)
>  	free(dump);
>  }
>
> +static void test_loc_dump(const char *btf_path)
> +{

[ ... ]

>  void test_bpftool_btf_dump(void)
>  {
>  	char path[PATH_MAX];
> -	struct btf *btf;
> +	struct btf *btf = NULL;
>
>  	btf = mk_btf();
>  	if (!btf)

This isn't a bug, but is the '= NULL' initializer on btf needed?
It looks like btf is always assigned by mk_btf() before it is used.

> @@ -171,7 +278,19 @@ void test_bpftool_btf_dump(void)
>  		test_dump(path, true);
>  	if (test__start_subtest("c_unsorted"))
>  		test_dump(path, false);
> +	unlink(path);
> +
> +	btf__free(btf);
> +
> +	btf = mk_loc_btf();
> +	if (!btf)
> +		return;
> +
> +	if (btf_to_tmpfile(btf, path))
> +		goto out_btf;
>
> +	if (test__start_subtest("loc_dump"))
> +		test_loc_dump(path);
>  	unlink(path);
>  out_btf:
>  	btf__free(btf);


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

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

* Re: [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds
  2026-09-24 11:14 ` [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
@ 2026-09-24 11:56   ` bot+bpf-ci
  2026-09-24 12:19   ` sashiko-bot
  1 sibling, 0 replies; 23+ messages in thread
From: bot+bpf-ci @ 2026-09-24 11:56 UTC (permalink / raw)
  To: alan.maguire, ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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: 1655 bytes --]

> diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst
> index 004aa1058d85..29de1222c3e7 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 {

This isn't a bug, but should this list include BTF_KIND_VAR as well?

Looking at the UAPI header (include/uapi/linux/btf.h line 59-61), it
states: "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, FUNC,
FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG. The BTF_KIND_VAR documentation
at line 422 confirms it uses the type field.

Since this patch updates this comment to note which types use or don't
use the type field, would it make sense to include VAR in the list for
completeness?

[ ... ]


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

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

* Re: [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info
  2026-09-24 11:14 ` [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
@ 2026-09-24 12:12   ` bot+bpf-ci
  2026-09-24 15:18   ` Alexei Starovoitov
  1 sibling, 0 replies; 23+ messages in thread
From: bot+bpf-ci @ 2026-09-24 12:12 UTC (permalink / raw)
  To: alan.maguire, ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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: 8479 bytes --]

> 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

[ ... ]

> diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
> index 618167cab4e6..11d65871d163 100644
> --- a/include/uapi/linux/btf.h
> +++ b/include/uapi/linux/btf.h

[ ... ]

> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 4a1fa4fbdf4e..5a0179cc1676 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c

[ ... ]

> +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;
> +}

Does btf_locsec_check_meta() ever log the LOCSEC type itself?

Looking at btf_datasec_check_meta() which this code was modeled after,
it calls btf_verifier_log_type(env, t, NULL) just before the
for_each_vsi() loop to print the type header.

kernel/bpf/btf.c:btf_datasec_check_meta() {
    ...
    btf_verifier_log_type(env, t, NULL);

    for_each_vsi(i, t, vsi) {
        ...
        btf_verifier_log_vsi(env, t, vsi, NULL);
    }
}

The new btf_verifier_log_loc() helper is a copy of btf_verifier_log_vsi()
and only prints the type header when env->phase != CHECK_META:

kernel/bpf/btf.c:btf_verifier_log_loc() {
    ...
    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", ...);
}

So it relies on the caller to print "[id] LOCSEC <name> vlen=N" first.

Since btf_locsec_check_meta() runs during CHECK_META phase but never
calls btf_verifier_log_type(env, t, NULL) before for_each_loc(), the
verifier log would show bare "\t func=0 loc_proto=1 offset=0" lines
with no LOCSEC header and no [type_id]. This means the loaded BTF log
would be missing the LOCSEC type entry entirely, and any "Invalid func"
or "Invalid loc_proto" errors could not be tied to a type ID or section.

Should btf_verifier_log_type(env, t, NULL) be added before for_each_loc()
to match the datasec pattern?

[ ... ]

> diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
> index 618167cab4e6..11d65871d163 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,
>  };

Does raising BTF_KIND_MAX cause issues with bpftool in this commit?

tools/bpf/bpftool/btf.c declares btf_kind_str[NR_BTF_KINDS] but at this
commit only fills entries up to BTF_KIND_ENUM64. Its guard is:

tools/bpf/bpftool/btf.c:btf_kind_safe() {
    return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
}

Before this patch, kinds 20-22 were above BTF_KIND_MAX and printed as
"UNKNOWN". After it, btf_kind_safe() returns 20..22 unchanged, and
btf_kind_str[20..22] are NULL.

The in-tree libbpf can parse these kinds when the BTF has a kind layout
section, so `bpftool btf dump file <btf>` on such a blob reaches:

tools/bpf/bpftool/btf.c:dump_btf_type() {
    ...
    if (json_output) {
        jsonw_string_field(w, "kind", btf_kind_str[btf_kind_safe(kind)]);
    } else {
        printf("[%u] %s '%s'", id, btf_kind_str[btf_kind_safe(kind)], ...);
    }
}

With -j, jsonw_string_field() passes NULL to jsonw_string(), which
runs `for (; *str; ++str)` on NULL and segfaults. The plain output
passes NULL to %s, which is undefined behaviour.

This is fixed later in the series by commit 68746bde8eed
("bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC")
which adds the three strings to btf_kind_str[]. Would it be better to
add the btf_kind_str[] entries in the same commit as the header sync,
so the series stays bisectable?

[ ... ]

> +/*
> + * 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.  Register numbers are
> + *   numbers derived from DW_OP_reg values, i.e. 0 is DW_OP_reg0; since
> + *   DW_OP_fbreg has its own special DW_OP_value and DW_OP_regx can refer
> + *   to an arbitrary register number, we reserve BTF_LOC_PARAM_FBREG for
> + *   the frame base register to avoid collisions.
> + * - 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 > 1 specifies the register number
> + *   in the first 4-byte value and the offset in the remainder.
> + *   In the case of REG and OFFSET combinations, the OFFSET has the width
> + *   of the value words while the type size describes the represented parameter,
> + *   so for example a REG | DEREF | OFFSET with size 8 and vlen 2 would be
> + *   an 8-byte register dereference with signed 4-byte offset, since the vlen 2
> + *   values consist of a register value and the signed value.
                                                     ^^^^^^
> + */
> +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,
> +};

Does the example comment accurately describe when offsets are signed?

The comment says a REG | DEREF | OFFSET parameter (no
BTF_LOC_PARAM_SIGNED) has "a signed 4-byte offset". The only place it
mentions _SIGNED is for constants: "a BTF_LOC_PARAM_CONST is a constant
value; combination of size, vlen and _SIGNED flag determines it".

That contradicts how the rest of the series defines the encoding.
Documentation/bpf/btf.rst (at 64003e032e13) says "BTF_LOC_PARAM_SIGNED
makes a constant or offset signed ... For a register-relative offset,
its signed width is the number of offset value words times 32 bits".

bpftool's btf_loc_param_str() (at 68746bde8eed) applies sign handling
only `if (p->flags & BTF_LOC_PARAM_SIGNED)`.

The selftests expect flags=0x38 (REG|DEREF|OFFSET) to print
'*(reg2 + 0x10)' and use flags=0x39 (with SIGNED) for '*(fbreg - 0x20)'.

A producer written from this header would leave out BTF_LOC_PARAM_SIGNED
for negative frame-base offsets, which are the common case. Consumers
built with the series would then read -0x20 as +0xffffffe0 and compute
the wrong parameter address.

Should the example say the offset is signed only when
BTF_LOC_PARAM_SIGNED is set, and should the _SIGNED description cover
offsets as well as constants?


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

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

* Re: [PATCH v4 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
  2026-09-24 11:14 ` [PATCH v4 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
@ 2026-09-24 12:12   ` bot+bpf-ci
  0 siblings, 0 replies; 23+ messages in thread
From: bot+bpf-ci @ 2026-09-24 12:12 UTC (permalink / raw)
  To: alan.maguire, ast, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, 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: 6264 bytes --]

> libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
>
> 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.

This isn't a bug, but could the changelog explain the adding_to_base
change? The same hunk changes how btf_add_distilled_types() decides
whether it is filling the distilled base:

Old test: dist->pipe.dst->start_id == 1
New test: dist->pipe.dst->base_btf == NULL

btf__distill_base() calls btf_add_distilled_types() twice. The first pass
writes into btf__new_empty(). The second writes into
btf__new_empty_split(new_base), whose start_id is btf__type_cnt(new_base).

The old test breaks when the split BTF references only base types that go to
split BTF. Examples are a base void * PTR, or a LOC_PROTO whose params are
all LOC_PARAMs. The distilled base then holds only void, so new_split->
start_id is 1. The second pass then looks like a base pass and skips
ARRAY/PTR/FUNC_PROTO/etc. with the adding_to_base continue. Those types are
never added to the new split BTF, and btf_update_distilled_type_ids() leaves
references to them pointing at ids from the original base.

The new kinds make this easier to hit, because a LOC_PROTO -> LOC_PARAM
chain pulls in no INT or named types. The changelog also does not mention
that base BTF_KIND_FUNC is now accepted as a distilled reference.

Would it be better as a separate fix with a Fixes: tag against the commit
that added btf__distill_base()?

[ ... ]

> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c

[ ... ]

> @@ -3344,6 +3400,243 @@ 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().
> + *
> + * - *byte_sz* - size of the type, in bytes (1 through 16);
> + * - *flags* - combination of enum btf_loc_param_flags values
> + *
> + * Returns:
> + *   -  >0, type ID of newly added BTF type;
> + *   - <0, on error.
> + */

[ ... ]

> +/*
> + * Append a BTF_KIND_LOC_PARAM id/0 to BTF_KIND_LOC_PROTO.
> + *
> + * Returns:
> + *   - 0 on success;
> + *   - <0, on error.
> +
> + */

[ ... ]

> +/*
> + * Append new BTF_KIND_LOCSEC type with:
> + *   - *name* - non-empty/non-NULL name;
> + *
> + * Location section is initially empty. Location info can be added with
> + * btf__add_locsec_loc() calls, after btf__add_locsec() succeeds.
> + *
> + * Returns:
> + *   - >0, type ID of newly added BTF type;
> + *   - <0, on error.
> + */
> +
> +int btf__add_locsec(struct btf *btf, const char *name)

[ ... ]

> +int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto,
> +			__u32 offset)

This isn't a bug, but could btf__add_locsec_loc() get a short doc comment
like btf__add_datasec_var_info() has? Its parameters (func, loc_proto and
offset) and the return convention (0/<0) are not documented where the
sibling functions document theirs.

Also, the comment block for btf__add_loc_proto_param() has an empty line
with no leading ' *' before its closing ' */'. The btf__add_locsec()
comment is separated from the function by a blank line, which no other
btf__add_*() comment in the file does. Could those stray blank lines be
dropped?

[ ... ]

> @@ -5997,7 +6393,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 +6461,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;

This isn't a bug, but can a base LOCSEC ever get a non-zero id_map entry
here? Looking at btf_add_distilled_type_ids():

tools/lib/bpf/btf.c:btf_add_distilled_type_ids() {
	switch (btf_kind(t)) {
	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:
		pr_debug("unexpected reference to base type %u kind %u\n", *id,
			 btf_kind(t));
		return -EINVAL;
	}
}

BTF_KIND_LOCSEC is not listed there. For a LOCSEC it falls to the default
branch, which prints "unexpected reference to base type" and returns
-EINVAL.

btf_add_distilled_types() only visits base ids with a non-zero map entry
(checking !dist->id_map[i] early). No BTF type refers to a LOCSEC by id
anyway, just as nothing refers to a DATASEC.

So the new BTF_KIND_LOCSEC label in btf_add_distilled_types() can never
run. The other kinds this patch adds (FUNC, LOC_PARAM, LOC_PROTO) were
added to both switches together, and the comparable DATASEC/VAR kinds
appear in neither. Could the BTF_KIND_LOCSEC label be dropped so the two
distill switches list the same kinds?


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

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

* Re: [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds
  2026-09-24 11:14 ` [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
  2026-09-24 11:56   ` bot+bpf-ci
@ 2026-09-24 12:19   ` sashiko-bot
  1 sibling, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-09-24 12:19 UTC (permalink / raw)
  To: Alan Maguire; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] bpf: LOCSEC info.vlen 16-bit limit overflows for large ELF sections
- [Low] bpf: contradiction between LOCSEC name_off rule and example
- [Low] bpf: omitted BTF_LOC_PARAM_FBREG and register/offset layout from LOC_PARAM spec
- [Low] bpf: omitted LOC_PROTO vlen strict matching requirement
--

commit 2cc26415b9bc105cbaaa995ced64eca8f5ea9509
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..29de1222c3e73 100644
> --- a/Documentation/bpf/btf.rst
> +++ b/Documentation/bpf/btf.rst

[ ... ]

> @@ -563,6 +568,80 @@ The ``btf_enum64`` encoding:

[ ... ]

> +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 through 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 offset
> +    relative to the runtime base address of the kernel or module image.
> +  * ``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

[Severity: Low]
Does this documentation need to clarify the layout of the values array for
this flag combination? It isn't clear that the register number occupies the
first 4-byte value while the offset occupies the remainder.

Also, does this section need to document the BTF_LOC_PARAM_FBREG (0xffffffff)
reserved value defined in the UAPI header? Implementers will be unable to
correctly decode or encode these values without these details.

> +    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. A constant's
> +    signed width is ``size``. For a register-relative offset, its signed
> +    width is the number of offset value words times 32 bits.

[ ... ]

> +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

[Severity: Medium]
Will this 16-bit info.vlen field overflow for large ELF sections?

The info.vlen field is structurally 16 bits, allowing a maximum of 65,535
inline sites per LOCSEC record. Large sections like the vmlinux .text section
contain significantly more inline sites, which will cause an overflow during
BTF generation.

Should the unused 32-bit size field be used for the site count instead, or
should the specification document how to split a large section across multiple
LOCSEC records?

> +  * ``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

[Severity: Low]
Should this document the strict structural length matching required by the
kernel verifier?

The kernel BTF verifier in btf_locsec_resolve() strictly requires that the
vlen of a BTF_KIND_LOC_PROTO matches the vlen of its associated
BTF_KIND_FUNC_PROTO. Missing this rule could lead tooling to generate
valid-looking BTF that the kernel will subsequently reject with -EINVAL.

> +the runtime base address of the ELF section associated with the LOCSEC.
> +For example, a LOCSEC named ``inline.text`` contains records for ``.text``
> +whose offsets are relative to the runtime base of that section.

[Severity: Low]
Is this example contradicting the name_off requirement?

The documentation above requires name_off to point to a valid ELF section
name, but this example uses "inline.text", creating ambiguity for
implementers parsing the BTF to map LOCSEC records to actual ELF sections.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260924111428.75957-1-alan.maguire@oracle.com?part=11

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

* Re: [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
  2026-09-24 11:14 ` [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
@ 2026-09-24 15:16   ` Alexei Starovoitov
  2026-09-24 15:33     ` Alan Maguire
  0 siblings, 1 reply; 23+ messages in thread
From: Alexei Starovoitov @ 2026-09-24 15:16 UTC (permalink / raw)
  To: Alan Maguire, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, martin.lau,
	memxor, emil, bpf, nsc, puranjay, yatsenko

On Thu, Sep 24, 2026 at 12:14 PM Alan Maguire <alan.maguire@oracle.com> wrote:
> +				printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u",
> +				       name, locs->func, locs->loc_proto, locs->offset);

My earlier feedback must have been lost:
Pls print argument names and where they are held here as well.
FUNC_PROTO has the names. LOC_PROTO has the locations.
Something like:
  foo(arg1 reg1, arg2 *(reg2 + 0x10)) offset=64

Or is it planned for follow up?


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

* Re: [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info
  2026-09-24 11:14 ` [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
  2026-09-24 12:12   ` bot+bpf-ci
@ 2026-09-24 15:18   ` Alexei Starovoitov
  1 sibling, 0 replies; 23+ messages in thread
From: Alexei Starovoitov @ 2026-09-24 15:18 UTC (permalink / raw)
  To: Alan Maguire, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, martin.lau,
	memxor, emil, bpf, nsc, puranjay, yatsenko

On Thu, Sep 24, 2026 at 12:14 PM Alan Maguire <alan.maguire@oracle.com> wrote:
> +	size = t->size;
> +	if (!size || size > 16) {
> +		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;
> +	}

flags and vlen are not checked.
flags == 0xffffffff with vlen == 0 is accepted.
So are CONST | REG and DEREF without REG.

Probably should reject everything that the uapi doesn't define,
so that the remaining 26 bits can be used later?


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

* Re: [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
  2026-09-24 15:16   ` Alexei Starovoitov
@ 2026-09-24 15:33     ` Alan Maguire
  2026-09-24 16:02       ` Alexei Starovoitov
  0 siblings, 1 reply; 23+ messages in thread
From: Alan Maguire @ 2026-09-24 15:33 UTC (permalink / raw)
  To: Alexei Starovoitov, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, martin.lau,
	memxor, emil, bpf, nsc, puranjay, yatsenko

On 24/09/2026 16:16, Alexei Starovoitov wrote:
> On Thu, Sep 24, 2026 at 12:14 PM Alan Maguire <alan.maguire@oracle.com> wrote:
>> +				printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u",
>> +				       name, locs->func, locs->loc_proto, locs->offset);
> 
> My earlier feedback must have been lost:
> Pls print argument names and where they are held here as well.
> FUNC_PROTO has the names. LOC_PROTO has the locations.
> Something like:
>   foo(arg1 reg1, arg2 *(reg2 + 0x10)) offset=64
> 
> Or is it planned for follow up?
> 

Ah, I missed that, sorry. I added display of LOC_PARAMs for LOC_PROTO; didn't add
the signature for each LOCSEC entry.

I can respin with this and the vlen/flag checking for patch 1, along with anything else 
the bots dig up.

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

* Re: [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
  2026-09-24 15:33     ` Alan Maguire
@ 2026-09-24 16:02       ` Alexei Starovoitov
  0 siblings, 0 replies; 23+ messages in thread
From: Alexei Starovoitov @ 2026-09-24 16:02 UTC (permalink / raw)
  To: Alan Maguire, andrii, eddyz87, qmo
  Cc: jolsa, daniel, ihor.solodrai, yonghong.song, song, martin.lau,
	memxor, emil, bpf, nsc, puranjay, yatsenko

On Thu Sep 24, 2026 at 3:33 PM UTC, Alan Maguire wrote:
> On 24/09/2026 16:16, Alexei Starovoitov wrote:
>> On Thu, Sep 24, 2026 at 12:14 PM Alan Maguire <alan.maguire@oracle.com> wrote:
>>> +				printf("\n\tname='%s' func_type_id=%u loc_proto_type_id=%u offset=%u",
>>> +				       name, locs->func, locs->loc_proto, locs->offset);
>> 
>> My earlier feedback must have been lost:
>> Pls print argument names and where they are held here as well.
>> FUNC_PROTO has the names. LOC_PROTO has the locations.
>> Something like:
>>   foo(arg1 reg1, arg2 *(reg2 + 0x10)) offset=64
>> 
>> Or is it planned for follow up?
>> 
>
> Ah, I missed that, sorry. I added display of LOC_PARAMs for LOC_PROTO; didn't add
> the signature for each LOCSEC entry.
>
> I can respin with this and the vlen/flag checking for patch 1, along with anything else 
> the bots dig up.

Already applied. Just send a follow up. We need to move fast.

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

* Re: [PATCH v4 bpf-next 00/11] Support inline functions in BTF
  2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
                   ` (10 preceding siblings ...)
  2026-09-24 11:14 ` [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
@ 2026-09-24 16:10 ` patchwork-bot+netdevbpf
  11 siblings, 0 replies; 23+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-24 16:10 UTC (permalink / raw)
  To: Alan Maguire
  Cc: ast, andrii, eddyz87, qmo, jolsa, daniel, ihor.solodrai,
	yonghong.song, song, martin.lau, memxor, emil, bpf, nsc, puranjay,
	yatsenko

Hello:

This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Thu, 24 Sep 2026 12:14:17 +0100 you wrote:
> This series adds support to facilitate tracing of inline function
> sites using BPF Type Format (BTF) information. An excellent overview
> of the problem and proposed solution presented at LSF/MM/BPF is
> available at [1].
> 
> The aim is to produce a compact representation providing sufficient
> information to a tracer wishing to instrument an inline site via a
> kprobe.  The challenge to solve is compact representation - my
> local bpf-next builds show nearly 600,000 inline sites for approximately
> 100,000 functions.  Any BTF representation should utilize deduplication
> where possible to minimize overheads.
> 
> [...]

Here is the summary with links:
  - [v4,bpf-next,01/11] btf: Extend UAPI to support BTF location (inline site) info
    https://git.kernel.org/bpf/bpf-next/c/33c5a3278bdb
  - [v4,bpf-next,02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC]
    https://git.kernel.org/bpf/bpf-next/c/dad2fcc644dd
  - [v4,bpf-next,03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC]
    https://git.kernel.org/bpf/bpf-next/c/0f1de9408e59
  - [v4,bpf-next,04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests
    https://git.kernel.org/bpf/bpf-next/c/663fa771389d
  - [v4,bpf-next,05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests
    https://git.kernel.org/bpf/bpf-next/c/dfb463b07117
  - [v4,bpf-next,06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF
    https://git.kernel.org/bpf/bpf-next/c/49a4c23a2234
  - [v4,bpf-next,07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs
    https://git.kernel.org/bpf/bpf-next/c/7890a57356a2
  - [v4,bpf-next,08/11] bpftool: Document support for multi-split BTF
    https://git.kernel.org/bpf/bpf-next/c/0ec4caf73ce5
  - [v4,bpf-next,09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC
    https://git.kernel.org/bpf/bpf-next/c/321562c34d5b
  - [v4,bpf-next,10/11] selftests/bpf: Test bpftool dump of BTF location info
    https://git.kernel.org/bpf/bpf-next/c/3429e01578b6
  - [v4,bpf-next,11/11] Documentation/bpf: Describe new location-related BTF kinds
    https://git.kernel.org/bpf/bpf-next/c/ce2913d959a2

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

end of thread, other threads:[~2026-09-24 16:11 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-24 11:14 [PATCH v4 bpf-next 00/11] Support inline functions in BTF Alan Maguire
2026-09-24 11:14 ` [PATCH v4 bpf-next 01/11] btf: Extend UAPI to support BTF location (inline site) info Alan Maguire
2026-09-24 12:12   ` bot+bpf-ci
2026-09-24 15:18   ` Alexei Starovoitov
2026-09-24 11:14 ` [PATCH v4 bpf-next 02/11] libbpf: Add support for BTF kinds LOC[_PARAM|_PROTO|SEC] Alan Maguire
2026-09-24 12:12   ` bot+bpf-ci
2026-09-24 11:14 ` [PATCH v4 bpf-next 03/11] selftests/bpf: Test helper support for BTF_KIND_LOC[_PARAM|_PROTO|SEC] Alan Maguire
2026-09-24 11:14 ` [PATCH v4 bpf-next 04/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to field iter tests Alan Maguire
2026-09-24 11:14 ` [PATCH v4 bpf-next 05/11] selftests/bpf: Add LOC_PARAM, LOC_PROTO, LOCSEC to dedup split tests Alan Maguire
2026-09-24 11:14 ` [PATCH v4 bpf-next 06/11] selftests/bpf: BTF distill tests to ensure LOC[_PARAM|_PROTO] add to split BTF Alan Maguire
2026-09-24 11:56   ` bot+bpf-ci
2026-09-24 11:14 ` [PATCH v4 bpf-next 07/11] bpftool: Handle multi-split BTF by supporting multiple base BTFs Alan Maguire
2026-09-24 11:14 ` [PATCH v4 bpf-next 08/11] bpftool: Document support for multi-split BTF Alan Maguire
2026-09-24 11:14 ` [PATCH v4 bpf-next 09/11] bpftool: Add ability to dump LOC_PARAM, LOC_PROTO and LOCSEC Alan Maguire
2026-09-24 15:16   ` Alexei Starovoitov
2026-09-24 15:33     ` Alan Maguire
2026-09-24 16:02       ` Alexei Starovoitov
2026-09-24 11:14 ` [PATCH v4 bpf-next 10/11] selftests/bpf: Test bpftool dump of BTF location info Alan Maguire
2026-09-24 11:56   ` bot+bpf-ci
2026-09-24 11:14 ` [PATCH v4 bpf-next 11/11] Documentation/bpf: Describe new location-related BTF kinds Alan Maguire
2026-09-24 11:56   ` bot+bpf-ci
2026-09-24 12:19   ` sashiko-bot
2026-09-24 16:10 ` [PATCH v4 bpf-next 00/11] Support inline functions in BTF patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox