[PATCH RFC bpf-next 1/7] bpf: Add bpf_btf_get_struct_member_info() helper

From: Siddharth Nayyar

Date: Fri Nov 06 2026 - 15:20:55 EST


Add a new BPF helper bpf_btf_get_struct_member_info() for
BPF_PROG_TYPE_SYSCALL programs to query struct member offset and index
(or the struct size when the member name is NULL) from vmlinux or
module BTF.

Given a struct's BTF type ID and an optional BTF object FD (0 for
vmlinux BTF), the helper returns a 64-bit value with the member's byte
offset in the lower 32 bits and the member's index in the upper 32 bits
when a member name is provided, or the byte size of the struct when the
member name is NULL.

This helper will be used by BPF light skeletons (gen_loader) to
initialize struct_ops maps and programs against kernel and module BTF
at load time.

Signed-off-by: Siddharth Nayyar <sidnayyar@xxxxxxxxxx>
---
include/linux/bpf.h | 1 +
include/uapi/linux/bpf.h | 13 ++++++++
kernel/bpf/btf.c | 73 ++++++++++++++++++++++++++++++++++++++++++
kernel/bpf/syscall.c | 2 ++
tools/include/uapi/linux/bpf.h | 13 ++++++++
5 files changed, 102 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index b011acc22c50..ab4d995b529c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -4206,6 +4206,7 @@ extern const struct bpf_func_proto bpf_task_storage_delete_recur_proto;
extern const struct bpf_func_proto bpf_task_storage_delete_proto;
extern const struct bpf_func_proto bpf_for_each_map_elem_proto;
extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto;
+extern const struct bpf_func_proto bpf_btf_get_struct_member_info_proto;
extern const struct bpf_func_proto bpf_sk_setsockopt_proto;
extern const struct bpf_func_proto bpf_sk_getsockopt_proto;
extern const struct bpf_func_proto bpf_sk_setsockopt_nodelay_proto;
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 62de0919db72..3a630411b920 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -6065,6 +6065,18 @@ union bpf_attr {
* 0 on success.
*
* **-ENOENT** if the bpf_local_storage cannot be found.
+ *
+ * long bpf_btf_get_struct_member_info(u32 btf_id, int btf_obj_fd, const char *name, int name_sz)
+ * Description
+ * Find a struct member with given *name* in the struct type identified
+ * by *btf_id* in the BTF object referenced by *btf_obj_fd* (or in
+ * vmlinux BTF if *btf_obj_fd* is 0). If *name* is **NULL** (and
+ * *name_sz* is 0), return the size of the struct instead.
+ * Return
+ * Returns the member's byte offset in the lower 32 bits and the
+ * member's index in the upper 32 bits when *name* is non-**NULL**,
+ * or the byte size of the struct when *name* is **NULL**. On error, a
+ * negative error code is returned.
*/
#define ___BPF_FUNC_MAPPER(FN, ctx...) \
FN(unspec, 0, ##ctx) \
@@ -6279,6 +6291,7 @@ union bpf_attr {
FN(user_ringbuf_drain, 209, ##ctx) \
FN(cgrp_storage_get, 210, ##ctx) \
FN(cgrp_storage_delete, 211, ##ctx) \
+ FN(btf_get_struct_member_info, 212, ##ctx) \
/* This helper list is effectively frozen. If you are trying to \
* add a new helper, you should add a kfunc instead which has \
* less stability guarantees. See Documentation/bpf/kfuncs.rst \
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 228b93d4f8c9..8d9188dda34e 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -9682,6 +9682,79 @@ const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.arg4_type = ARG_ANYTHING,
};

+BPF_CALL_4(bpf_btf_get_struct_member_info, u32, btf_id, int, btf_obj_fd,
+ const char *, name, int, name_sz)
+{
+ const struct btf_member *member;
+ const struct btf_type *t;
+ struct btf *btf;
+ long ret = -EINVAL;
+ u32 i;
+
+ if (!name) {
+ if (name_sz)
+ return -EINVAL;
+ } else if (name_sz <= 1 || !*name || name[name_sz - 1]) {
+ return -EINVAL;
+ }
+
+ if (btf_obj_fd) {
+ btf = btf_get_by_fd(btf_obj_fd);
+ if (IS_ERR(btf))
+ return PTR_ERR(btf);
+ } else {
+ btf = bpf_get_btf_vmlinux();
+ if (IS_ERR(btf))
+ return PTR_ERR(btf);
+ if (!btf)
+ return -EINVAL;
+ btf_get(btf);
+ }
+
+ t = btf_type_by_id(btf, btf_id);
+ if (!t || !__btf_type_is_struct(t))
+ goto out;
+
+ if (!name) {
+ ret = t->size;
+ goto out;
+ }
+
+ ret = -ENOENT;
+ for_each_member(i, t, member) {
+ const char *mname = btf_name_by_offset(btf, member->name_off);
+
+ if (!mname || !*mname)
+ continue;
+ if (!strcmp(mname, name)) {
+ u32 moff;
+
+ if (__btf_member_bitfield_size(t, member)) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+ moff = __btf_member_bit_offset(t, member) / 8;
+ ret = moff | (((u64)i) << 32);
+ goto out;
+ }
+ }
+
+out:
+ btf_put(btf);
+ return ret;
+}
+
+const struct bpf_func_proto bpf_btf_get_struct_member_info_proto = {
+ .func = bpf_btf_get_struct_member_info,
+ .gpl_only = false,
+ .might_sleep = true,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_ANYTHING,
+ .arg2_type = ARG_ANYTHING,
+ .arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY | PTR_MAYBE_NULL,
+ .arg4_type = ARG_MEM_SIZE_OR_ZERO,
+};
+
BTF_ID_LIST_GLOBAL(btf_tracing_ids, MAX_BTF_TRACING_TYPE)
#define BTF_TRACING_TYPE(name, type) BTF_ID(struct, type)
BTF_TRACING_TYPE_xxx
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 9004f2673ccf..6e7b6b10603e 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6756,6 +6756,8 @@ syscall_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
? NULL : &bpf_sys_bpf_proto;
case BPF_FUNC_btf_find_by_name_kind:
return &bpf_btf_find_by_name_kind_proto;
+ case BPF_FUNC_btf_get_struct_member_info:
+ return &bpf_btf_get_struct_member_info_proto;
case BPF_FUNC_sys_close:
return &bpf_sys_close_proto;
case BPF_FUNC_kallsyms_lookup_name:
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 62de0919db72..3a630411b920 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -6065,6 +6065,18 @@ union bpf_attr {
* 0 on success.
*
* **-ENOENT** if the bpf_local_storage cannot be found.
+ *
+ * long bpf_btf_get_struct_member_info(u32 btf_id, int btf_obj_fd, const char *name, int name_sz)
+ * Description
+ * Find a struct member with given *name* in the struct type identified
+ * by *btf_id* in the BTF object referenced by *btf_obj_fd* (or in
+ * vmlinux BTF if *btf_obj_fd* is 0). If *name* is **NULL** (and
+ * *name_sz* is 0), return the size of the struct instead.
+ * Return
+ * Returns the member's byte offset in the lower 32 bits and the
+ * member's index in the upper 32 bits when *name* is non-**NULL**,
+ * or the byte size of the struct when *name* is **NULL**. On error, a
+ * negative error code is returned.
*/
#define ___BPF_FUNC_MAPPER(FN, ctx...) \
FN(unspec, 0, ##ctx) \
@@ -6279,6 +6291,7 @@ union bpf_attr {
FN(user_ringbuf_drain, 209, ##ctx) \
FN(cgrp_storage_get, 210, ##ctx) \
FN(cgrp_storage_delete, 211, ##ctx) \
+ FN(btf_get_struct_member_info, 212, ##ctx) \
/* This helper list is effectively frozen. If you are trying to \
* add a new helper, you should add a kfunc instead which has \
* less stability guarantees. See Documentation/bpf/kfuncs.rst \

--
2.56.0.455.g6c5a503b38-goog