* [PATCH v7 bpf-next 01/10] btf: add kind layout encoding to UAPI
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 02/10] libbpf: Support kind layout section handling in BTF Alan Maguire
` (8 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
BTF kind layouts provide information to parse BTF kinds. By separating
parsing BTF from using all the information it provides, we allow BTF
to encode new features even if they cannot be used by readers. This
will be helpful in particular for cases where older tools are used
to parse newer BTF with kinds the older tools do not recognize;
the BTF can still be parsed in such cases using kind layout.
The intent is to support encoding of kind layouts optionally so that
tools like pahole can add this information. For each kind, we record
- length of singular element following struct btf_type
- length of each of the btf_vlen() elements following
The ideas here were discussed at [1], [2]; hence
Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
[1] https://lore.kernel.org/bpf/CAEf4BzYjWHRdNNw4B=eOXOs_ONrDwrgX4bn=Nuc1g8JPFC34MA@mail.gmail.com/
[2] https://lore.kernel.org/bpf/20230531201936.1992188-1-alan.maguire@oracle.com/
---
include/uapi/linux/btf.h | 10 ++++++++++
tools/include/uapi/linux/btf.h | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index 266d4ffa6c07..64dd681274f4 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -8,6 +8,14 @@
#define BTF_MAGIC 0xeB9F
#define BTF_VERSION 1
+/* kind layout section consists of a struct btf_kind_layout for each known
+ * kind at BTF encoding time.
+ */
+struct btf_kind_layout {
+ __u8 info_sz; /* size of singular element after btf_type */
+ __u8 elem_sz; /* size of each of btf_vlen(t) elements */
+};
+
struct btf_header {
__u16 magic;
__u8 version;
@@ -19,6 +27,8 @@ struct btf_header {
__u32 type_len; /* length of type section */
__u32 str_off; /* offset of string section */
__u32 str_len; /* length of string section */
+ __u32 kind_layout_off;/* offset of kind layout section */
+ __u32 kind_layout_len;/* length of kind layout section */
};
/* Max # of type identifier */
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index 266d4ffa6c07..64dd681274f4 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -8,6 +8,14 @@
#define BTF_MAGIC 0xeB9F
#define BTF_VERSION 1
+/* kind layout section consists of a struct btf_kind_layout for each known
+ * kind at BTF encoding time.
+ */
+struct btf_kind_layout {
+ __u8 info_sz; /* size of singular element after btf_type */
+ __u8 elem_sz; /* size of each of btf_vlen(t) elements */
+};
+
struct btf_header {
__u16 magic;
__u8 version;
@@ -19,6 +27,8 @@ struct btf_header {
__u32 type_len; /* length of type section */
__u32 str_off; /* offset of string section */
__u32 str_len; /* length of string section */
+ __u32 kind_layout_off;/* offset of kind layout section */
+ __u32 kind_layout_len;/* length of kind layout section */
};
/* Max # of type identifier */
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v7 bpf-next 02/10] libbpf: Support kind layout section handling in BTF
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 01/10] btf: add kind layout encoding to UAPI Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 17:07 ` bot+bpf-ci
2025-12-11 16:46 ` [PATCH v7 bpf-next 03/10] libbpf: use kind layout to compute an unknown kind size Alan Maguire
` (7 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
Support reading in kind layout fixing endian issues on reading;
also support writing kind layout section to raw BTF object.
There is not yet an API to populate the kind layout with meaningful
information.
As part of this, we need to consider multiple valid BTF header
sizes; the original or the kind layout-extended headers.
So to support this, the "struct btf" representation is modified
to always allocate a "struct btf_header" and copy the valid
portion from the raw data to it; this means we can always safely
check fields like btf->hdr->kind_layout_len.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/lib/bpf/btf.c | 218 +++++++++++++++++++++++++++++++-------------
1 file changed, 157 insertions(+), 61 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index b136572e889a..531089a64adc 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -40,42 +40,53 @@ struct btf {
/*
* When BTF is loaded from an ELF or raw memory it is stored
- * in a contiguous memory block. The hdr, type_data, and, strs_data
+ * in a contiguous memory block. The type_data, and, strs_data
* point inside that memory region to their respective parts of BTF
* representation:
*
- * +--------------------------------+
- * | Header | Types | Strings |
- * +--------------------------------+
- * ^ ^ ^
- * | | |
- * hdr | |
- * types_data-+ |
- * strs_data------------+
+ * +--------------------------------+---------------------+
+ * | Header | Types | Strings |Optional kind layout |
+ * +--------------------------------+---------------------+
+ * ^ ^ ^ ^
+ * | | | |
+ * raw_data | | |
+ * types_data-+ | |
+ * strs_data------------+ |
+ * kind_layout----------------------+
+ *
+ * A separate struct btf_header is allocated for btf->hdr,
+ * and header information is copied into it. This allows us
+ * to handle header data for various header formats; the original,
+ * the extended header with kind layout, etc.
*
* If BTF data is later modified, e.g., due to types added or
* removed, BTF deduplication performed, etc, this contiguous
- * representation is broken up into three independently allocated
- * memory regions to be able to modify them independently.
+ * representation is broken up into four independent memory
+ * regions.
+ *
* raw_data is nulled out at that point, but can be later allocated
* and cached again if user calls btf__raw_data(), at which point
- * raw_data will contain a contiguous copy of header, types, and
- * strings:
+ * raw_data will contain a contiguous copy of header, types, strings
+ * and optionally kind_layout. kind_layout optionally points to a
+ * kind_layout array - this allows us to encode information about
+ * the kinds known at encoding time. If kind_layout is NULL no
+ * kind information is encoded.
*
- * +----------+ +---------+ +-----------+
- * | Header | | Types | | Strings |
- * +----------+ +---------+ +-----------+
- * ^ ^ ^
- * | | |
- * hdr | |
- * types_data----+ |
- * strset__data(strs_set)-----+
+ * +----------+ +---------+ +-----------+ +-----------+
+ * | Header | | Types | | Strings | |kind_layout|
+ * +----------+ +---------+ +-----------+ +-----------+
+ * ^ ^ ^ ^
+ * | | | |
+ * hdr | | |
+ * types_data----+ | |
+ * strset__data(strs_set)-----+ |
+ * kind_layout--------------------------------+
*
- * +----------+---------+-----------+
- * | Header | Types | Strings |
- * raw_data----->+----------+---------+-----------+
+ * +----------+---------+-----------+---------------------+
+ * | Header | Types | Strings | Optional kind layout|
+ * raw_data----->+----------+---------+-----------+---------------------+
*/
- struct btf_header *hdr;
+ struct btf_header *hdr; /* separately-allocated header data */
void *types_data;
size_t types_data_cap; /* used size stored in hdr->type_len */
@@ -123,6 +134,13 @@ struct btf {
/* whether raw_data is a (read-only) mmap */
bool raw_data_is_mmap;
+ /* is BTF modifiable? i.e. is it split into separate sections as described above? */
+ bool modifiable;
+ /* Points either at raw kind layout data in parsed BTF (if present), or
+ * at an allocated kind layout array when BTF is modifiable.
+ */
+ void *kind_layout;
+
/* BTF object FD, if loaded into kernel */
int fd;
@@ -214,7 +232,7 @@ static int btf_add_type_idx_entry(struct btf *btf, __u32 type_off)
return 0;
}
-static void btf_bswap_hdr(struct btf_header *h)
+static void btf_bswap_hdr(struct btf_header *h, __u32 hdr_len)
{
h->magic = bswap_16(h->magic);
h->hdr_len = bswap_32(h->hdr_len);
@@ -222,54 +240,89 @@ static void btf_bswap_hdr(struct btf_header *h)
h->type_len = bswap_32(h->type_len);
h->str_off = bswap_32(h->str_off);
h->str_len = bswap_32(h->str_len);
+ /* May be operating on raw data with hdr_len that does not include below fields */
+ if (hdr_len >= sizeof(struct btf_header)) {
+ h->kind_layout_off = bswap_32(h->kind_layout_off);
+ h->kind_layout_len = bswap_32(h->kind_layout_len);
+ }
}
static int btf_parse_hdr(struct btf *btf)
{
- struct btf_header *hdr = btf->hdr;
+ struct btf_header *hdr = btf->raw_data;
+ __u32 hdr_len = hdr->hdr_len;
__u32 meta_left;
- if (btf->raw_size < sizeof(struct btf_header)) {
+ if (btf->raw_size < offsetofend(struct btf_header, str_len)) {
pr_debug("BTF header not found\n");
return -EINVAL;
}
if (hdr->magic == bswap_16(BTF_MAGIC)) {
btf->swapped_endian = true;
- if (bswap_32(hdr->hdr_len) != sizeof(struct btf_header)) {
+ hdr_len = bswap_32(hdr->hdr_len);
+ if (hdr_len < offsetofend(struct btf_header, str_len)) {
pr_warn("Can't load BTF with non-native endianness due to unsupported header length %u\n",
- bswap_32(hdr->hdr_len));
+ hdr_len);
return -ENOTSUP;
}
- btf_bswap_hdr(hdr);
} else if (hdr->magic != BTF_MAGIC) {
pr_debug("Invalid BTF magic: %x\n", hdr->magic);
return -EINVAL;
}
- if (btf->raw_size < hdr->hdr_len) {
+ if (btf->raw_size < hdr_len) {
pr_debug("BTF header len %u larger than data size %u\n",
- hdr->hdr_len, btf->raw_size);
+ hdr_len, btf->raw_size);
return -EINVAL;
}
- meta_left = btf->raw_size - hdr->hdr_len;
- if (meta_left < (long long)hdr->str_off + hdr->str_len) {
+ /* At this point, we have basic header information, so allocate btf->hdr */
+ btf->hdr = calloc(1, sizeof(struct btf_header));
+ if (!btf->hdr) {
+ pr_debug("BTF header allocation failed\n");
+ return -ENOMEM;
+ }
+ if (btf->swapped_endian)
+ btf_bswap_hdr(hdr, hdr_len);
+ memcpy(btf->hdr, hdr, hdr_len < sizeof(struct btf_header) ? hdr_len :
+ sizeof(struct btf_header));
+
+ meta_left = btf->raw_size - hdr_len;
+ if (meta_left < (long long)btf->hdr->str_off + btf->hdr->str_len) {
pr_debug("Invalid BTF total size: %u\n", btf->raw_size);
return -EINVAL;
}
- if ((long long)hdr->type_off + hdr->type_len > hdr->str_off) {
+ if ((long long)btf->hdr->type_off + btf->hdr->type_len > btf->hdr->str_off) {
pr_debug("Invalid BTF data sections layout: type data at %u + %u, strings data at %u + %u\n",
- hdr->type_off, hdr->type_len, hdr->str_off, hdr->str_len);
+ btf->hdr->type_off, btf->hdr->type_len, btf->hdr->str_off,
+ btf->hdr->str_len);
return -EINVAL;
}
- if (hdr->type_off % 4) {
+ if (btf->hdr->type_off % 4) {
pr_debug("BTF type section is not aligned to 4 bytes\n");
return -EINVAL;
}
+ if (btf->hdr->kind_layout_len == 0)
+ return 0;
+
+ if (btf->hdr->kind_layout_off % 4) {
+ pr_debug("BTF kind_layout section is not aligned to 4 bytes\n");
+ return -EINVAL;
+ }
+ if (btf->hdr->kind_layout_off < btf->hdr->str_off + btf->hdr->str_len) {
+ pr_debug("Invalid BTF data sections layout: strings data at %u + %u, kind layout data at %u + %u\n",
+ btf->hdr->str_off, btf->hdr->str_len,
+ btf->hdr->kind_layout_off, btf->hdr->kind_layout_len);
+ return -EINVAL;
+ }
+ if (btf->hdr->kind_layout_off + btf->hdr->kind_layout_len > meta_left) {
+ pr_debug("Invalid BTF total size: %u\n", btf->raw_size);
+ return -EINVAL;
+ }
return 0;
}
@@ -292,6 +345,22 @@ static int btf_parse_str_sec(struct btf *btf)
return 0;
}
+static int btf_parse_kind_layout_sec(struct btf *btf)
+{
+ const struct btf_header *hdr = btf->hdr;
+
+ if (!hdr->kind_layout_len)
+ return 0;
+
+ if (hdr->kind_layout_len % sizeof(struct btf_kind_layout) != 0) {
+ pr_debug("Invalid BTF kind layout section\n");
+ return -EINVAL;
+ }
+ btf->kind_layout = btf->raw_data + btf->hdr->hdr_len + btf->hdr->kind_layout_off;
+
+ return 0;
+}
+
static int btf_type_size(const struct btf_type *t)
{
const int base_size = sizeof(struct btf_type);
@@ -951,7 +1020,8 @@ __s32 btf__find_by_name_kind(const struct btf *btf, const char *type_name,
static bool btf_is_modifiable(const struct btf *btf)
{
- return (void *)btf->hdr != btf->raw_data;
+ /* BTF is modifiable if split into multiple sections */
+ return btf->modifiable;
}
static void btf_free_raw_data(struct btf *btf)
@@ -980,10 +1050,11 @@ void btf__free(struct btf *btf)
* might still have a cached contiguous raw data present,
* which will be unconditionally freed below.
*/
- free(btf->hdr);
free(btf->types_data);
strset__free(btf->strs_set);
+ free(btf->kind_layout);
}
+ free(btf->hdr);
btf_free_raw_data(btf);
free(btf->raw_data_swapped);
free(btf->type_offs);
@@ -994,6 +1065,7 @@ void btf__free(struct btf *btf)
static struct btf *btf_new_empty(struct btf *base_btf)
{
+ struct btf_header *hdr;
struct btf *btf;
btf = calloc(1, sizeof(*btf));
@@ -1022,14 +1094,21 @@ static struct btf *btf_new_empty(struct btf *base_btf)
return ERR_PTR(-ENOMEM);
}
- btf->hdr = btf->raw_data;
- btf->hdr->hdr_len = sizeof(struct btf_header);
- btf->hdr->magic = BTF_MAGIC;
- btf->hdr->version = BTF_VERSION;
+ hdr = btf->raw_data;
+ hdr->hdr_len = sizeof(struct btf_header);
+ hdr->magic = BTF_MAGIC;
+ hdr->version = BTF_VERSION;
- btf->types_data = btf->raw_data + btf->hdr->hdr_len;
- btf->strs_data = btf->raw_data + btf->hdr->hdr_len;
- btf->hdr->str_len = base_btf ? 0 : 1; /* empty string at offset 0 */
+ btf->types_data = btf->raw_data + hdr->hdr_len;
+ btf->strs_data = btf->raw_data + hdr->hdr_len;
+ hdr->str_len = base_btf ? 0 : 1; /* empty string at offset 0 */
+ btf->hdr = calloc(1, sizeof(struct btf_header));
+ if (!btf->hdr) {
+ free(btf->raw_data);
+ free(btf);
+ return ERR_PTR(-ENOMEM);
+ }
+ memcpy(btf->hdr, hdr, sizeof(*hdr));
return btf;
}
@@ -1078,7 +1157,6 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, b
btf->raw_size = size;
- btf->hdr = btf->raw_data;
err = btf_parse_hdr(btf);
if (err)
goto done;
@@ -1087,6 +1165,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, b
btf->types_data = btf->raw_data + btf->hdr->hdr_len + btf->hdr->type_off;
err = btf_parse_str_sec(btf);
+ err = err ?: btf_parse_kind_layout_sec(btf);
err = err ?: btf_parse_type_sec(btf);
err = err ?: btf_sanity_check(btf);
if (err)
@@ -1550,6 +1629,11 @@ static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endi
}
data_sz = hdr->hdr_len + hdr->type_len + hdr->str_len;
+ if (btf->kind_layout) {
+ data_sz = roundup(data_sz, 4);
+ data_sz += hdr->kind_layout_len;
+ hdr->kind_layout_off = roundup(hdr->type_len + hdr->str_len, 4);
+ }
data = calloc(1, data_sz);
if (!data)
return NULL;
@@ -1557,7 +1641,7 @@ static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endi
memcpy(p, hdr, hdr->hdr_len);
if (swap_endian)
- btf_bswap_hdr(p);
+ btf_bswap_hdr(p, hdr->hdr_len);
p += hdr->hdr_len;
memcpy(p, btf->types_data, hdr->type_len);
@@ -1576,7 +1660,11 @@ static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endi
p += hdr->type_len;
memcpy(p, btf_strs_data(btf), hdr->str_len);
- p += hdr->str_len;
+
+ if (btf->kind_layout) {
+ p = data + hdr->hdr_len + hdr->kind_layout_off;
+ memcpy(p, btf->kind_layout, hdr->kind_layout_len);
+ }
*size = data_sz;
return data;
@@ -1717,13 +1805,13 @@ static void btf_invalidate_raw_data(struct btf *btf)
}
}
-/* Ensure BTF is ready to be modified (by splitting into a three memory
- * regions for header, types, and strings). Also invalidate cached
- * raw_data, if any.
+/* Ensure BTF is ready to be modified (by splitting into memory regions
+ * for types and strings, with kind layout section if needed (btf->hdr
+ * is already a separate region). Also invalidate cached raw_data, if any.
*/
static int btf_ensure_modifiable(struct btf *btf)
{
- void *hdr, *types;
+ void *types, *kind_layout = NULL;
struct strset *set = NULL;
int err = -ENOMEM;
@@ -1733,15 +1821,20 @@ static int btf_ensure_modifiable(struct btf *btf)
return 0;
}
- /* split raw data into three memory regions */
- hdr = malloc(btf->hdr->hdr_len);
+ /* split raw data into memory regions; btf->hdr is done already. */
types = malloc(btf->hdr->type_len);
- if (!hdr || !types)
+ if (!types)
goto err_out;
-
- memcpy(hdr, btf->hdr, btf->hdr->hdr_len);
memcpy(types, btf->types_data, btf->hdr->type_len);
+ if (btf->hdr->kind_layout_len) {
+ kind_layout = malloc(btf->hdr->kind_layout_len);
+ if (!kind_layout)
+ goto err_out;
+ memcpy(kind_layout, btf->raw_data + btf->hdr->hdr_len + btf->hdr->kind_layout_off,
+ btf->hdr->kind_layout_len);
+ }
+
/* build lookup index for all strings */
set = strset__new(BTF_MAX_STR_OFFSET, btf->strs_data, btf->hdr->str_len);
if (IS_ERR(set)) {
@@ -1750,11 +1843,12 @@ static int btf_ensure_modifiable(struct btf *btf)
}
/* only when everything was successful, update internal state */
- btf->hdr = hdr;
btf->types_data = types;
btf->types_data_cap = btf->hdr->type_len;
btf->strs_data = NULL;
btf->strs_set = set;
+ if (kind_layout)
+ btf->kind_layout = kind_layout;
/* if BTF was created from scratch, all strings are guaranteed to be
* unique and deduplicated
*/
@@ -1766,12 +1860,14 @@ static int btf_ensure_modifiable(struct btf *btf)
/* invalidate raw_data representation */
btf_invalidate_raw_data(btf);
+ btf->modifiable = true;
+
return 0;
err_out:
strset__free(set);
- free(hdr);
free(types);
+ free(kind_layout);
return err;
}
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v7 bpf-next 02/10] libbpf: Support kind layout section handling in BTF
2025-12-11 16:46 ` [PATCH v7 bpf-next 02/10] libbpf: Support kind layout section handling in BTF Alan Maguire
@ 2025-12-11 17:07 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2025-12-11 17:07 UTC (permalink / raw)
To: alan.maguire, andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, alan.maguire, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 4623 bytes --]
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index b136572e889a..531089a64adc 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
[ ... ]
> @@ -214,7 +232,7 @@ static int btf_add_type_idx_entry(struct btf *btf, __u32 type_off)
> return 0;
> }
>
> -static void btf_bswap_hdr(struct btf_header *h)
> +static void btf_bswap_hdr(struct btf_header *h, __u32 hdr_len)
> {
> h->magic = bswap_16(h->magic);
> h->hdr_len = bswap_32(h->hdr_len);
> @@ -222,54 +240,89 @@ static void btf_bswap_hdr(struct btf_header *h)
> h->type_len = bswap_32(h->type_len);
> h->str_off = bswap_32(h->str_off);
> h->str_len = bswap_32(h->str_len);
> + /* May be operating on raw data with hdr_len that does not include below fields */
> + if (hdr_len >= sizeof(struct btf_header)) {
> + h->kind_layout_off = bswap_32(h->kind_layout_off);
> + h->kind_layout_len = bswap_32(h->kind_layout_len);
> + }
> }
>
> static int btf_parse_hdr(struct btf *btf)
> {
> - struct btf_header *hdr = btf->hdr;
> + struct btf_header *hdr = btf->raw_data;
> + __u32 hdr_len = hdr->hdr_len;
> __u32 meta_left;
>
> - if (btf->raw_size < sizeof(struct btf_header)) {
> + if (btf->raw_size < offsetofend(struct btf_header, str_len)) {
> pr_debug("BTF header not found\n");
> return -EINVAL;
> }
>
> if (hdr->magic == bswap_16(BTF_MAGIC)) {
> btf->swapped_endian = true;
> - if (bswap_32(hdr->hdr_len) != sizeof(struct btf_header)) {
> + hdr_len = bswap_32(hdr->hdr_len);
> + if (hdr_len < offsetofend(struct btf_header, str_len)) {
> pr_warn("Can't load BTF with non-native endianness due to unsupported header length %u\n",
> - bswap_32(hdr->hdr_len));
> + hdr_len);
> return -ENOTSUP;
> }
> - btf_bswap_hdr(hdr);
> } else if (hdr->magic != BTF_MAGIC) {
> pr_debug("Invalid BTF magic: %x\n", hdr->magic);
> return -EINVAL;
> }
>
> - if (btf->raw_size < hdr->hdr_len) {
> + if (btf->raw_size < hdr_len) {
> pr_debug("BTF header len %u larger than data size %u\n",
> - hdr->hdr_len, btf->raw_size);
> + hdr_len, btf->raw_size);
> return -EINVAL;
> }
>
> - meta_left = btf->raw_size - hdr->hdr_len;
> - if (meta_left < (long long)hdr->str_off + hdr->str_len) {
> + /* At this point, we have basic header information, so allocate btf->hdr */
> + btf->hdr = calloc(1, sizeof(struct btf_header));
> + if (!btf->hdr) {
> + pr_debug("BTF header allocation failed\n");
> + return -ENOMEM;
> + }
> + if (btf->swapped_endian)
> + btf_bswap_hdr(hdr, hdr_len);
^^^
Can this segfault on mmap'd BTF data? In btf_parse_raw_mmap(), the BTF
file is mmap'd with PROT_READ (read-only), then btf_new() is called with
is_mmap=true. The hdr pointer here points to btf->raw_data which is the
mmap'd read-only memory. When btf_bswap_hdr() attempts to write to hdr
(swapping the header fields in-place), it will write to read-only memory.
This differs from the old code which rejected swapped-endian BTF with
the old strict header size check. Now that the header size check is
relaxed to support kind layout, swapped-endian BTF from mmap becomes
accessible but crashes.
Call chain: btf__parse_raw()->btf_parse_raw_mmap() mmaps PROT_READ at
mmap() call, then btf_new(..., true) sets btf->raw_data to mmap'd
memory, then btf_parse_hdr() attempts to write to it.
> + memcpy(btf->hdr, hdr, hdr_len < sizeof(struct btf_header) ? hdr_len :
> + sizeof(struct btf_header));
[ ... ]
> @@ -1550,6 +1629,11 @@ static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endi
> }
>
> data_sz = hdr->hdr_len + hdr->type_len + hdr->str_len;
> + if (btf->kind_layout) {
> + data_sz = roundup(data_sz, 4);
> + data_sz += hdr->kind_layout_len;
> + hdr->kind_layout_off = roundup(hdr->type_len + hdr->str_len, 4);
^^^^^^^^^^^^^^^^^^^^
Should btf_get_raw_data() modify btf->hdr->kind_layout_off? The hdr
pointer is btf->hdr, so this modifies the BTF object's header. The
function signature takes const struct btf *btf_ro, suggesting the object
shouldn't be modified. If btf__raw_data() is called multiple times,
kind_layout_off could potentially be set to different values on each
call, or if the BTF was originally loaded with kind_layout at a
different offset, that information is lost.
> + }
> data = calloc(1, data_sz);
> if (!data)
> return NULL;
[ ... ]
---
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/20140850025
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v7 bpf-next 03/10] libbpf: use kind layout to compute an unknown kind size
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 01/10] btf: add kind layout encoding to UAPI Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 02/10] libbpf: Support kind layout section handling in BTF Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 04/10] libbpf: Add kind layout encoding support Alan Maguire
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
This allows BTF parsing to proceed even if we do not know the
kind.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/lib/bpf/btf.c | 33 ++++++++++++++++++++++++++-------
1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 531089a64adc..c21ee836ba1f 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -361,7 +361,27 @@ static int btf_parse_kind_layout_sec(struct btf *btf)
return 0;
}
-static int btf_type_size(const struct btf_type *t)
+/* for unknown kinds, consult kind layout. */
+static int btf_type_size_unknown(const struct btf *btf, const struct btf_type *t)
+{
+ int size = sizeof(struct btf_type);
+ struct btf_kind_layout *k = NULL;
+ __u16 vlen = btf_vlen(t);
+ __u8 kind = btf_kind(t);
+
+ if (!btf->kind_layout || kind >= (btf->hdr->kind_layout_len / sizeof(*k))) {
+ pr_debug("Unsupported BTF_KIND: %u\n", btf_kind(t));
+ return -EINVAL;
+ }
+ k = &((struct btf_kind_layout *)btf->kind_layout)[kind];
+
+ size += k->info_sz;
+ size += vlen * k->elem_sz;
+
+ return size;
+}
+
+static int btf_type_size(const struct btf *btf, const struct btf_type *t)
{
const int base_size = sizeof(struct btf_type);
__u16 vlen = btf_vlen(t);
@@ -397,8 +417,7 @@ static int btf_type_size(const struct btf_type *t)
case BTF_KIND_DECL_TAG:
return base_size + sizeof(struct btf_decl_tag);
default:
- pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
- return -EINVAL;
+ return btf_type_size_unknown(btf, t);
}
}
@@ -497,7 +516,7 @@ static int btf_parse_type_sec(struct btf *btf)
if (btf->swapped_endian)
btf_bswap_type_base(next_type);
- type_size = btf_type_size(next_type);
+ type_size = btf_type_size(btf, next_type);
if (type_size < 0)
return type_size;
if (next_type + type_size > end_type) {
@@ -1994,7 +2013,7 @@ static int btf_add_type(struct btf_pipe *p, const struct btf_type *src_type)
__u32 *str_off;
int sz, err;
- sz = btf_type_size(src_type);
+ sz = btf_type_size(p->src, src_type);
if (sz < 0)
return libbpf_err(sz);
@@ -2076,7 +2095,7 @@ int btf__add_btf(struct btf *btf, const struct btf *src_btf)
struct btf_field_iter it;
__u32 *type_id, *str_off;
- sz = btf_type_size(t);
+ sz = btf_type_size(src_btf, t);
if (sz < 0) {
/* unlikely, has to be corrupted src_btf */
err = sz;
@@ -5417,7 +5436,7 @@ static int btf_dedup_compact_types(struct btf_dedup *d)
continue;
t = btf__type_by_id(d->btf, id);
- len = btf_type_size(t);
+ len = btf_type_size(d->btf, t);
if (len < 0)
return len;
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v7 bpf-next 04/10] libbpf: Add kind layout encoding support
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
` (2 preceding siblings ...)
2025-12-11 16:46 ` [PATCH v7 bpf-next 03/10] libbpf: use kind layout to compute an unknown kind size Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 05/10] libbpf: BTF validation can use kind layout for unknown kinds Alan Maguire
` (5 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
Support encoding of BTF kind layout data via btf__new_empty_opts().
Current supported opts are base_btf and add_kind_layout.
Kind layout information is maintained in btf.c in the
kind_layouts[] array; when BTF is created with the
add_kind_layout option it represents the current view
of supported BTF kinds.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/lib/bpf/btf.c | 60 ++++++++++++++++++++++++++++++++++++++--
tools/lib/bpf/btf.h | 20 ++++++++++++++
tools/lib/bpf/libbpf.map | 1 +
3 files changed, 78 insertions(+), 3 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index c21ee836ba1f..9ef46aef43fc 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -29,6 +29,35 @@
static struct btf_type btf_void;
+/* Describe how kinds are laid out; some have a singular element following the "struct btf_type",
+ * some have BTF_INFO_VLEN(t->info) elements. Specify sizes for both. Flags are currently unused.
+ * Kind layout can be optionally added to the BTF representation in a dedicated section to
+ * facilitate parsing. New kinds must be added here.
+ */
+struct btf_kind_layout kind_layouts[NR_BTF_KINDS] = {
+/* singular element size vlen element(s) size */
+{ 0, 0 }, /* _UNKN */
+{ sizeof(__u32), 0 }, /* _INT */
+{ 0, 0 }, /* _PTR */
+{ sizeof(struct btf_array), 0 }, /* _ARRAY */
+{ 0, sizeof(struct btf_member) }, /* _STRUCT */
+{ 0, sizeof(struct btf_member) }, /* _UNION */
+{ 0, sizeof(struct btf_enum) }, /* _ENUM */
+{ 0, 0 }, /* _FWD */
+{ 0, 0 }, /* _TYPEDEF */
+{ 0, 0 }, /* _VOLATILE */
+{ 0, 0 }, /* _CONST */
+{ 0, 0 }, /* _RESTRICT */
+{ 0, 0 }, /* _FUNC */
+{ 0, sizeof(struct btf_param) }, /* _FUNC_PROTO */
+{ sizeof(struct btf_var), 0 }, /* _VAR */
+{ 0, sizeof(struct btf_var_secinfo) }, /* _DATASEC */
+{ 0, 0 }, /* _FLOAT */
+{ sizeof(struct btf_decl_tag), 0 }, /* _DECL_TAG */
+{ 0, 0 }, /* _TYPE_TAG */
+{ 0, sizeof(struct btf_enum64) }, /* _ENUM64 */
+};
+
struct btf {
/* raw BTF data in native endianness */
void *raw_data;
@@ -1082,8 +1111,10 @@ void btf__free(struct btf *btf)
free(btf);
}
-static struct btf *btf_new_empty(struct btf *base_btf)
+static struct btf *btf_new_empty(struct btf_new_opts *opts)
{
+ bool add_kind_layout = OPTS_GET(opts, add_kind_layout, false);
+ struct btf *base_btf = OPTS_GET(opts, base_btf, NULL);
struct btf_header *hdr;
struct btf *btf;
@@ -1107,6 +1138,8 @@ static struct btf *btf_new_empty(struct btf *base_btf)
/* +1 for empty string at offset 0 */
btf->raw_size = sizeof(struct btf_header) + (base_btf ? 0 : 1);
+ if (add_kind_layout)
+ btf->raw_size = roundup(btf->raw_size, 4) + sizeof(kind_layouts);
btf->raw_data = calloc(1, btf->raw_size);
if (!btf->raw_data) {
free(btf);
@@ -1127,6 +1160,13 @@ static struct btf *btf_new_empty(struct btf *base_btf)
free(btf);
return ERR_PTR(-ENOMEM);
}
+
+ if (add_kind_layout) {
+ hdr->kind_layout_len = sizeof(kind_layouts);
+ hdr->kind_layout_off = roundup(hdr->str_len, 4);
+ btf->kind_layout = btf->raw_data + hdr->hdr_len + hdr->kind_layout_off;
+ memcpy(btf->kind_layout, kind_layouts, sizeof(kind_layouts));
+ }
memcpy(btf->hdr, hdr, sizeof(*hdr));
return btf;
@@ -1134,12 +1174,26 @@ static struct btf *btf_new_empty(struct btf *base_btf)
struct btf *btf__new_empty(void)
{
- return libbpf_ptr(btf_new_empty(NULL));
+ LIBBPF_OPTS(btf_new_opts, opts);
+
+ return libbpf_ptr(btf_new_empty(&opts));
}
struct btf *btf__new_empty_split(struct btf *base_btf)
{
- return libbpf_ptr(btf_new_empty(base_btf));
+ LIBBPF_OPTS(btf_new_opts, opts);
+
+ opts.base_btf = base_btf;
+
+ return libbpf_ptr(btf_new_empty(&opts));
+}
+
+struct btf *btf__new_empty_opts(struct btf_new_opts *opts)
+{
+ if (!OPTS_VALID(opts, btf_new_opts))
+ return libbpf_err_ptr(-EINVAL);
+
+ return libbpf_ptr(btf_new_empty(opts));
}
static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, bool is_mmap)
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index cc01494d6210..dcc166834937 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -109,6 +109,26 @@ LIBBPF_API struct btf *btf__new_empty(void);
*/
LIBBPF_API struct btf *btf__new_empty_split(struct btf *base_btf);
+struct btf_new_opts {
+ size_t sz;
+ struct btf *base_btf; /* optional base BTF */
+ bool add_kind_layout; /* add BTF kind layout information */
+ size_t:0;
+};
+#define btf_new_opts__last_field add_kind_layout
+
+/**
+ * @brief **btf__new_empty_opts()** creates an unpopulated BTF object with
+ * optional *base_btf* and BTF kind layout description if *add_kind_layout*
+ * is set
+ * @return new BTF object instance which has to be eventually freed with
+ * **btf__free()**
+ *
+ * On error, NULL is returned and the thread-local `errno` variable is
+ * set to the error code.
+ */
+LIBBPF_API struct btf *btf__new_empty_opts(struct btf_new_opts *opts);
+
/**
* @brief **btf__distill_base()** creates new versions of the split BTF
* *src_btf* and its base BTF. The new base BTF will only contain the types
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 84fb90a016c9..0fb9a1f70e72 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -453,4 +453,5 @@ LIBBPF_1.7.0 {
bpf_map__exclusive_program;
bpf_prog_assoc_struct_ops;
bpf_program__assoc_struct_ops;
+ btf__new_empty_opts;
} LIBBPF_1.6.0;
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v7 bpf-next 05/10] libbpf: BTF validation can use kind layout for unknown kinds
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
` (3 preceding siblings ...)
2025-12-11 16:46 ` [PATCH v7 bpf-next 04/10] libbpf: Add kind layout encoding support Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 06/10] btf: support kernel parsing of BTF with kind layout Alan Maguire
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
BTF parsing can use kind layout to navigate unknown kinds, so
btf_validate_type() should take kind layout information into
account to avoid failure when an unrecognized kind is met.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/lib/bpf/btf.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 9ef46aef43fc..440cd451c340 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -706,8 +706,12 @@ static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __
break;
}
default:
- pr_warn("btf: type [%u]: unrecognized kind %u\n", id, kind);
- return -EINVAL;
+ /* Kind may be represented in kind layout information. */
+ if (btf_type_size_unknown(btf, t) < 0) {
+ pr_warn("btf: type [%u]: unrecognized kind %u\n", id, kind);
+ return -EINVAL;
+ }
+ break;
}
return 0;
}
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v7 bpf-next 06/10] btf: support kernel parsing of BTF with kind layout
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
` (4 preceding siblings ...)
2025-12-11 16:46 ` [PATCH v7 bpf-next 05/10] libbpf: BTF validation can use kind layout for unknown kinds Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 17:08 ` bot+bpf-ci
2025-12-11 16:46 ` [PATCH v7 bpf-next 07/10] selftests/bpf: test kind encoding/decoding Alan Maguire
` (3 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
Validate kind layout if present, but because the kernel must be
strict in what it accepts, reject BTF with unsupported kinds,
even if they are in the kind layout information.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
kernel/bpf/btf.c | 96 ++++++++++++++++++++++++++++++++++++++----------
1 file changed, 76 insertions(+), 20 deletions(-)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 0de8fc8a0e0b..d6221d3e7893 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -268,6 +268,7 @@ struct btf {
struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;
struct btf_struct_metas *struct_meta_tab;
struct btf_struct_ops_tab *struct_ops_tab;
+ struct btf_kind_layout *kind_layout;
/* split BTF support */
struct btf *base_btf;
@@ -5215,23 +5216,36 @@ static s32 btf_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}
- if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX ||
- BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) {
+ if (!btf_name_offset_valid(env->btf, t->name_off)) {
+ btf_verifier_log(env, "[%u] Invalid name_offset:%u",
+ env->log_type_id, t->name_off);
+ return -EINVAL;
+ }
+
+ if (BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) {
btf_verifier_log(env, "[%u] Invalid kind:%u",
env->log_type_id, BTF_INFO_KIND(t->info));
return -EINVAL;
}
- if (!btf_name_offset_valid(env->btf, t->name_off)) {
- btf_verifier_log(env, "[%u] Invalid name_offset:%u",
- env->log_type_id, t->name_off);
+ if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX && env->btf->kind_layout &&
+ (BTF_INFO_KIND(t->info) * sizeof(struct btf_kind_layout)) <
+ env->btf->hdr.kind_layout_len) {
+ btf_verifier_log(env, "[%u] unknown but required kind %u",
+ env->log_type_id,
+ BTF_INFO_KIND(t->info));
return -EINVAL;
+ } else {
+ if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX) {
+ btf_verifier_log(env, "[%u] Invalid kind:%u",
+ env->log_type_id, BTF_INFO_KIND(t->info));
+ return -EINVAL;
+ }
+ var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left);
+ if (var_meta_size < 0)
+ return var_meta_size;
}
- var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left);
- if (var_meta_size < 0)
- return var_meta_size;
-
meta_left -= var_meta_size;
return saved_meta_left - meta_left;
@@ -5405,7 +5419,8 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
start = btf->nohdr_data + hdr->str_off;
end = start + hdr->str_len;
- if (end != btf->data + btf->data_size) {
+ if (hdr->hdr_len < sizeof(struct btf_header) &&
+ end != btf->data + btf->data_size) {
btf_verifier_log(env, "String section is not at the end");
return -EINVAL;
}
@@ -5426,9 +5441,41 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
return 0;
}
+static int btf_parse_kind_layout_sec(struct btf_verifier_env *env)
+{
+ const struct btf_header *hdr = &env->btf->hdr;
+ struct btf *btf = env->btf;
+ void *start, *end;
+
+ if (hdr->hdr_len < sizeof(struct btf_header) ||
+ hdr->kind_layout_len == 0)
+ return 0;
+
+ /* Kind layout section must align to 4 bytes */
+ if (hdr->kind_layout_off & (sizeof(u32) - 1)) {
+ btf_verifier_log(env, "Unaligned kind_layout_off");
+ return -EINVAL;
+ }
+ start = btf->nohdr_data + hdr->kind_layout_off;
+ end = start + hdr->kind_layout_len;
+
+ if (hdr->kind_layout_len < sizeof(struct btf_kind_layout)) {
+ btf_verifier_log(env, "Kind layout section is too small");
+ return -EINVAL;
+ }
+ if (end > btf->data + btf->data_size) {
+ btf_verifier_log(env, "Kind layout section is too big");
+ return -EINVAL;
+ }
+ btf->kind_layout = start;
+
+ return 0;
+}
+
static const size_t btf_sec_info_offset[] = {
offsetof(struct btf_header, type_off),
offsetof(struct btf_header, str_off),
+ offsetof(struct btf_header, kind_layout_off),
};
static int btf_sec_info_cmp(const void *a, const void *b)
@@ -5443,44 +5490,49 @@ static int btf_check_sec_info(struct btf_verifier_env *env,
u32 btf_data_size)
{
struct btf_sec_info secs[ARRAY_SIZE(btf_sec_info_offset)];
- u32 total, expected_total, i;
+ u32 nr_secs = ARRAY_SIZE(btf_sec_info_offset);
+ u32 total, expected_total, gap, i;
const struct btf_header *hdr;
const struct btf *btf;
btf = env->btf;
hdr = &btf->hdr;
+ if (hdr->hdr_len < sizeof(struct btf_header))
+ nr_secs--;
+
/* Populate the secs from hdr */
- for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++)
+ for (i = 0; i < nr_secs; i++)
secs[i] = *(struct btf_sec_info *)((void *)hdr +
btf_sec_info_offset[i]);
- sort(secs, ARRAY_SIZE(btf_sec_info_offset),
+ sort(secs, nr_secs,
sizeof(struct btf_sec_info), btf_sec_info_cmp, NULL);
/* Check for gaps and overlap among sections */
total = 0;
expected_total = btf_data_size - hdr->hdr_len;
- for (i = 0; i < ARRAY_SIZE(btf_sec_info_offset); i++) {
+ for (i = 0; i < nr_secs; i++) {
if (expected_total < secs[i].off) {
btf_verifier_log(env, "Invalid section offset");
return -EINVAL;
}
- if (total < secs[i].off) {
- /* gap */
- btf_verifier_log(env, "Unsupported section found");
- return -EINVAL;
- }
if (total > secs[i].off) {
btf_verifier_log(env, "Section overlap found");
return -EINVAL;
}
+ gap = secs[i].off - total;
+ if (gap >= 4) {
+ /* gap larger than alignment gap */
+ btf_verifier_log(env, "Unsupported section found");
+ return -EINVAL;
+ }
if (expected_total - total < secs[i].len) {
btf_verifier_log(env,
"Total section length too long");
return -EINVAL;
}
- total += secs[i].len;
+ total += secs[i].len + gap;
}
/* There is data other than hdr and known sections */
@@ -5816,6 +5868,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
if (err)
goto errout;
+ err = btf_parse_kind_layout_sec(env);
+ if (err)
+ goto errout;
+
err = btf_parse_type_sec(env);
if (err)
goto errout;
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v7 bpf-next 06/10] btf: support kernel parsing of BTF with kind layout
2025-12-11 16:46 ` [PATCH v7 bpf-next 06/10] btf: support kernel parsing of BTF with kind layout Alan Maguire
@ 2025-12-11 17:08 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2025-12-11 17:08 UTC (permalink / raw)
To: alan.maguire, andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, alan.maguire, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2810 bytes --]
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 0de8fc8a0e0b..d6221d3e7893 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
[ ... ]
> @@ -5215,23 +5216,36 @@ static s32 btf_check_meta(struct btf_verifier_env *env,
> return -EINVAL;
> }
>
> - if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX ||
> - BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) {
> + if (!btf_name_offset_valid(env->btf, t->name_off)) {
> + btf_verifier_log(env, "[%u] Invalid name_offset:%u",
> + env->log_type_id, t->name_off);
> + return -EINVAL;
> + }
> +
> + if (BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) {
> btf_verifier_log(env, "[%u] Invalid kind:%u",
> env->log_type_id, BTF_INFO_KIND(t->info));
> return -EINVAL;
> }
>
> - if (!btf_name_offset_valid(env->btf, t->name_off)) {
> - btf_verifier_log(env, "[%u] Invalid name_offset:%u",
> - env->log_type_id, t->name_off);
> + if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX && env->btf->kind_layout &&
> + (BTF_INFO_KIND(t->info) * sizeof(struct btf_kind_layout)) <
> + env->btf->hdr.kind_layout_len) {
^^^^
Is the bounds check correct in btf_check_meta()? The code checks whether
kind_layout[kind] exists by testing if (kind * elem_size < total_len).
This verifies the element starts within the buffer, but to actually read
the full element we need (kind + 1) * elem_size <= total_len.
For example, with kind_layout_len=41 bytes and elem_size=2 bytes:
- Check: kind=20 -> 20 * 2 < 41 -> 40 < 41 -> passes
- But reading kind_layout[20] needs bytes [40, 42) which exceeds the
41-byte buffer
While both code paths still reject the BTF (just with different error
messages), the check allows kind_layout_len values that aren't proper
multiples of sizeof(struct btf_kind_layout) to be treated as if they
contain complete elements at the boundary.
The validation in btf_parse_kind_layout_sec() requires at least one
complete element (kind_layout_len >= sizeof(struct btf_kind_layout)) but
doesn't enforce that the length is a multiple of the element size, which
could lead to this off-by-one scenario.
> + btf_verifier_log(env, "[%u] unknown but required kind %u",
> + env->log_type_id,
> + BTF_INFO_KIND(t->info));
> return -EINVAL;
> + } else {
> + if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX) {
> + btf_verifier_log(env, "[%u] Invalid kind:%u",
> + env->log_type_id, BTF_INFO_KIND(t->info));
> + return -EINVAL;
> + }
> + var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left);
> + if (var_meta_size < 0)
> + return var_meta_size;
> }
[ ... ]
---
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/20140850025
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v7 bpf-next 07/10] selftests/bpf: test kind encoding/decoding
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
` (5 preceding siblings ...)
2025-12-11 16:46 ` [PATCH v7 bpf-next 06/10] btf: support kernel parsing of BTF with kind layout Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 08/10] bpftool: add BTF dump "format meta" to dump header/metadata Alan Maguire
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
verify btf__new_empty_opts() adds kind layouts for all kinds supported,
and after adding kind-related types for an unknown kind, ensure that
parsing uses this info when that kind is encountered rather than
giving up.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
.../selftests/bpf/prog_tests/btf_kind.c | 178 ++++++++++++++++++
1 file changed, 178 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_kind.c
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_kind.c b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
new file mode 100644
index 000000000000..322f207873c2
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025, Oracle and/or its affiliates. */
+
+#include <test_progs.h>
+#include <bpf/btf.h>
+#include <bpf/libbpf.h>
+
+/* verify kind encoding exists for each kind */
+static void test_btf_kind_encoding(void)
+{
+ LIBBPF_OPTS(btf_new_opts, opts);
+ const struct btf_header *hdr;
+ const void *raw_btf;
+ struct btf *btf;
+ __u32 raw_size;
+
+ opts.add_kind_layout = true;
+ btf = btf__new_empty_opts(&opts);
+ if (!ASSERT_OK_PTR(btf, "btf_new"))
+ return;
+
+ raw_btf = btf__raw_data(btf, &raw_size);
+ if (!ASSERT_OK_PTR(raw_btf, "btf__raw_data"))
+ return;
+
+ hdr = raw_btf;
+
+ ASSERT_EQ(hdr->kind_layout_off % 4, 0, "kind_layout aligned");
+ ASSERT_EQ(hdr->kind_layout_len, sizeof(struct btf_kind_layout) * NR_BTF_KINDS,
+ "kind_layout_len");
+ btf__free(btf);
+}
+
+static void write_raw_btf(const char *btf_path, void *raw_btf, size_t raw_size)
+{
+ int fd = open(btf_path, O_WRONLY | O_CREAT);
+
+ write(fd, raw_btf, raw_size);
+ close(fd);
+}
+
+/* fabricate an unrecognized kind at BTF_KIND_MAX + 1, and after adding
+ * the appropriate struct/typedefs to the BTF such that it recognizes
+ * this kind, ensure that parsing of BTF containing the unrecognized kind
+ * can succeed.
+ */
+void test_btf_kind_decoding(void)
+{
+ __s32 int_id, unrec_id, id, id2;
+ LIBBPF_OPTS(btf_new_opts, opts);
+ struct btf *btf, *new_btf;
+ struct btf_kind_layout *k;
+ struct btf_header *hdr;
+ const void *raw_btf;
+ struct btf_type *t;
+ char btf_path[64];
+ void *new_raw_btf;
+ __u32 raw_size;
+
+ opts.add_kind_layout = true;
+ btf = btf__new_empty_opts(&opts);
+ if (!ASSERT_OK_PTR(btf, "btf_new"))
+ return;
+
+ int_id = btf__add_int(btf, "test_char", 1, BTF_INT_CHAR);
+ if (!ASSERT_GT(int_id, 0, "add_int_id"))
+ return;
+
+ /* now create our type with unrecognized kind by adding a typedef kind
+ * we will overwrite it with our unrecognized kind value.
+ */
+ unrec_id = btf__add_typedef(btf, "unrec_kind", int_id);
+ if (!ASSERT_GT(unrec_id, 0, "add_unrec_id"))
+ return;
+
+ /* add an id after it that we will look up to verify we can parse
+ * beyond unrecognized kinds.
+ */
+ id = btf__add_typedef(btf, "test_lookup", int_id);
+ if (!ASSERT_GT(id, 0, "add_test_lookup_id"))
+ return;
+ id2 = btf__add_typedef(btf, "test_lookup2", int_id);
+ if (!ASSERT_GT(id2, 0, "add_test_lookup_id2"))
+ return;
+
+ raw_btf = (void *)btf__raw_data(btf, &raw_size);
+ if (!ASSERT_OK_PTR(raw_btf, "btf__raw_data"))
+ return;
+
+ new_raw_btf = calloc(1, raw_size + sizeof(*k));
+ if (!ASSERT_OK_PTR(new_raw_btf, "calloc_raw_btf"))
+ return;
+ memcpy(new_raw_btf, raw_btf, raw_size);
+
+ /* add new layout description */
+ hdr = new_raw_btf;
+ hdr->kind_layout_len += sizeof(*k);
+ k = new_raw_btf + hdr->hdr_len + hdr->kind_layout_off;
+ k[NR_BTF_KINDS].info_sz = 0;
+ k[NR_BTF_KINDS].elem_sz = 0;
+
+ /* now modify our typedef added above to be an unrecognized kind. */
+ t = (void *)hdr + hdr->hdr_len + hdr->type_off + sizeof(struct btf_type) +
+ sizeof(__u32);
+ t->info = (NR_BTF_KINDS << 24);
+
+ /* now write our BTF to a raw file, ready for parsing. */
+ snprintf(btf_path, sizeof(btf_path), "/tmp/btf_kind.%d", getpid());
+
+ write_raw_btf(btf_path, new_raw_btf, raw_size + sizeof(*k));
+
+ /* verify parsing succeeds, and that we can read type info past
+ * the unrecognized kind.
+ */
+ new_btf = btf__parse_raw(btf_path);
+ if (ASSERT_OK_PTR(new_btf, "btf__parse_raw")) {
+ ASSERT_EQ(btf__find_by_name_kind(new_btf, "test_lookup",
+ BTF_KIND_TYPEDEF), id,
+ "verify_id_lookup");
+ ASSERT_EQ(btf__find_by_name_kind(new_btf, "test_lookup2",
+ BTF_KIND_TYPEDEF), id2,
+ "verify_id2_lookup");
+ }
+ btf__free(new_btf);
+
+ /* next, change info_sz to equal sizeof(struct btf_type); this means the
+ * "test_lookup" kind will be reinterpreted as a singular info element
+ * following the unrecognized kind.
+ */
+ k[NR_BTF_KINDS].info_sz = sizeof(struct btf_type);
+ write_raw_btf(btf_path, new_raw_btf, raw_size + sizeof(*k));
+
+ new_btf = btf__parse_raw(btf_path);
+ if (ASSERT_OK_PTR(new_btf, "btf__parse_raw")) {
+ ASSERT_EQ(btf__find_by_name_kind(new_btf, "test_lookup",
+ BTF_KIND_TYPEDEF), -ENOENT,
+ "verify_id_not_found");
+ /* id of "test_lookup2" will be id2 -1 as we have removed one type */
+ ASSERT_EQ(btf__find_by_name_kind(new_btf, "test_lookup2",
+ BTF_KIND_TYPEDEF), id2 - 1,
+ "verify_id_lookup2");
+
+ }
+ btf__free(new_btf);
+
+ /* next, change elem_sz to equal sizeof(struct btf_type)/2 and set
+ * vlen associated with unrecognized type to 2; this allows us to verify
+ * vlen-specified BTF can still be parsed.
+ */
+ k[NR_BTF_KINDS].info_sz = 0;
+ k[NR_BTF_KINDS].elem_sz = sizeof(struct btf_type)/2;
+ t->info |= 2;
+ write_raw_btf(btf_path, new_raw_btf, raw_size + sizeof(*k));
+
+ new_btf = btf__parse_raw(btf_path);
+ if (ASSERT_OK_PTR(new_btf, "btf__parse_raw")) {
+ ASSERT_EQ(btf__find_by_name_kind(new_btf, "test_lookup",
+ BTF_KIND_TYPEDEF), -ENOENT,
+ "verify_id_not_found");
+ /* id of "test_lookup2" will be id2 -1 as we have removed one type */
+ ASSERT_EQ(btf__find_by_name_kind(new_btf, "test_lookup2",
+ BTF_KIND_TYPEDEF), id2 - 1,
+ "verify_id_lookup2");
+
+ }
+ btf__free(new_btf);
+ free(new_raw_btf);
+ unlink(btf_path);
+ btf__free(btf);
+}
+
+void test_btf_kind(void)
+{
+ if (test__start_subtest("btf_kind_encoding"))
+ test_btf_kind_encoding();
+ if (test__start_subtest("btf_kind_decoding"))
+ test_btf_kind_decoding();
+}
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v7 bpf-next 08/10] bpftool: add BTF dump "format meta" to dump header/metadata
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
` (6 preceding siblings ...)
2025-12-11 16:46 ` [PATCH v7 bpf-next 07/10] selftests/bpf: test kind encoding/decoding Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 09/10] bpftool: Update doc to describe bpftool btf dump .. format metadata Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 10/10] kbuild, bpf: Specify "kind_layout" optional feature Alan Maguire
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
Provide a way to dump BTF metadata info via bpftool; this
consists of BTF size, header fields and kind layout info
(if available); for example
$ bpftool btf dump file vmlinux format meta
size 5460436
magic 0xeb9f
version 1
flags 0x0
hdr_len 32
type_len 3194640
type_off 0
str_len 2265721
str_off 3194640
kind_layout_len 40
kind_layout_off 5460364
kind 0 UNKNOWN info_sz 0 elem_sz 0
kind 1 INT info_sz 4 elem_sz 0
kind 2 PTR info_sz 0 elem_sz 0
kind 3 ARRAY info_sz 12 elem_sz 0
kind 4 STRUCT info_sz 0 elem_sz 12
kind 5 UNION info_sz 0 elem_sz 12
kind 6 ENUM info_sz 0 elem_sz 8
kind 7 FWD info_sz 0 elem_sz 0
kind 8 TYPEDEF info_sz 0 elem_sz 0
kind 9 VOLATILE info_sz 0 elem_sz 0
kind 10 CONST info_sz 0 elem_sz 0
kind 11 RESTRICT info_sz 0 elem_sz 0
kind 12 FUNC info_sz 0 elem_sz 0
kind 13 FUNC_PROTO info_sz 0 elem_sz 8
kind 14 VAR info_sz 4 elem_sz 0
kind 15 DATASEC info_sz 0 elem_sz 12
kind 16 FLOAT info_sz 0 elem_sz 0
kind 17 DECL_TAG info_sz 4 elem_sz 0
kind 18 TYPE_TAG info_sz 0 elem_sz 0
kind 19 ENUM64 info_sz 0 elem_sz 12
JSON output is also supported:
$ $ bpftool -p btf dump file /var/tmp/vmlinux.kind_layout format meta
{
"size": 5460436,
"header": {
"magic": 60319,
"version": 1,
"flags": 0,
"hdr_len": 32,
"type_len": 3194640,
"type_off": 0,
"str_len": 2265721,
"str_off": 3194640,
"kind_layout_len": 40,
"kind_layout_offset": 5460364
},
"kind_layouts": [{
"kind": 0,
"name": "UNKNOWN",
"info_sz": 0,
"elem_sz": 0
},{
"kind": 1,
"name": "INT",
"info_sz": 4,
"elem_sz": 0
},{
...
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/bpf/bpftool/bash-completion/bpftool | 2 +-
tools/bpf/bpftool/btf.c | 94 ++++++++++++++++++++++-
2 files changed, 92 insertions(+), 4 deletions(-)
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 53bcfeb1a76e..a331172cf8de 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -950,7 +950,7 @@ _bpftool()
return 0
;;
format)
- COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
+ COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) )
;;
root_id)
return 0;
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 946612029dee..fdb9b36fe106 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -835,6 +835,90 @@ static int dump_btf_c(const struct btf *btf,
return err;
}
+static int dump_btf_meta(const struct btf *btf)
+{
+ const struct btf_header *hdr;
+ const struct btf_kind_layout *k;
+ __u8 i, nr_kinds = 0;
+ const void *data;
+ __u32 data_sz;
+
+ data = btf__raw_data(btf, &data_sz);
+ if (!data)
+ return -ENOMEM;
+ hdr = data;
+ if (json_output) {
+ jsonw_start_object(json_wtr); /* metadata object */
+ jsonw_uint_field(json_wtr, "size", data_sz);
+ jsonw_name(json_wtr, "header");
+ jsonw_start_object(json_wtr); /* header object */
+ jsonw_uint_field(json_wtr, "magic", hdr->magic);
+ jsonw_uint_field(json_wtr, "version", hdr->version);
+ jsonw_uint_field(json_wtr, "flags", hdr->flags);
+ jsonw_uint_field(json_wtr, "hdr_len", hdr->hdr_len);
+ jsonw_uint_field(json_wtr, "type_len", hdr->type_len);
+ jsonw_uint_field(json_wtr, "type_off", hdr->type_off);
+ jsonw_uint_field(json_wtr, "str_len", hdr->str_len);
+ jsonw_uint_field(json_wtr, "str_off", hdr->str_off);
+ } else {
+ printf("size %-10u\n", data_sz);
+ printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10u\n",
+ hdr->magic, hdr->version, hdr->flags, hdr->hdr_len);
+ printf("type_len %-10u\ntype_off %-10u\n", hdr->type_len, hdr->type_off);
+ printf("str_len %-10u\nstr_off %-10u\n", hdr->str_len, hdr->str_off);
+ }
+
+ if (hdr->hdr_len < sizeof(struct btf_header)) {
+ if (json_output) {
+ jsonw_end_object(json_wtr); /* end header object */
+ jsonw_end_object(json_wtr); /* end metadata object */
+ }
+ return 0;
+ }
+
+ data_sz -= hdr->hdr_len;
+
+ if (hdr->kind_layout_len > 0 && hdr->kind_layout_off > 0) {
+ if (hdr->kind_layout_off + hdr->kind_layout_len <= data_sz) {
+ k = (void *)hdr + hdr->hdr_len + hdr->kind_layout_off;
+ nr_kinds = hdr->kind_layout_len / sizeof(*k);
+ }
+ }
+ if (json_output) {
+ jsonw_uint_field(json_wtr, "kind_layout_len", hdr->kind_layout_len);
+ jsonw_uint_field(json_wtr, "kind_layout_offset", hdr->kind_layout_off);
+ jsonw_end_object(json_wtr); /* end header object */
+
+ if (nr_kinds > 0) {
+ jsonw_name(json_wtr, "kind_layouts");
+ jsonw_start_array(json_wtr);
+ for (i = 0; i < nr_kinds; i++) {
+ jsonw_start_object(json_wtr);
+ jsonw_uint_field(json_wtr, "kind", i);
+ if (i < NR_BTF_KINDS)
+ jsonw_string_field(json_wtr, "name", btf_kind_str[i]);
+ else
+ jsonw_null_field(json_wtr, "name");
+ jsonw_uint_field(json_wtr, "info_sz", k[i].info_sz);
+ jsonw_uint_field(json_wtr, "elem_sz", k[i].elem_sz);
+ jsonw_end_object(json_wtr);
+ }
+ jsonw_end_array(json_wtr);
+ }
+ jsonw_end_object(json_wtr); /* end metadata object */
+ } else {
+ printf("kind_layout_len %-10u\nkind_layout_off %-10u\n",
+ hdr->kind_layout_len, hdr->kind_layout_off);
+ for (i = 0; i < nr_kinds; i++) {
+ printf("kind %-4d %-10s info_sz %-4d elem_sz %-4d\n",
+ i, i < NR_BTF_KINDS ? btf_kind_str[i] : "?",
+ k[i].info_sz, k[i].elem_sz);
+ }
+ }
+
+ return 0;
+}
+
static const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux";
static struct btf *get_vmlinux_btf_from_sysfs(void)
@@ -880,7 +964,7 @@ static bool btf_is_kernel_module(__u32 btf_id)
static int do_dump(int argc, char **argv)
{
- bool dump_c = false, sort_dump_c = true;
+ bool dump_c = false, sort_dump_c = true, dump_meta = false;
struct btf *btf = NULL, *base = NULL;
__u32 root_type_ids[MAX_ROOT_IDS];
bool have_id_filtering;
@@ -990,10 +1074,12 @@ static int do_dump(int argc, char **argv)
}
if (strcmp(*argv, "c") == 0) {
dump_c = true;
+ } else if (is_prefix(*argv, "meta")) {
+ dump_meta = true;
} else if (strcmp(*argv, "raw") == 0) {
dump_c = false;
} else {
- p_err("unrecognized format specifier: '%s', possible values: raw, c",
+ p_err("unrecognized format specifier: '%s', possible values: raw, c, meta",
*argv);
err = -EINVAL;
goto done;
@@ -1072,6 +1158,8 @@ static int do_dump(int argc, char **argv)
goto done;
}
err = dump_btf_c(btf, root_type_ids, root_type_cnt, sort_dump_c);
+ } else if (dump_meta) {
+ err = dump_btf_meta(btf);
} else {
err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
}
@@ -1446,7 +1534,7 @@ static int do_help(int argc, char **argv)
" %1$s %2$s help\n"
"\n"
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
- " FORMAT := { raw | c [unsorted] }\n"
+ " FORMAT := { raw | c [unsorted] | meta }\n"
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS " |\n"
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v7 bpf-next 09/10] bpftool: Update doc to describe bpftool btf dump .. format metadata
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
` (7 preceding siblings ...)
2025-12-11 16:46 ` [PATCH v7 bpf-next 08/10] bpftool: add BTF dump "format meta" to dump header/metadata Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
2025-12-11 16:46 ` [PATCH v7 bpf-next 10/10] kbuild, bpf: Specify "kind_layout" optional feature Alan Maguire
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
...and provide an example of output.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
.../bpf/bpftool/Documentation/bpftool-btf.rst | 28 +++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index d47dddc2b4ee..1c11b5647ab7 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -28,7 +28,7 @@ BTF COMMANDS
| **bpftool** **btf help**
|
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
-| *FORMAT* := { **raw** | **c** [**unsorted**] }
+| *FORMAT* := { **raw** | **c** [**unsorted**] | **meta** }
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
@@ -65,7 +65,8 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
**format** option can be used to override default (raw) output format. Raw
(**raw**) or C-syntax (**c**) output formats are supported. With C-style
formatting, the output is sorted by default. Use the **unsorted** option
- to avoid sorting the output.
+ to avoid sorting the output. BTF metadata can be displayed with the
+ **meta** option.
**root_id** option can be used to filter a dump to a single type and all
its dependent types. It cannot be used with any other types of filtering
@@ -267,3 +268,26 @@ All the standard ways to specify map or program are supported:
[104859] FUNC 'smbalert_work' type_id=9695 linkage=static
[104860] FUNC 'smbus_alert' type_id=71367 linkage=static
[104861] FUNC 'smbus_do_alert' type_id=84827 linkage=static
+
+Display BTF metadata from file vmlinux
+
+**# bpftool btf dump file vmlinux format meta**
+
+::
+
+ size 5161076
+ magic 0xeb9f
+ version 1
+ flags 0x1
+ hdr_len 40
+ type_len 3036368
+ type_off 0
+ str_len 2124588
+ str_off 3036368
+ kind_layout_len 80
+ kind_layout_off 5160956
+ kind 0 UNKNOWN info_sz 0 elem_sz 0
+ kind 1 INT info_sz 0 elem_sz 0
+ kind 2 PTR info_sz 0 elem_sz 0
+ kind 3 ARRAY info_sz 0 elem_sz 0
+ kind 4 STRUCT info_sz 0 elem_sz 0
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v7 bpf-next 10/10] kbuild, bpf: Specify "kind_layout" optional feature
2025-12-11 16:46 [PATCH v7 bpf-next 00/10] Add kind layout to BTF Alan Maguire
` (8 preceding siblings ...)
2025-12-11 16:46 ` [PATCH v7 bpf-next 09/10] bpftool: Update doc to describe bpftool btf dump .. format metadata Alan Maguire
@ 2025-12-11 16:46 ` Alan Maguire
9 siblings, 0 replies; 13+ messages in thread
From: Alan Maguire @ 2025-12-11 16:46 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, john.fastabend,
kpsingh, sdf, haoluo, jolsa, qmo, ihor.solodrai, dwarves, bpf,
ttreyer, Alan Maguire
The "kind_layout" feature will add metadata about BTF kinds to the
generated BTF; its absence in pahole will not trigger an error so it
is safe to add unconditionally as it will simply be ignored if pahole
does not support it.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
scripts/Makefile.btf | 2 ++
1 file changed, 2 insertions(+)
diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
index db76335dd917..c20f9bbcabeb 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -25,6 +25,8 @@ pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=enc
pahole-flags-$(call test-ge, $(pahole-ver), 130) += --btf_features=attributes
+pahole-flags-$(call test-ge, $(pahole-ver), 131) += --btf_features=kind_layout
+
ifneq ($(KBUILD_EXTMOD),)
module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base
endif
--
2.39.3
^ permalink raw reply related [flat|nested] 13+ messages in thread