* [PATCH v15 bpf-next 1/9] btf: Add BTF kind layout encoding to UAPI
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 2/9] libbpf: Support layout section handling in BTF Alan Maguire
` (7 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, 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
- a (currently unused) flags field
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 | 12 ++++++++++++
tools/include/uapi/linux/btf.h | 12 ++++++++++++
2 files changed, 24 insertions(+)
diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
index 266d4ffa6c07..638615ebddc2 100644
--- a/include/uapi/linux/btf.h
+++ b/include/uapi/linux/btf.h
@@ -8,6 +8,16 @@
#define BTF_MAGIC 0xeB9F
#define BTF_VERSION 1
+/*
+ * BTF layout section consists of a struct btf_layout for each known
+ * kind at BTF encoding time.
+ */
+struct btf_layout {
+ __u8 info_sz; /* size of singular element after btf_type */
+ __u8 elem_sz; /* size of each of btf_vlen(t) elements */
+ __u16 flags; /* currently unused */
+};
+
struct btf_header {
__u16 magic;
__u8 version;
@@ -19,6 +29,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 layout_off; /* offset of layout section */
+ __u32 layout_len; /* length of layout section */
};
/* Max # of type identifier */
diff --git a/tools/include/uapi/linux/btf.h b/tools/include/uapi/linux/btf.h
index 266d4ffa6c07..638615ebddc2 100644
--- a/tools/include/uapi/linux/btf.h
+++ b/tools/include/uapi/linux/btf.h
@@ -8,6 +8,16 @@
#define BTF_MAGIC 0xeB9F
#define BTF_VERSION 1
+/*
+ * BTF layout section consists of a struct btf_layout for each known
+ * kind at BTF encoding time.
+ */
+struct btf_layout {
+ __u8 info_sz; /* size of singular element after btf_type */
+ __u8 elem_sz; /* size of each of btf_vlen(t) elements */
+ __u16 flags; /* currently unused */
+};
+
struct btf_header {
__u16 magic;
__u8 version;
@@ -19,6 +29,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 layout_off; /* offset of layout section */
+ __u32 layout_len; /* length of layout section */
};
/* Max # of type identifier */
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v15 bpf-next 2/9] libbpf: Support layout section handling in BTF
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 1/9] btf: Add BTF kind layout encoding to UAPI Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 3/9] libbpf: Use layout to compute an unknown kind size Alan Maguire
` (6 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
Support reading in layout fixing endian issues on reading;
also support writing layout section to raw BTF object.
There is not yet an API to populate the layout with meaningful
information.
As part of this, we need to consider multiple valid BTF header
sizes; the original or the layout-extended headers.
So to support this, the "struct btf" representation is modified
to contain a "struct btf_header" and we copy the valid
portion from the raw data to it; this means we can always safely
check fields like btf->hdr.layout_len .
Note if parsed-in BTF has extra header information beyond
sizeof(struct btf_header) - if so we make that BTF ineligible
for modification by setting btf->has_hdr_extra .
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/lib/bpf/btf.c | 459 +++++++++++++++++++++++++++++---------------
1 file changed, 306 insertions(+), 153 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 40becc964368..9e7991498708 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, layout 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 | Optional layout | Strings |
+ * +--------------------------------------------------------+
+ * ^ ^ ^ ^
+ * | | | |
+ * raw_data | | |
+ * types_data-+ | |
+ * layout---------------+ |
+ * strs_data--------------------------------+
+ *
+ * A separate struct btf_header is embedded as 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 layout info, 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, optional
+ * layout and strings. layout optionally points to a
+ * btf_layout array - this allows us to encode information about
+ * the kinds known at encoding time. If layout is NULL no
+ * layout information is encoded.
*
- * +----------+ +---------+ +-----------+
- * | Header | | Types | | Strings |
- * +----------+ +---------+ +-----------+
- * ^ ^ ^
- * | | |
- * hdr | |
- * types_data----+ |
- * strset__data(strs_set)-----+
+ * +----------+ +---------+ +-----------+ +-----------+
+ * | Header | | Types | | Layout | | Strings |
+ * +----------+ +---------+ +-----------+ +-----------+
+ * ^ ^ ^ ^
+ * | | | |
+ * hdr | | |
+ * types_data----+ | |
+ * layout---------------------+ |
+ * strset__data(strs_set)---------------------+
*
- * +----------+---------+-----------+
- * | Header | Types | Strings |
- * raw_data----->+----------+---------+-----------+
+ * +----------+---------+-------------------+-----------+
+ * | Header | Types | Optional Layout | Strings |
+ * raw_data----->+----------+---------+-------------------+-----------+
*/
- struct btf_header *hdr;
+ struct btf_header hdr;
void *types_data;
size_t types_data_cap; /* used size stored in hdr->type_len */
@@ -125,6 +136,17 @@ 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;
+ /* does BTF have header information we do not support? If so, disallow
+ * modification.
+ */
+ bool has_hdr_extra;
+ /* Points either at raw kind layout data in parsed BTF (if present), or
+ * at an allocated kind layout array when BTF is modifiable.
+ */
+ void *layout;
+
/* BTF object FD, if loaded into kernel */
int fd;
@@ -216,7 +238,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);
@@ -224,66 +246,115 @@ 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->layout_off = bswap_32(h->layout_off);
+ h->layout_len = bswap_32(h->layout_len);
+ }
}
static int btf_parse_hdr(struct btf *btf)
{
- struct btf_header *hdr = btf->hdr;
- __u32 meta_left;
+ struct btf_header *hdr = btf->raw_data;
+ __u32 hdr_len, 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;
}
+ hdr_len = hdr->hdr_len;
+
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) {
+ if (btf->swapped_endian)
+ btf_bswap_hdr(hdr, hdr_len);
+
+ memcpy(&btf->hdr, hdr, min((size_t)hdr_len, sizeof(struct btf_header)));
+
+ /* If unknown header data is found, modification is prohibited in
+ * btf_ensure_modifiable().
+ */
+ if (hdr_len > sizeof(struct btf_header)) {
+ __u8 *h = (__u8 *)hdr;
+ __u32 i;
+
+ for (i = sizeof(struct btf_header); i < hdr_len; i++) {
+ if (!h[i])
+ continue;
+ btf->has_hdr_extra = true;
+ pr_debug("Unknown BTF header data at offset %u; modification is disallowed\n",
+ i);
+ break;
+ }
+ }
+
+ 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.layout_len == 0)
+ return 0;
+
+ /* optional layout section sits between types and strings */
+ if (btf->hdr.layout_off % 4) {
+ pr_debug("BTF layout section is not aligned to 4 bytes\n");
+ return -EINVAL;
+ }
+ if (btf->hdr.layout_off < (long long)btf->hdr.type_off + btf->hdr.type_len) {
+ pr_debug("Invalid BTF data sections layout: type data at %u + %u, layout data at %u + %u\n",
+ btf->hdr.type_off, btf->hdr.type_len,
+ btf->hdr.layout_off, btf->hdr.layout_len);
+ return -EINVAL;
+ }
+ if ((long long)btf->hdr.layout_off + btf->hdr.layout_len > btf->hdr.str_off ||
+ btf->hdr.layout_off > btf->hdr.str_off) {
+ pr_debug("Invalid BTF data sections layout: layout data at %u + %u, strings data at %u\n",
+ btf->hdr.layout_off, btf->hdr.layout_len, btf->hdr.str_off);
+ return -EINVAL;
+ }
return 0;
}
static int btf_parse_str_sec(struct btf *btf)
{
- const struct btf_header *hdr = btf->hdr;
const char *start = btf->strs_data;
- const char *end = start + btf->hdr->str_len;
+ const char *end = start + btf->hdr.str_len;
- if (btf->base_btf && hdr->str_len == 0)
+ if (btf->base_btf && btf->hdr.str_len == 0)
return 0;
- if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_STR_OFFSET || end[-1]) {
+ if (!btf->hdr.str_len || btf->hdr.str_len - 1 > BTF_MAX_STR_OFFSET || end[-1]) {
pr_debug("Invalid BTF string section\n");
return -EINVAL;
}
@@ -294,6 +365,20 @@ static int btf_parse_str_sec(struct btf *btf)
return 0;
}
+static int btf_parse_layout_sec(struct btf *btf)
+{
+ if (!btf->hdr.layout_len)
+ return 0;
+
+ if (btf->hdr.layout_len % sizeof(struct btf_layout) != 0) {
+ pr_debug("Invalid BTF kind layout section\n");
+ return -EINVAL;
+ }
+ btf->layout = btf->raw_data + btf->hdr.hdr_len + btf->hdr.layout_off;
+
+ return 0;
+}
+
static int btf_type_size(const struct btf_type *t)
{
const int base_size = sizeof(struct btf_type);
@@ -421,9 +506,8 @@ static int btf_bswap_type_rest(struct btf_type *t)
static int btf_parse_type_sec(struct btf *btf)
{
- struct btf_header *hdr = btf->hdr;
void *next_type = btf->types_data;
- void *end_type = next_type + hdr->type_len;
+ void *end_type = next_type + btf->hdr.type_len;
int err, type_size;
while (next_type + sizeof(struct btf_type) <= end_type) {
@@ -1012,7 +1096,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)
@@ -1036,14 +1121,14 @@ void btf__free(struct btf *btf)
if (btf_is_modifiable(btf)) {
/* if BTF was modified after loading, it will have a split
- * in-memory representation for header, types, and strings
+ * in-memory representation for types, strings and layout
* sections, so we need to free all of them individually. It
* might still have a cached contiguous raw data present,
* which will be unconditionally freed below.
*/
- free(btf->hdr);
free(btf->types_data);
strset__free(btf->strs_set);
+ free(btf->layout);
}
btf_free_raw_data(btf);
free(btf->raw_data_swapped);
@@ -1055,6 +1140,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));
@@ -1072,7 +1158,7 @@ static struct btf *btf_new_empty(struct btf *base_btf)
if (base_btf) {
btf->base_btf = base_btf;
btf->start_id = btf__type_cnt(base_btf);
- btf->start_str_off = base_btf->hdr->str_len + base_btf->start_str_off;
+ btf->start_str_off = base_btf->hdr.str_len + base_btf->start_str_off;
btf->swapped_endian = base_btf->swapped_endian;
}
@@ -1084,14 +1170,15 @@ 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 */
+ memcpy(&btf->hdr, hdr, sizeof(*hdr));
return btf;
}
@@ -1124,7 +1211,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, b
if (base_btf) {
btf->base_btf = base_btf;
btf->start_id = btf__type_cnt(base_btf);
- btf->start_str_off = base_btf->hdr->str_len + base_btf->start_str_off;
+ btf->start_str_off = base_btf->hdr.str_len + base_btf->start_str_off;
}
if (is_mmap) {
@@ -1141,15 +1228,15 @@ 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;
- btf->strs_data = btf->raw_data + btf->hdr->hdr_len + btf->hdr->str_off;
- btf->types_data = btf->raw_data + btf->hdr->hdr_len + btf->hdr->type_off;
+ btf->strs_data = btf->raw_data + btf->hdr.hdr_len + btf->hdr.str_off;
+ btf->types_data = btf->raw_data + btf->hdr.hdr_len + btf->hdr.type_off;
err = btf_parse_str_sec(btf);
+ err = err ?: btf_parse_layout_sec(btf);
err = err ?: btf_parse_type_sec(btf);
err = err ?: btf_sanity_check(btf);
if (err)
@@ -1601,7 +1688,7 @@ static const void *btf_strs_data(const struct btf *btf)
static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endian)
{
- struct btf_header *hdr = btf->hdr;
+ const struct btf_header *hdr = &btf->hdr;
struct btf_type *t;
void *data, *p;
__u32 data_sz;
@@ -1614,14 +1701,17 @@ 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->layout)
+ data_sz += hdr->layout_len;
+
data = calloc(1, data_sz);
if (!data)
return NULL;
p = data;
- memcpy(p, hdr, hdr->hdr_len);
+ memcpy(p, hdr, min((__u32)sizeof(struct btf_header), 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);
@@ -1639,8 +1729,12 @@ static void *btf_get_raw_data(const struct btf *btf, __u32 *size, bool swap_endi
}
p += hdr->type_len;
+ if (btf->layout) {
+ memcpy(p, btf->layout, hdr->layout_len);
+ p += hdr->layout_len;
+ }
+
memcpy(p, btf_strs_data(btf), hdr->str_len);
- p += hdr->str_len;
*size = data_sz;
return data;
@@ -1675,7 +1769,7 @@ const char *btf__str_by_offset(const struct btf *btf, __u32 offset)
{
if (offset < btf->start_str_off)
return btf__str_by_offset(btf->base_btf, offset);
- else if (offset - btf->start_str_off < btf->hdr->str_len)
+ else if (offset - btf->start_str_off < btf->hdr.str_len)
return btf_strs_data(btf) + (offset - btf->start_str_off);
else
return errno = EINVAL, NULL;
@@ -1783,12 +1877,12 @@ 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
+ * regions for types, strings and layout. Also invalidate cached
* raw_data, if any.
*/
static int btf_ensure_modifiable(struct btf *btf)
{
- void *hdr, *types;
+ void *types, *layout = NULL;
struct strset *set = NULL;
int err = -ENOMEM;
@@ -1798,45 +1892,58 @@ static int btf_ensure_modifiable(struct btf *btf)
return 0;
}
- /* split raw data into three memory regions */
- hdr = malloc(btf->hdr->hdr_len);
- types = malloc(btf->hdr->type_len);
- if (!hdr || !types)
+ if (btf->has_hdr_extra) {
+ /* Additional BTF header data was found; not safe to modify. */
+ return -EOPNOTSUPP;
+ }
+
+ /* split raw data into memory regions; btf->hdr is done already. */
+ types = malloc(btf->hdr.type_len);
+ if (!types)
goto err_out;
+ memcpy(types, btf->types_data, btf->hdr.type_len);
- memcpy(hdr, btf->hdr, btf->hdr->hdr_len);
- memcpy(types, btf->types_data, btf->hdr->type_len);
+ if (btf->hdr.layout_len) {
+ layout = malloc(btf->hdr.layout_len);
+ if (!layout)
+ goto err_out;
+ memcpy(layout, btf->raw_data + btf->hdr.hdr_len + btf->hdr.layout_off,
+ btf->hdr.layout_len);
+ }
/* build lookup index for all strings */
- set = strset__new(BTF_MAX_STR_OFFSET, btf->strs_data, btf->hdr->str_len);
+ set = strset__new(BTF_MAX_STR_OFFSET, btf->strs_data, btf->hdr.str_len);
if (IS_ERR(set)) {
err = PTR_ERR(set);
goto err_out;
}
/* only when everything was successful, update internal state */
- btf->hdr = hdr;
btf->types_data = types;
- btf->types_data_cap = btf->hdr->type_len;
+ btf->types_data_cap = btf->hdr.type_len;
btf->strs_data = NULL;
btf->strs_set = set;
+ if (layout)
+ btf->layout = layout;
/* if BTF was created from scratch, all strings are guaranteed to be
* unique and deduplicated
*/
- if (btf->hdr->str_len == 0)
+ if (btf->hdr.str_len == 0)
btf->strs_deduped = true;
- if (!btf->base_btf && btf->hdr->str_len == 1)
+ if (!btf->base_btf && btf->hdr.str_len == 1)
btf->strs_deduped = true;
/* invalidate raw_data representation */
btf_invalidate_raw_data(btf);
+ btf->modifiable = true;
+
return 0;
err_out:
strset__free(set);
- free(hdr);
free(types);
+ free(layout);
return err;
}
@@ -1849,6 +1956,7 @@ static int btf_ensure_modifiable(struct btf *btf)
int btf__find_str(struct btf *btf, const char *s)
{
int off;
+ int err;
if (btf->base_btf) {
off = btf__find_str(btf->base_btf, s);
@@ -1857,8 +1965,9 @@ int btf__find_str(struct btf *btf, const char *s)
}
/* BTF needs to be in a modifiable state to build string lookup index */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
off = strset__find_str(btf->strs_set, s);
if (off < 0)
@@ -1875,6 +1984,7 @@ int btf__find_str(struct btf *btf, const char *s)
int btf__add_str(struct btf *btf, const char *s)
{
int off;
+ int err;
if (btf->base_btf) {
off = btf__find_str(btf->base_btf, s);
@@ -1882,14 +1992,15 @@ int btf__add_str(struct btf *btf, const char *s)
return off;
}
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
off = strset__add_str(btf->strs_set, s);
if (off < 0)
return libbpf_err(off);
- btf->hdr->str_len = strset__data_size(btf->strs_set);
+ btf->hdr.str_len = strset__data_size(btf->strs_set);
return btf->start_str_off + off;
}
@@ -1897,7 +2008,7 @@ int btf__add_str(struct btf *btf, const char *s)
static void *btf_add_type_mem(struct btf *btf, size_t add_sz)
{
return libbpf_add_mem(&btf->types_data, &btf->types_data_cap, 1,
- btf->hdr->type_len, UINT_MAX, add_sz);
+ btf->hdr.type_len, UINT_MAX, add_sz);
}
static void btf_type_inc_vlen(struct btf_type *t)
@@ -1905,16 +2016,31 @@ static void btf_type_inc_vlen(struct btf_type *t)
t->info = btf_type_info(btf_kind(t), btf_vlen(t) + 1, btf_kflag(t));
}
+static void btf_hdr_update_type_len(struct btf *btf, int new_len)
+{
+ btf->hdr.type_len = new_len;
+ if (btf->layout) {
+ btf->hdr.layout_off = btf->hdr.type_off + new_len;
+ btf->hdr.str_off = btf->hdr.layout_off + btf->hdr.layout_len;
+ } else {
+ btf->hdr.str_off = btf->hdr.type_off + new_len;
+ }
+}
+
+static void btf_hdr_update_str_len(struct btf *btf, int new_len)
+{
+ btf->hdr.str_len = new_len;
+}
+
static int btf_commit_type(struct btf *btf, int data_sz)
{
int err;
- err = btf_add_type_idx_entry(btf, btf->hdr->type_len);
+ err = btf_add_type_idx_entry(btf, btf->hdr.type_len);
if (err)
return libbpf_err(err);
- btf->hdr->type_len += data_sz;
- btf->hdr->str_off += data_sz;
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + data_sz);
btf->nr_types++;
return btf->start_id + btf->nr_types - 1;
}
@@ -1968,8 +2094,9 @@ static int btf_add_type(struct btf_pipe *p, const struct btf_type *src_type)
return libbpf_err(sz);
/* deconstruct BTF, if necessary, and invalidate raw_data */
- if (btf_ensure_modifiable(p->dst))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(p->dst);
+ if (err)
+ return libbpf_err(err);
t = btf_add_type_mem(p->dst, sz);
if (!t)
@@ -2018,15 +2145,16 @@ int btf__add_btf(struct btf *btf, const struct btf *src_btf)
src_start_id = src_btf->base_btf ? btf__type_cnt(src_btf->base_btf) : 1;
/* deconstruct BTF, if necessary, and invalidate raw_data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
/* remember original strings section size if we have to roll back
* partial strings section changes
*/
- old_strs_len = btf->hdr->str_len;
+ old_strs_len = btf->hdr.str_len;
- data_sz = src_btf->hdr->type_len;
+ data_sz = src_btf->hdr.type_len;
cnt = src_btf->nr_types;
/* pre-allocate enough memory for new types */
@@ -2103,8 +2231,7 @@ int btf__add_btf(struct btf *btf, const struct btf *src_btf)
* update type count and various internal offsets and sizes to
* "commit" the changes and made them visible to the outside world.
*/
- btf->hdr->type_len += data_sz;
- btf->hdr->str_off += data_sz;
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + data_sz);
btf->nr_types += cnt;
hashmap__free(p.str_off_map);
@@ -2115,13 +2242,13 @@ int btf__add_btf(struct btf *btf, const struct btf *src_btf)
/* zero out preallocated memory as if it was just allocated with
* libbpf_add_mem()
*/
- memset(btf->types_data + btf->hdr->type_len, 0, data_sz);
- memset(btf->strs_data + old_strs_len, 0, btf->hdr->str_len - old_strs_len);
+ memset(btf->types_data + btf->hdr.type_len, 0, data_sz);
+ memset(btf->strs_data + old_strs_len, 0, btf->hdr.str_len - old_strs_len);
/* and now restore original strings section size; types data size
* wasn't modified, so doesn't need restoring, see big comment above
*/
- btf->hdr->str_len = old_strs_len;
+ btf_hdr_update_str_len(btf, old_strs_len);
hashmap__free(p.str_off_map);
@@ -2141,6 +2268,7 @@ int btf__add_int(struct btf *btf, const char *name, size_t byte_sz, int encoding
{
struct btf_type *t;
int sz, name_off;
+ int err;
/* non-empty name */
if (str_is_empty(name))
@@ -2152,8 +2280,9 @@ int btf__add_int(struct btf *btf, const char *name, size_t byte_sz, int encoding
return libbpf_err(-EINVAL);
/* deconstruct BTF, if necessary, and invalidate raw_data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type) + sizeof(int);
t = btf_add_type_mem(btf, sz);
@@ -2189,6 +2318,7 @@ int btf__add_float(struct btf *btf, const char *name, size_t byte_sz)
{
struct btf_type *t;
int sz, name_off;
+ int err;
/* non-empty name */
if (str_is_empty(name))
@@ -2199,8 +2329,9 @@ int btf__add_float(struct btf *btf, const char *name, size_t byte_sz)
byte_sz != 16)
return libbpf_err(-EINVAL);
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz);
@@ -2234,12 +2365,14 @@ static int btf_add_ref_kind(struct btf *btf, int kind, const char *name, int ref
{
struct btf_type *t;
int sz, name_off = 0;
+ int err;
if (validate_type_id(ref_type_id))
return libbpf_err(-EINVAL);
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz);
@@ -2284,13 +2417,15 @@ int btf__add_array(struct btf *btf, int index_type_id, int elem_type_id, __u32 n
{
struct btf_type *t;
struct btf_array *a;
+ int err;
int sz;
if (validate_type_id(index_type_id) || validate_type_id(elem_type_id))
return libbpf_err(-EINVAL);
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type) + sizeof(struct btf_array);
t = btf_add_type_mem(btf, sz);
@@ -2314,9 +2449,11 @@ static int btf_add_composite(struct btf *btf, int kind, const char *name, __u32
{
struct btf_type *t;
int sz, name_off = 0;
+ int err;
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz);
@@ -2396,6 +2533,7 @@ int btf__add_field(struct btf *btf, const char *name, int type_id,
struct btf_member *m;
bool is_bitfield;
int sz, name_off = 0;
+ int err;
/* last type should be union/struct */
if (btf->nr_types == 0)
@@ -2416,8 +2554,9 @@ int btf__add_field(struct btf *btf, const char *name, int type_id,
return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_member);
m = btf_add_type_mem(btf, sz);
@@ -2439,8 +2578,7 @@ int btf__add_field(struct btf *btf, const char *name, int type_id,
/* update parent type's vlen and kflag */
t->info = btf_type_info(btf_kind(t), btf_vlen(t) + 1, is_bitfield || btf_kflag(t));
- btf->hdr->type_len += sz;
- btf->hdr->str_off += sz;
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
return 0;
}
@@ -2449,13 +2587,15 @@ static int btf_add_enum_common(struct btf *btf, const char *name, __u32 byte_sz,
{
struct btf_type *t;
int sz, name_off = 0;
+ int err;
/* byte_sz must be power of 2 */
if (!byte_sz || (byte_sz & (byte_sz - 1)) || byte_sz > 8)
return libbpf_err(-EINVAL);
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz);
@@ -2511,6 +2651,7 @@ int btf__add_enum_value(struct btf *btf, const char *name, __s64 value)
struct btf_type *t;
struct btf_enum *v;
int sz, name_off;
+ int err;
/* last type should be BTF_KIND_ENUM */
if (btf->nr_types == 0)
@@ -2526,8 +2667,9 @@ int btf__add_enum_value(struct btf *btf, const char *name, __s64 value)
return libbpf_err(-E2BIG);
/* decompose and invalidate raw data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_enum);
v = btf_add_type_mem(btf, sz);
@@ -2549,8 +2691,7 @@ int btf__add_enum_value(struct btf *btf, const char *name, __s64 value)
if (value < 0)
t->info = btf_type_info(btf_kind(t), btf_vlen(t), true);
- btf->hdr->type_len += sz;
- btf->hdr->str_off += sz;
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
return 0;
}
@@ -2588,6 +2729,7 @@ int btf__add_enum64_value(struct btf *btf, const char *name, __u64 value)
struct btf_enum64 *v;
struct btf_type *t;
int sz, name_off;
+ int err;
/* last type should be BTF_KIND_ENUM64 */
if (btf->nr_types == 0)
@@ -2601,8 +2743,9 @@ int btf__add_enum64_value(struct btf *btf, const char *name, __u64 value)
return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_enum64);
v = btf_add_type_mem(btf, sz);
@@ -2621,8 +2764,7 @@ int btf__add_enum64_value(struct btf *btf, const char *name, __u64 value)
t = btf_last_type(btf);
btf_type_inc_vlen(t);
- btf->hdr->type_len += sz;
- btf->hdr->str_off += sz;
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
return 0;
}
@@ -2791,13 +2933,15 @@ int btf__add_func(struct btf *btf, const char *name,
int btf__add_func_proto(struct btf *btf, int ret_type_id)
{
struct btf_type *t;
+ int err;
int sz;
if (validate_type_id(ret_type_id))
return libbpf_err(-EINVAL);
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz);
@@ -2827,6 +2971,7 @@ int btf__add_func_param(struct btf *btf, const char *name, int type_id)
struct btf_type *t;
struct btf_param *p;
int sz, name_off = 0;
+ int err;
if (validate_type_id(type_id))
return libbpf_err(-EINVAL);
@@ -2839,8 +2984,9 @@ int btf__add_func_param(struct btf *btf, const char *name, int type_id)
return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_param);
p = btf_add_type_mem(btf, sz);
@@ -2860,8 +3006,7 @@ int btf__add_func_param(struct btf *btf, const char *name, int type_id)
t = btf_last_type(btf);
btf_type_inc_vlen(t);
- btf->hdr->type_len += sz;
- btf->hdr->str_off += sz;
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
return 0;
}
@@ -2880,6 +3025,7 @@ int btf__add_var(struct btf *btf, const char *name, int linkage, int type_id)
struct btf_type *t;
struct btf_var *v;
int sz, name_off;
+ int err;
/* non-empty name */
if (str_is_empty(name))
@@ -2891,8 +3037,9 @@ int btf__add_var(struct btf *btf, const char *name, int linkage, int type_id)
return libbpf_err(-EINVAL);
/* deconstruct BTF, if necessary, and invalidate raw_data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type) + sizeof(struct btf_var);
t = btf_add_type_mem(btf, sz);
@@ -2929,13 +3076,15 @@ int btf__add_datasec(struct btf *btf, const char *name, __u32 byte_sz)
{
struct btf_type *t;
int sz, name_off;
+ int err;
/* non-empty name */
if (str_is_empty(name))
return libbpf_err(-EINVAL);
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz);
@@ -2968,6 +3117,7 @@ int btf__add_datasec_var_info(struct btf *btf, int var_type_id, __u32 offset, __
{
struct btf_type *t;
struct btf_var_secinfo *v;
+ int err;
int sz;
/* last type should be BTF_KIND_DATASEC */
@@ -2981,8 +3131,9 @@ int btf__add_datasec_var_info(struct btf *btf, int var_type_id, __u32 offset, __
return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_var_secinfo);
v = btf_add_type_mem(btf, sz);
@@ -2997,8 +3148,7 @@ int btf__add_datasec_var_info(struct btf *btf, int var_type_id, __u32 offset, __
t = btf_last_type(btf);
btf_type_inc_vlen(t);
- btf->hdr->type_len += sz;
- btf->hdr->str_off += sz;
+ btf_hdr_update_type_len(btf, btf->hdr.type_len + sz);
return 0;
}
@@ -3007,6 +3157,7 @@ static int btf_add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
{
struct btf_type *t;
int sz, value_off;
+ int err;
if (str_is_empty(value) || component_idx < -1)
return libbpf_err(-EINVAL);
@@ -3014,8 +3165,9 @@ static int btf_add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
if (validate_type_id(ref_type_id))
return libbpf_err(-EINVAL);
- if (btf_ensure_modifiable(btf))
- return libbpf_err(-ENOMEM);
+ err = btf_ensure_modifiable(btf);
+ if (err)
+ return libbpf_err(err);
sz = sizeof(struct btf_type) + sizeof(struct btf_decl_tag);
t = btf_add_type_mem(btf, sz);
@@ -3639,10 +3791,9 @@ int btf__dedup(struct btf *btf, const struct btf_dedup_opts *opts)
return libbpf_err(-EINVAL);
}
- if (btf_ensure_modifiable(btf)) {
- err = -ENOMEM;
+ err = btf_ensure_modifiable(btf);
+ if (err)
goto done;
- }
err = btf_dedup_prep(d);
if (err) {
@@ -3962,7 +4113,7 @@ static int btf_dedup_strings(struct btf_dedup *d)
/* replace BTF string data and hash with deduped ones */
strset__free(d->btf->strs_set);
- d->btf->hdr->str_len = strset__data_size(d->strs_set);
+ btf_hdr_update_str_len(d->btf, strset__data_size(d->strs_set));
d->btf->strs_set = d->strs_set;
d->strs_set = NULL;
d->btf->strs_deduped = true;
@@ -5409,14 +5560,17 @@ static int btf_dedup_compact_types(struct btf_dedup *d)
/* shrink struct btf's internal types index and update btf_header */
d->btf->nr_types = next_type_id - d->btf->start_id;
d->btf->type_offs_cap = d->btf->nr_types;
- d->btf->hdr->type_len = p - d->btf->types_data;
+ d->btf->hdr.type_len = p - d->btf->types_data;
new_offs = libbpf_reallocarray(d->btf->type_offs, d->btf->type_offs_cap,
sizeof(*new_offs));
if (d->btf->type_offs_cap && !new_offs)
return -ENOMEM;
d->btf->type_offs = new_offs;
- d->btf->hdr->str_off = d->btf->hdr->type_len;
- d->btf->raw_size = d->btf->hdr->hdr_len + d->btf->hdr->type_len + d->btf->hdr->str_len;
+ if (d->btf->layout)
+ d->btf->hdr.layout_off = d->btf->hdr.type_off + d->btf->hdr.type_len;
+ d->btf->hdr.str_off = d->btf->hdr.type_off + d->btf->hdr.type_len + d->btf->hdr.layout_len;
+ d->btf->raw_size = d->btf->hdr.hdr_len + d->btf->hdr.type_off + d->btf->hdr.type_len +
+ d->btf->hdr.layout_len + d->btf->hdr.str_len;
return 0;
}
@@ -5874,7 +6028,7 @@ int btf__distill_base(const struct btf *src_btf, struct btf **new_base_btf,
goto done;
}
dist.split_start_id = btf__type_cnt(old_base);
- dist.split_start_str = old_base->hdr->str_len;
+ dist.split_start_str = old_base->hdr.str_len;
/* Pass over src split BTF; generate the list of base BTF type ids it
* references; these will constitute our distilled BTF set to be
@@ -5943,14 +6097,14 @@ int btf__distill_base(const struct btf *src_btf, struct btf **new_base_btf,
const struct btf_header *btf_header(const struct btf *btf)
{
- return btf->hdr;
+ return &btf->hdr;
}
void btf_set_base_btf(struct btf *btf, const struct btf *base_btf)
{
btf->base_btf = (struct btf *)base_btf;
btf->start_id = btf__type_cnt(base_btf);
- btf->start_str_off = base_btf->hdr->str_len + base_btf->start_str_off;
+ btf->start_str_off = base_btf->hdr.str_len + base_btf->start_str_off;
}
int btf__relocate(struct btf *btf, const struct btf *base_btf)
@@ -6017,16 +6171,15 @@ int btf__permute(struct btf *btf, __u32 *id_map, __u32 id_map_cnt,
goto done;
}
- new_types = calloc(btf->hdr->type_len, 1);
+ new_types = calloc(btf->hdr.type_len, 1);
if (!new_types) {
err = -ENOMEM;
goto done;
}
- if (btf_ensure_modifiable(btf)) {
- err = -ENOMEM;
+ err = btf_ensure_modifiable(btf);
+ if (err)
goto done;
- }
for (i = start_offs; i < id_map_cnt; i++) {
id = id_map[i];
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v15 bpf-next 3/9] libbpf: Use layout to compute an unknown kind size
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 1/9] btf: Add BTF kind layout encoding to UAPI Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 2/9] libbpf: Support layout section handling in BTF Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 4/9] libbpf: Add layout encoding support Alan Maguire
` (5 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
This allows BTF parsing to proceed even if we do not know the
kind. Fall back to base BTF layout if layout information is
not in split BTF.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/lib/bpf/btf.c | 42 +++++++++++++++++++++++++++++++++---------
1 file changed, 33 insertions(+), 9 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 9e7991498708..f4e3785179dd 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -379,7 +379,32 @@ static int btf_parse_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)
+{
+ __u32 layout_kinds = btf->hdr.layout_len / sizeof(struct btf_layout);
+ struct btf_layout *l = btf->layout;
+ __u16 vlen = btf_vlen(t);
+ __u32 kind = btf_kind(t);
+
+ /* Fall back to base BTF if needed as they share layout information */
+ if (!l) {
+ struct btf *base_btf = btf->base_btf;
+
+ if (base_btf) {
+ l = base_btf->layout;
+ layout_kinds = base_btf->hdr.layout_len / sizeof(struct btf_layout);
+ }
+ }
+ if (!l || kind >= layout_kinds) {
+ pr_debug("Unsupported BTF_KIND: %u\n", btf_kind(t));
+ return -EINVAL;
+ }
+
+ return sizeof(struct btf_type) + l[kind].info_sz + vlen * l[kind].elem_sz;
+}
+
+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);
@@ -415,8 +440,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);
}
}
@@ -514,7 +538,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) {
@@ -2089,7 +2113,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);
@@ -2179,7 +2203,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;
@@ -5546,7 +5570,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;
@@ -6208,7 +6232,7 @@ int btf__permute(struct btf *btf, __u32 *id_map, __u32 id_map_cnt,
id = order_map[i];
t = btf__type_by_id(btf, id);
- type_size = btf_type_size(t);
+ type_size = btf_type_size(btf, t);
memcpy(nt, t, type_size);
/* fix up referenced IDs for BTF */
@@ -6234,7 +6258,7 @@ int btf__permute(struct btf *btf, __u32 *id_map, __u32 id_map_cnt,
for (nt = new_types, i = 0; i < id_map_cnt - start_offs; i++) {
btf->type_offs[i] = nt - new_types;
- nt += btf_type_size(nt);
+ nt += btf_type_size(btf, nt);
}
free(order_map);
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v15 bpf-next 4/9] libbpf: Add layout encoding support
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
` (2 preceding siblings ...)
2026-03-24 17:44 ` [PATCH v15 bpf-next 3/9] libbpf: Use layout to compute an unknown kind size Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 5/9] libbpf: BTF validation can use layout for unknown kinds Alan Maguire
` (4 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
Support encoding of BTF layout data via btf__new_empty_opts().
Current supported opts are base_btf and add_layout.
Layout information is maintained in btf.c in the layouts[] array;
when BTF is created with the add_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 | 59 ++++++++++++++++++++++++++++++++++++++--
tools/lib/bpf/btf.h | 20 ++++++++++++++
tools/lib/bpf/libbpf.map | 2 ++
3 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index f4e3785179dd..ef4145652a08 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -29,6 +29,36 @@
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.
+ */
+static struct btf_layout layouts[NR_BTF_KINDS] = {
+/* singular element size vlen element(s) size flags */
+[BTF_KIND_UNKN] = { 0, 0, 0 },
+[BTF_KIND_INT] = { sizeof(__u32), 0, 0 },
+[BTF_KIND_PTR] = { 0, 0, 0 },
+[BTF_KIND_ARRAY] = { sizeof(struct btf_array), 0, 0 },
+[BTF_KIND_STRUCT] = { 0, sizeof(struct btf_member), 0 },
+[BTF_KIND_UNION] = { 0, sizeof(struct btf_member), 0 },
+[BTF_KIND_ENUM] = { 0, sizeof(struct btf_enum), 0 },
+[BTF_KIND_FWD] = { 0, 0, 0 },
+[BTF_KIND_TYPEDEF] = { 0, 0, 0 },
+[BTF_KIND_VOLATILE] = { 0, 0, 0 },
+[BTF_KIND_CONST] = { 0, 0, 0 },
+[BTF_KIND_RESTRICT] = { 0, 0, 0 },
+[BTF_KIND_FUNC] = { 0, 0, 0 },
+[BTF_KIND_FUNC_PROTO] = { 0, sizeof(struct btf_param), 0 },
+[BTF_KIND_VAR] = { sizeof(struct btf_var), 0, 0 },
+[BTF_KIND_DATASEC] = { 0, sizeof(struct btf_var_secinfo), 0 },
+[BTF_KIND_FLOAT] = { 0, 0, 0 },
+[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 },
+};
+
struct btf {
/* raw BTF data in native endianness */
void *raw_data;
@@ -1162,8 +1192,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_layout = OPTS_GET(opts, add_layout, false);
+ struct btf *base_btf = OPTS_GET(opts, base_btf, NULL);
struct btf_header *hdr;
struct btf *btf;
@@ -1188,6 +1220,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_layout)
+ btf->raw_size += sizeof(layouts);
btf->raw_data = calloc(1, btf->raw_size);
if (!btf->raw_data) {
free(btf);
@@ -1202,6 +1236,15 @@ static struct btf *btf_new_empty(struct btf *base_btf)
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 */
+
+ if (add_layout) {
+ hdr->layout_len = sizeof(layouts);
+ btf->layout = btf->raw_data + hdr->hdr_len;
+ memcpy(btf->layout, layouts, sizeof(layouts));
+ btf->strs_data += sizeof(layouts);
+ hdr->str_off += sizeof(layouts);
+ }
+
memcpy(&btf->hdr, hdr, sizeof(*hdr));
return btf;
@@ -1214,7 +1257,19 @@ struct btf *btf__new_empty(void)
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 b30008c267c0..a1f8deca2603 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_layout; /* add BTF layout information */
+ size_t:0;
+};
+#define btf_new_opts__last_field add_layout
+
+/**
+ * @brief **btf__new_empty_opts()** creates an unpopulated BTF object with
+ * optional *base_btf* and BTF kind layout description if *add_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 3526c937bdbe..61afa58caaf3 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -457,4 +457,6 @@ LIBBPF_1.7.0 {
} LIBBPF_1.6.0;
LIBBPF_1.8.0 {
+ global:
+ btf__new_empty_opts;
} LIBBPF_1.7.0;
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v15 bpf-next 5/9] libbpf: BTF validation can use layout for unknown kinds
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
` (3 preceding siblings ...)
2026-03-24 17:44 ` [PATCH v15 bpf-next 4/9] libbpf: Add layout encoding support Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 6/9] libbpf: Support sanitization of BTF layout for older kernels Alan Maguire
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
BTF parsing can use layout to navigate unknown kinds, so
btf_validate_type() should take 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 | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index ef4145652a08..7743ffaf4306 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -412,7 +412,7 @@ static int btf_parse_layout_sec(struct btf *btf)
/* for unknown kinds, consult kind layout. */
static int btf_type_size_unknown(const struct btf *btf, const struct btf_type *t)
{
- __u32 layout_kinds = btf->hdr.layout_len / sizeof(struct btf_layout);
+ __u32 l_cnt = btf->hdr.layout_len / sizeof(struct btf_layout);
struct btf_layout *l = btf->layout;
__u16 vlen = btf_vlen(t);
__u32 kind = btf_kind(t);
@@ -423,10 +423,10 @@ static int btf_type_size_unknown(const struct btf *btf, const struct btf_type *t
if (base_btf) {
l = base_btf->layout;
- layout_kinds = base_btf->hdr.layout_len / sizeof(struct btf_layout);
+ l_cnt = base_btf->hdr.layout_len / sizeof(struct btf_layout);
}
}
- if (!l || kind >= layout_kinds) {
+ if (!l || kind >= l_cnt) {
pr_debug("Unsupported BTF_KIND: %u\n", btf_kind(t));
return -EINVAL;
}
@@ -729,8 +729,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] 14+ messages in thread* [PATCH v15 bpf-next 6/9] libbpf: Support sanitization of BTF layout for older kernels
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
` (4 preceding siblings ...)
2026-03-24 17:44 ` [PATCH v15 bpf-next 5/9] libbpf: BTF validation can use layout for unknown kinds Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-03-24 18:55 ` bot+bpf-ci
2026-03-24 17:44 ` [PATCH v15 bpf-next 7/9] btf: support kernel parsing of BTF with layout info Alan Maguire
` (2 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
Add a FEAT_BTF_LAYOUT feature check which checks if the
kernel supports BTF layout information. Also sanitize
BTF if it contains layout data but the kernel does not
support it. The sanitization requires rewriting raw
BTF data to update the header and eliminate the layout
section (since it lies between the types and strings),
so refactor sanitization to do the raw BTF retrieval
and creation of updated BTF, returning that new BTF
on success.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
tools/lib/bpf/features.c | 29 +++++++++++
tools/lib/bpf/libbpf.c | 88 +++++++++++++++++++++++++--------
tools/lib/bpf/libbpf_internal.h | 6 +++
tools/lib/bpf/libbpf_probes.c | 40 +++++++++------
4 files changed, 127 insertions(+), 36 deletions(-)
diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c
index adcad221c601..4f19a0d79b0c 100644
--- a/tools/lib/bpf/features.c
+++ b/tools/lib/bpf/features.c
@@ -589,6 +589,32 @@ static int probe_uprobe_syscall(int token_fd)
}
#endif
+static int probe_kern_btf_layout(int token_fd)
+{
+ static const char strs[] = "\0int";
+ __u32 types[] = {
+ /* int */
+ BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
+ };
+ struct btf_layout layout[] = {
+ { 0, 0, 0 },
+ { sizeof(__u32), 0, 0 },
+ };
+ struct btf_header hdr = {
+ .magic = BTF_MAGIC,
+ .version = BTF_VERSION,
+ .hdr_len = sizeof(struct btf_header),
+ .type_len = sizeof(types),
+ .str_off = sizeof(types) + sizeof(layout),
+ .str_len = sizeof(strs),
+ .layout_off = sizeof(types),
+ .layout_len = sizeof(layout),
+ };
+
+ return probe_fd(libbpf__load_raw_btf_hdr(&hdr, (char *)types, strs,
+ (char *)layout, token_fd));
+}
+
typedef int (*feature_probe_fn)(int /* token_fd */);
static struct kern_feature_cache feature_cache;
@@ -670,6 +696,9 @@ static struct kern_feature_desc {
[FEAT_UPROBE_SYSCALL] = {
"kernel supports uprobe syscall", probe_uprobe_syscall,
},
+ [FEAT_BTF_LAYOUT] = {
+ "kernel supports BTF layout", probe_kern_btf_layout,
+ },
};
bool feat_supported(struct kern_feature_cache *cache, enum kern_feature_id feat_id)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 0662d72bad20..1877de0cff44 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -3138,12 +3138,14 @@ static bool btf_needs_sanitization(struct bpf_object *obj)
bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
bool has_enum64 = kernel_supports(obj, FEAT_BTF_ENUM64);
bool has_qmark_datasec = kernel_supports(obj, FEAT_BTF_QMARK_DATASEC);
+ bool has_layout = kernel_supports(obj, FEAT_BTF_LAYOUT);
return !has_func || !has_datasec || !has_func_global || !has_float ||
- !has_decl_tag || !has_type_tag || !has_enum64 || !has_qmark_datasec;
+ !has_decl_tag || !has_type_tag || !has_enum64 || !has_qmark_datasec ||
+ !has_layout;
}
-static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
+static struct btf *bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *orig_btf)
{
bool has_func_global = kernel_supports(obj, FEAT_BTF_GLOBAL_FUNC);
bool has_datasec = kernel_supports(obj, FEAT_BTF_DATASEC);
@@ -3153,9 +3155,64 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
bool has_enum64 = kernel_supports(obj, FEAT_BTF_ENUM64);
bool has_qmark_datasec = kernel_supports(obj, FEAT_BTF_QMARK_DATASEC);
+ bool has_layout = kernel_supports(obj, FEAT_BTF_LAYOUT);
int enum64_placeholder_id = 0;
+ const struct btf_header *hdr;
+ struct btf *btf = NULL;
+ const void *raw_data;
struct btf_type *t;
int i, j, vlen;
+ __u32 sz;
+ int err;
+
+ /* clone BTF to sanitize a copy and leave the original intact */
+ raw_data = btf__raw_data(orig_btf, &sz);
+ if (!raw_data)
+ return ERR_PTR(-ENOMEM);
+ /* btf_header() gives us endian-safe header info */
+ hdr = btf_header(orig_btf);
+
+ if (!has_layout && hdr->hdr_len >= sizeof(struct btf_header) &&
+ (hdr->layout_len != 0 || hdr->layout_off != 0)) {
+ const struct btf_header *old_hdr = raw_data;
+ struct btf_header *new_hdr;
+ void *new_raw_data;
+ __u32 new_str_off;
+
+ /*
+ * Need to rewrite BTF to exclude layout information and
+ * move string section to immediately after types.
+ */
+ new_raw_data = malloc(sz);
+ if (!new_raw_data)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(new_raw_data, raw_data, sz);
+ new_hdr = new_raw_data;
+ new_hdr->layout_off = 0;
+ new_hdr->layout_len = 0;
+ new_str_off = hdr->type_off + hdr->type_len;
+ /* Handle swapped endian case */
+ if (old_hdr->str_off != hdr->str_off)
+ new_hdr->str_off = bswap_32(new_str_off);
+ else
+ new_hdr->str_off = new_str_off;
+
+ memmove(new_raw_data + hdr->hdr_len + new_hdr->str_off,
+ new_raw_data + hdr->hdr_len + hdr->str_off,
+ hdr->str_len);
+ sz = hdr->hdr_len + hdr->type_off + hdr->type_len + hdr->str_len;
+ btf = btf__new(new_raw_data, sz);
+ free(new_raw_data);
+ } else {
+ btf = btf__new(raw_data, sz);
+ }
+ err = libbpf_get_error(btf);
+ if (err)
+ return ERR_PTR(err);
+
+ /* enforce 8-byte pointers for BPF-targeted BTFs */
+ btf__set_pointer_size(btf, 8);
for (i = 1; i < btf__type_cnt(btf); i++) {
t = (struct btf_type *)btf__type_by_id(btf, i);
@@ -3233,9 +3290,10 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
if (enum64_placeholder_id == 0) {
enum64_placeholder_id = btf__add_int(btf, "enum64_placeholder", 1, 0);
- if (enum64_placeholder_id < 0)
- return enum64_placeholder_id;
-
+ if (enum64_placeholder_id < 0) {
+ btf__free(btf);
+ return ERR_PTR(enum64_placeholder_id);
+ }
t = (struct btf_type *)btf__type_by_id(btf, i);
}
@@ -3249,7 +3307,7 @@ static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
}
}
- return 0;
+ return btf;
}
static bool libbpf_needs_btf(const struct bpf_object *obj)
@@ -3600,21 +3658,9 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
sanitize = btf_needs_sanitization(obj);
if (sanitize) {
- const void *raw_data;
- __u32 sz;
-
- /* clone BTF to sanitize a copy and leave the original intact */
- raw_data = btf__raw_data(obj->btf, &sz);
- kern_btf = btf__new(raw_data, sz);
- err = libbpf_get_error(kern_btf);
- if (err)
- return err;
-
- /* enforce 8-byte pointers for BPF-targeted BTFs */
- btf__set_pointer_size(obj->btf, 8);
- err = bpf_object__sanitize_btf(obj, kern_btf);
- if (err)
- return err;
+ kern_btf = bpf_object__sanitize_btf(obj, obj->btf);
+ if (IS_ERR(kern_btf))
+ return PTR_ERR(kern_btf);
}
if (obj->gen_loader) {
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 4bcb6ca69bb1..cabdaef79098 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -396,6 +396,8 @@ enum kern_feature_id {
FEAT_LDIMM64_FULL_RANGE_OFF,
/* Kernel supports uprobe syscall */
FEAT_UPROBE_SYSCALL,
+ /* Kernel supports BTF layout information */
+ FEAT_BTF_LAYOUT,
__FEAT_CNT,
};
@@ -422,6 +424,10 @@ int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz);
int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
const char *str_sec, size_t str_len,
int token_fd);
+int libbpf__load_raw_btf_hdr(const struct btf_header *hdr,
+ const char *raw_types, const char *str_sec,
+ const char *layout_sec, int token_fd);
+
int btf_load_into_kernel(struct btf *btf,
char *log_buf, size_t log_sz, __u32 log_level,
int token_fd);
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index bccf4bb747e1..b70d9637ecf5 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -218,18 +218,10 @@ int libbpf_probe_bpf_prog_type(enum bpf_prog_type prog_type, const void *opts)
return libbpf_err(ret);
}
-int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
- const char *str_sec, size_t str_len,
- int token_fd)
+int libbpf__load_raw_btf_hdr(const struct btf_header *hdr, const char *raw_types,
+ const char *str_sec, const char *layout_sec,
+ int token_fd)
{
- struct btf_header hdr = {
- .magic = BTF_MAGIC,
- .version = BTF_VERSION,
- .hdr_len = sizeof(struct btf_header),
- .type_len = types_len,
- .str_off = types_len,
- .str_len = str_len,
- };
LIBBPF_OPTS(bpf_btf_load_opts, opts,
.token_fd = token_fd,
.btf_flags = token_fd ? BPF_F_TOKEN_FD : 0,
@@ -237,14 +229,16 @@ int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
int btf_fd, btf_len;
__u8 *raw_btf;
- btf_len = hdr.hdr_len + hdr.type_len + hdr.str_len;
+ btf_len = hdr->hdr_len + hdr->type_off + hdr->type_len + hdr->str_len + hdr->layout_len;
raw_btf = malloc(btf_len);
if (!raw_btf)
return -ENOMEM;
- memcpy(raw_btf, &hdr, sizeof(hdr));
- memcpy(raw_btf + hdr.hdr_len, raw_types, hdr.type_len);
- memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len);
+ memcpy(raw_btf, hdr, sizeof(*hdr));
+ memcpy(raw_btf + hdr->hdr_len + hdr->type_off, raw_types, hdr->type_len);
+ memcpy(raw_btf + hdr->hdr_len + hdr->str_off, str_sec, hdr->str_len);
+ if (layout_sec)
+ memcpy(raw_btf + hdr->hdr_len + hdr->layout_off, layout_sec, hdr->layout_len);
btf_fd = bpf_btf_load(raw_btf, btf_len, &opts);
@@ -252,6 +246,22 @@ int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
return btf_fd;
}
+int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
+ const char *str_sec, size_t str_len,
+ int token_fd)
+{
+ struct btf_header hdr = {
+ .magic = BTF_MAGIC,
+ .version = BTF_VERSION,
+ .hdr_len = sizeof(struct btf_header),
+ .type_len = types_len,
+ .str_off = types_len,
+ .str_len = str_len,
+ };
+
+ return libbpf__load_raw_btf_hdr(&hdr, raw_types, str_sec, NULL, token_fd);
+}
+
static int load_local_storage_btf(void)
{
const char strs[] = "\0bpf_spin_lock\0val\0cnt\0l";
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v15 bpf-next 6/9] libbpf: Support sanitization of BTF layout for older kernels
2026-03-24 17:44 ` [PATCH v15 bpf-next 6/9] libbpf: Support sanitization of BTF layout for older kernels Alan Maguire
@ 2026-03-24 18:55 ` bot+bpf-ci
2026-03-25 17:39 ` Alan Maguire
0 siblings, 1 reply; 14+ messages in thread
From: bot+bpf-ci @ 2026-03-24 18:55 UTC (permalink / raw)
To: alan.maguire, andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, alan.maguire, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, clm,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2301 bytes --]
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 1eaa7527d4dab..e32b42ef9be84 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
[ ... ]
> + /* btf_header() gives us endian-safe header info */
> + hdr = btf_header(orig_btf);
> +
> + if (!has_layout && hdr->hdr_len >= sizeof(struct btf_header) &&
> + (hdr->layout_len != 0 || hdr->layout_off != 0)) {
> + const struct btf_header *old_hdr = raw_data;
> + struct btf_header *new_hdr;
> + void *new_raw_data;
> + __u32 new_str_off;
> +
> + /*
> + * Need to rewrite BTF to exclude layout information and
> + * move string section to immediately after types.
> + */
> + new_raw_data = malloc(sz);
> + if (!new_raw_data)
> + return ERR_PTR(-ENOMEM);
> +
> + memcpy(new_raw_data, raw_data, sz);
> + new_hdr = new_raw_data;
> + new_hdr->layout_off = 0;
> + new_hdr->layout_len = 0;
> + new_str_off = hdr->type_off + hdr->type_len;
> + /* Handle swapped endian case */
> + if (old_hdr->str_off != hdr->str_off)
> + new_hdr->str_off = bswap_32(new_str_off);
> + else
> + new_hdr->str_off = new_str_off;
> +
> + memmove(new_raw_data + hdr->hdr_len + new_hdr->str_off,
> + new_raw_data + hdr->hdr_len + hdr->str_off,
> + hdr->str_len);
In the swapped-endian case, new_hdr->str_off holds the byte-swapped
(file-endian) value after the bswap_32() above. When that value is
used here as a host memory offset for the memmove destination, it
produces a nonsensical address.
For example, if new_str_off is 24 (0x00000018), then bswap_32 gives
0x18000000, which is ~400 MB past the buffer. This would overflow
the malloc'd new_raw_data buffer.
Should the memmove destination use new_str_off (the host-endian
offset) instead of new_hdr->str_off? Something like:
memmove(new_raw_data + hdr->hdr_len + new_str_off,
new_raw_data + hdr->hdr_len + hdr->str_off,
hdr->str_len);
> + sz = hdr->hdr_len + hdr->type_off + hdr->type_len + hdr->str_len;
> + btf = btf__new(new_raw_data, sz);
> + free(new_raw_data);
[ ... ]
---
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/23504838405
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v15 bpf-next 6/9] libbpf: Support sanitization of BTF layout for older kernels
2026-03-24 18:55 ` bot+bpf-ci
@ 2026-03-25 17:39 ` Alan Maguire
2026-03-25 18:52 ` Alexei Starovoitov
0 siblings, 1 reply; 14+ messages in thread
From: Alan Maguire @ 2026-03-25 17:39 UTC (permalink / raw)
To: bot+bpf-ci, andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, martin.lau, clm
On 24/03/2026 18:55, bot+bpf-ci@kernel.org wrote:
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 1eaa7527d4dab..e32b42ef9be84 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>
> [ ... ]
>
>> + /* btf_header() gives us endian-safe header info */
>> + hdr = btf_header(orig_btf);
>> +
>> + if (!has_layout && hdr->hdr_len >= sizeof(struct btf_header) &&
>> + (hdr->layout_len != 0 || hdr->layout_off != 0)) {
>> + const struct btf_header *old_hdr = raw_data;
>> + struct btf_header *new_hdr;
>> + void *new_raw_data;
>> + __u32 new_str_off;
>> +
>> + /*
>> + * Need to rewrite BTF to exclude layout information and
>> + * move string section to immediately after types.
>> + */
>> + new_raw_data = malloc(sz);
>> + if (!new_raw_data)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + memcpy(new_raw_data, raw_data, sz);
>> + new_hdr = new_raw_data;
>> + new_hdr->layout_off = 0;
>> + new_hdr->layout_len = 0;
>> + new_str_off = hdr->type_off + hdr->type_len;
>> + /* Handle swapped endian case */
>> + if (old_hdr->str_off != hdr->str_off)
>> + new_hdr->str_off = bswap_32(new_str_off);
>> + else
>> + new_hdr->str_off = new_str_off;
>> +
>> + memmove(new_raw_data + hdr->hdr_len + new_hdr->str_off,
>> + new_raw_data + hdr->hdr_len + hdr->str_off,
>> + hdr->str_len);
>
> In the swapped-endian case, new_hdr->str_off holds the byte-swapped
> (file-endian) value after the bswap_32() above. When that value is
> used here as a host memory offset for the memmove destination, it
> produces a nonsensical address.
>
> For example, if new_str_off is 24 (0x00000018), then bswap_32 gives
> 0x18000000, which is ~400 MB past the buffer. This would overflow
> the malloc'd new_raw_data buffer.
>
> Should the memmove destination use new_str_off (the host-endian
> offset) instead of new_hdr->str_off? Something like:
>
> memmove(new_raw_data + hdr->hdr_len + new_str_off,
> new_raw_data + hdr->hdr_len + hdr->str_off,
> hdr->str_len);
>
yep, this is a legit issue and the fix makes sense. sashiko.dev [1] also
pointed out it would be better to use hdr->magic for endian comparison.
It also suggested doing endian swaps for layout info despite the only
endian-impacted field being the unused flags, and given that this will avoid
future issues if/when flags starts being used I think that makes sense too.
I'll wait a few days for additional feedback if any and roll v16..
[1] https://sashiko.dev/#/patchset/20260324174450.1570809-1-alan.maguire%40oracle.com
>> + sz = hdr->hdr_len + hdr->type_off + hdr->type_len + hdr->str_len;
>> + btf = btf__new(new_raw_data, sz);
>> + free(new_raw_data);
>
> [ ... ]
>
>
> ---
> 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/23504838405
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v15 bpf-next 6/9] libbpf: Support sanitization of BTF layout for older kernels
2026-03-25 17:39 ` Alan Maguire
@ 2026-03-25 18:52 ` Alexei Starovoitov
0 siblings, 0 replies; 14+ messages in thread
From: Alexei Starovoitov @ 2026-03-25 18:52 UTC (permalink / raw)
To: Alan Maguire
Cc: bot+bpf-ci, Andrii Nakryiko, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Eduard, Song Liu, Yonghong Song, Jiri Olsa,
Quentin Monnet, Ihor Solodrai, Mykyta Yatsenko, dwarves, bpf,
Martin KaFai Lau, Chris Mason
On Wed, Mar 25, 2026 at 10:40 AM Alan Maguire <alan.maguire@oracle.com> wrote:
>
>
> I'll wait a few days for additional feedback if any and roll v16..
No need to wait. Fix quickly and respin. Let's land it asap.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v15 bpf-next 7/9] btf: support kernel parsing of BTF with layout info
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
` (5 preceding siblings ...)
2026-03-24 17:44 ` [PATCH v15 bpf-next 6/9] libbpf: Support sanitization of BTF layout for older kernels Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 8/9] selftests/bpf: Test kind encoding/decoding Alan Maguire
2026-03-24 17:44 ` [PATCH v15 bpf-next 9/9] kbuild, bpf: Specify "layout" optional feature Alan Maguire
8 siblings, 0 replies; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
Validate 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 layout information.
Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
kernel/bpf/btf.c | 60 ++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 56 insertions(+), 4 deletions(-)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 09fcbb125155..e6030627c3d4 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -270,6 +270,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_layout *layout;
/* split BTF support */
struct btf *base_btf;
@@ -1707,6 +1708,11 @@ static void btf_verifier_log_hdr(struct btf_verifier_env *env,
__btf_verifier_log(log, "type_len: %u\n", hdr->type_len);
__btf_verifier_log(log, "str_off: %u\n", hdr->str_off);
__btf_verifier_log(log, "str_len: %u\n", hdr->str_len);
+ if (hdr->hdr_len >= sizeof(struct btf_header) &&
+ btf_data_size >= hdr->hdr_len) {
+ __btf_verifier_log(log, "layout_off: %u\n", hdr->layout_off);
+ __btf_verifier_log(log, "layout_len: %u\n", hdr->layout_len);
+ }
__btf_verifier_log(log, "btf_total_size: %u\n", btf_data_size);
}
@@ -5517,7 +5523,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;
}
@@ -5538,9 +5545,46 @@ static int btf_parse_str_sec(struct btf_verifier_env *env)
return 0;
}
+static int btf_parse_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->layout_len == 0)
+ return 0;
+
+ /* Layout section must align to 4 bytes */
+ if (hdr->layout_off & (sizeof(u32) - 1)) {
+ btf_verifier_log(env, "Unaligned layout_off");
+ return -EINVAL;
+ }
+ start = btf->nohdr_data + hdr->layout_off;
+ end = start + hdr->layout_len;
+
+ if (hdr->layout_len < sizeof(struct btf_layout)) {
+ btf_verifier_log(env, "Layout section is too small");
+ return -EINVAL;
+ }
+ if (hdr->layout_len % sizeof(struct btf_layout) != 0) {
+ btf_verifier_log(env, "layout_len is not multiple of %zu",
+ sizeof(struct btf_layout));
+ return -EINVAL;
+ }
+ if (end > btf->data + btf->data_size) {
+ btf_verifier_log(env, "Layout section is too big");
+ return -EINVAL;
+ }
+ btf->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, layout_off)
};
static int btf_sec_info_cmp(const void *a, const void *b)
@@ -5556,24 +5600,28 @@ static int btf_check_sec_info(struct btf_verifier_env *env,
{
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);
const struct btf_header *hdr;
const struct btf *btf;
btf = env->btf;
hdr = &btf->hdr;
+ if (hdr->hdr_len < sizeof(struct btf_header) || hdr->layout_len == 0)
+ 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;
@@ -5929,6 +5977,10 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, u32 uat
if (err)
goto errout;
+ err = btf_parse_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] 14+ messages in thread* [PATCH v15 bpf-next 8/9] selftests/bpf: Test kind encoding/decoding
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
` (6 preceding siblings ...)
2026-03-24 17:44 ` [PATCH v15 bpf-next 7/9] btf: support kernel parsing of BTF with layout info Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
2026-08-13 22:35 ` sashiko-bot
2026-03-24 17:44 ` [PATCH v15 bpf-next 9/9] kbuild, bpf: Specify "layout" optional feature Alan Maguire
8 siblings, 1 reply; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
verify btf__new_empty_opts() adds 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 | 226 ++++++++++++++++++
1 file changed, 226 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..64ceac1d999e
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026, 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_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->layout_off % 4, 0, "layout_aligned");
+ ASSERT_EQ(hdr->layout_len, sizeof(struct btf_layout) * NR_BTF_KINDS,
+ "layout_len");
+ ASSERT_EQ(hdr->str_off, hdr->layout_off + hdr->layout_len, "str_after_layout");
+ btf__free(btf);
+
+ opts.add_layout = false;
+ 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->layout_off, 0, "no_layout_off");
+ ASSERT_EQ(hdr->layout_len, 0, "no_layout_len");
+ ASSERT_EQ(hdr->str_off, hdr->type_off + hdr->type_len, "strs_after_types");
+ btf__free(btf);
+}
+
+static int write_raw_btf(void *raw_btf, size_t raw_size, char *file)
+{
+ int fd = mkstemp(file);
+ ssize_t n;
+
+ if (!ASSERT_OK_FD(fd, "open_raw_btf"))
+ return -1;
+ n = write(fd, raw_btf, raw_size);
+ close(fd);
+ if (!ASSERT_EQ(n, (ssize_t)raw_size, "write_raw_btf"))
+ return -1;
+ return 0;
+}
+
+/*
+ * 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)
+{
+ char btf_kind_file1[] = "/tmp/test_btf_kind.XXXXXX";
+ char btf_kind_file2[] = "/tmp/test_btf_kind.XXXXXX";
+ char btf_kind_file3[] = "/tmp/test_btf_kind.XXXXXX";
+ struct btf *btf = NULL, *new_btf = NULL;
+ __s32 int_id, unrec_id, id, id2;
+ LIBBPF_OPTS(btf_new_opts, opts);
+ struct btf_layout *l;
+ struct btf_header *hdr;
+ const void *raw_btf;
+ struct btf_type *t;
+ void *new_raw_btf;
+ void *str_data;
+ __u32 raw_size;
+
+ opts.add_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;
+
+ /*
+ * 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(*l));
+ if (!ASSERT_OK_PTR(new_raw_btf, "calloc_raw_btf"))
+ return;
+ memcpy(new_raw_btf, raw_btf, raw_size);
+
+ hdr = new_raw_btf;
+
+ /* Move strings to make space for one new layout description */
+ raw_size += sizeof(*l);
+ str_data = new_raw_btf + hdr->hdr_len + hdr->str_off;
+ memmove(str_data + sizeof(*l), str_data, hdr->str_len);
+ hdr->str_off += sizeof(*l);
+
+ /* Add new layout description */
+ hdr->layout_len += sizeof(*l);
+ l = new_raw_btf + hdr->hdr_len + hdr->layout_off;
+ l[NR_BTF_KINDS].info_sz = 0;
+ l[NR_BTF_KINDS].elem_sz = 0;
+ l[NR_BTF_KINDS].flags = 0;
+
+ /* Now modify 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);
+
+ /* Write BTF to a raw file, ready for parsing. */
+ if (write_raw_btf(new_raw_btf, raw_size, btf_kind_file1))
+ goto out;
+
+ /*
+ * Verify parsing succeeds, and that we can read type info past
+ * the unrecognized kind.
+ */
+ new_btf = btf__parse_raw(btf_kind_file1);
+ if (ASSERT_OK_PTR(new_btf, "btf__parse_raw")) {
+ ASSERT_EQ(btf__find_by_name(new_btf, "unrec_kind"), unrec_id,
+ "unrec_kind_found");
+ 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);
+ new_btf = NULL;
+
+ /*
+ * 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.
+ */
+ l[NR_BTF_KINDS].info_sz = sizeof(struct btf_type);
+ if (write_raw_btf(new_raw_btf, raw_size, btf_kind_file2))
+ goto out;
+
+ new_btf = btf__parse_raw(btf_kind_file2);
+ 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);
+ new_btf = NULL;
+
+ /*
+ * 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.
+ */
+ l[NR_BTF_KINDS].info_sz = 0;
+ l[NR_BTF_KINDS].elem_sz = sizeof(struct btf_type)/2;
+ t->info |= 2;
+ if (write_raw_btf(new_raw_btf, raw_size, btf_kind_file3))
+ goto out;
+
+ new_btf = btf__parse_raw(btf_kind_file3);
+ 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");
+
+ }
+out:
+ btf__free(new_btf);
+ free(new_raw_btf);
+ unlink(btf_kind_file1);
+ unlink(btf_kind_file2);
+ unlink(btf_kind_file3);
+ 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] 14+ messages in thread* Re: [PATCH v15 bpf-next 8/9] selftests/bpf: Test kind encoding/decoding
2026-03-24 17:44 ` [PATCH v15 bpf-next 8/9] selftests/bpf: Test kind encoding/decoding Alan Maguire
@ 2026-08-13 22:35 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-08-13 22:35 UTC (permalink / raw)
To: Alan Maguire; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The BTF object `btf` is leaked on early error paths.
--
--- Patch [8]: [PATCH v15 bpf-next 8/9] selftests/bpf: Test kind encoding/decoding ---
commit 2e5fe50dd1168fa6e0f69edd9f780d42539e7151
Author: Alan Maguire <alan.maguire@oracle.com>
selftests/bpf: Test kind encoding/decoding
This commit adds tests to verify that btf__new_empty_opts() correctly adds
layouts for all supported kinds. It also ensures that parsing of a BTF
containing unrecognized kinds can still succeed.
> 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 0000000000000..64ceac1d999e6
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
> @@ -0,0 +1,226 @@
[ ... ]
> +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_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;
[Severity: Medium]
Does this early return leak the btf object allocated just above by
btf__new_empty_opts()? It looks like btf__free(btf) is skipped if
btf__raw_data() fails.
[ ... ]
> +void test_btf_kind_decoding(void)
> +{
> + char btf_kind_file1[] = "/tmp/test_btf_kind.XXXXXX";
> + char btf_kind_file2[] = "/tmp/test_btf_kind.XXXXXX";
> + char btf_kind_file3[] = "/tmp/test_btf_kind.XXXXXX";
> + struct btf *btf = NULL, *new_btf = NULL;
> + __s32 int_id, unrec_id, id, id2;
> + LIBBPF_OPTS(btf_new_opts, opts);
> + struct btf_layout *l;
> + struct btf_header *hdr;
> + const void *raw_btf;
> + struct btf_type *t;
> + void *new_raw_btf;
> + void *str_data;
> + __u32 raw_size;
> +
> + opts.add_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;
[Severity: Medium]
Will this direct return bypass the cleanup block at the end of the function
and leak the btf object?
There are several other similar return statements following this one. Should
these early failure paths use goto out to ensure btf__free(btf) is called?
> +
> + /*
> + * 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;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260324174450.1570809-1-alan.maguire@oracle.com?part=8
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v15 bpf-next 9/9] kbuild, bpf: Specify "layout" optional feature
2026-03-24 17:44 [PATCH v15 bpf-next 0/9] Add BTF layout to BTF Alan Maguire
` (7 preceding siblings ...)
2026-03-24 17:44 ` [PATCH v15 bpf-next 8/9] selftests/bpf: Test kind encoding/decoding Alan Maguire
@ 2026-03-24 17:44 ` Alan Maguire
8 siblings, 0 replies; 14+ messages in thread
From: Alan Maguire @ 2026-03-24 17:44 UTC (permalink / raw)
To: andrii, ast
Cc: daniel, martin.lau, eddyz87, song, yonghong.song, jolsa, qmo,
ihor.solodrai, mykyta.yatsenko5, dwarves, bpf, Alan Maguire
The "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 562a04b40e06..e66e13e79653 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -18,6 +18,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=layout
+
endif
pahole-flags-$(CONFIG_PAHOLE_HAS_LANG_EXCLUDE) += --lang_exclude=rust
--
2.39.3
^ permalink raw reply related [flat|nested] 14+ messages in thread