summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/uapi/linux/btf.h12
-rw-r--r--kernel/bpf/btf.c60
-rw-r--r--scripts/Makefile.btf2
-rw-r--r--tools/include/uapi/linux/btf.h12
-rw-r--r--tools/lib/bpf/btf.c596
-rw-r--r--tools/lib/bpf/btf.h20
-rw-r--r--tools/lib/bpf/features.c29
-rw-r--r--tools/lib/bpf/libbpf.c88
-rw-r--r--tools/lib/bpf/libbpf.map2
-rw-r--r--tools/lib/bpf/libbpf_internal.h6
-rw-r--r--tools/lib/bpf/libbpf_probes.c40
-rw-r--r--tools/testing/selftests/bpf/prog_tests/btf_kind.c226
12 files changed, 887 insertions, 206 deletions
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/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 15f4c99a46c0..a62d78581207 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);
}
@@ -5526,7 +5532,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;
}
@@ -5547,9 +5554,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)
@@ -5565,24 +5609,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;
@@ -5938,6 +5986,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;
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
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 */
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 40becc964368..ceb57b46a878 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;
@@ -40,42 +70,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 +166,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 +268,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 +276,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,7 +395,63 @@ static int btf_parse_str_sec(struct btf *btf)
return 0;
}
-static int btf_type_size(const struct btf_type *t)
+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;
+
+ if (btf->swapped_endian) {
+ struct btf_layout *l, *end = btf->layout + btf->hdr.layout_len;
+
+ for (l = btf->layout; l < end; l++)
+ l->flags = bswap_16(l->flags);
+ }
+
+ return 0;
+}
+
+/* for unknown kinds, consult kind layout. */
+static int btf_type_size_unknown(const struct btf *btf, const struct btf_type *t)
+{
+ __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);
+
+ /* 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;
+ l_cnt = base_btf->hdr.layout_len / sizeof(struct btf_layout);
+ }
+ }
+ if (!l || kind >= l_cnt) {
+ pr_debug("Unsupported BTF_KIND: %u\n", btf_kind(t));
+ return -EINVAL;
+ }
+ if (l[kind].info_sz % 4) {
+ pr_debug("Unsupported info_sz %u for kind %u\n",
+ l[kind].info_sz, kind);
+ return -EINVAL;
+ }
+ if (l[kind].elem_sz % 4) {
+ pr_debug("Unsupported elem_sz %u for kind %u\n",
+ l[kind].elem_sz, kind);
+ 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);
@@ -330,8 +487,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);
}
}
@@ -421,16 +577,15 @@ 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) {
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) {
@@ -591,8 +746,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;
}
@@ -1012,7 +1171,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 +1196,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);
@@ -1053,8 +1213,11 @@ 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;
btf = calloc(1, sizeof(*btf));
@@ -1072,26 +1235,42 @@ 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;
}
/* +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);
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 */
+
+ if (add_layout) {
+ hdr->layout_len = sizeof(layouts);
+ btf->layout = layouts;
+ /*
+ * No need to swap endianness here as btf_get_raw_data()
+ * will do this for us if btf->swapped_endian is true.
+ */
+ memcpy(btf->raw_data + hdr->hdr_len, layouts, sizeof(layouts));
+ btf->strs_data += sizeof(layouts);
+ hdr->str_off += sizeof(layouts);
+ }
+
+ memcpy(&btf->hdr, hdr, sizeof(*hdr));
return btf;
}
@@ -1103,7 +1282,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)
@@ -1124,7 +1315,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 +1332,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 +1792,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 +1805,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 +1833,18 @@ 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);
+ if (swap_endian) {
+ struct btf_layout *l, *end = p + hdr->layout_len;
+
+ for (l = p; l < end ; l++)
+ l->flags = bswap_16(l->flags);
+ }
+ p += hdr->layout_len;
+ }
+
memcpy(p, btf_strs_data(btf), hdr->str_len);
- p += hdr->str_len;
*size = data_sz;
return data;
@@ -1675,7 +1879,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 +1987,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 +2002,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 +2066,7 @@ err_out:
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 +2075,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 +2094,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 +2102,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 +2118,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 +2126,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;
}
@@ -1963,13 +2199,14 @@ 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);
/* 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 +2255,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 */
@@ -2051,7 +2289,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;
@@ -2103,8 +2341,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 +2352,14 @@ err_out:
/* 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);
+ if (btf->strs_data)
+ 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 +2379,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 +2391,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 +2429,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 +2440,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 +2476,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 +2528,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 +2560,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 +2644,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 +2665,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 +2689,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 +2698,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 +2762,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 +2778,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 +2802,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 +2840,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 +2854,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 +2875,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 +3044,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 +3082,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 +3095,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 +3117,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 +3136,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 +3148,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 +3187,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 +3228,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 +3242,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 +3259,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 +3268,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 +3276,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 +3902,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 +4224,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;
@@ -5395,7 +5657,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;
@@ -5409,14 +5671,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 +6139,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 +6208,14 @@ done:
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 +6282,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];
@@ -6055,7 +6319,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 */
@@ -6081,7 +6345,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);
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/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 1eaa7527d4da..9ea41f40dc82 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->magic != hdr->magic)
+ 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_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.map b/tools/lib/bpf/libbpf.map
index 5828040f178a..346fd346666b 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -458,4 +458,6 @@ LIBBPF_1.7.0 {
} LIBBPF_1.6.0;
LIBBPF_1.8.0 {
+ global:
+ btf__new_empty_opts;
} LIBBPF_1.7.0;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 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";
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..f61afe6a79a5
--- /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) and set vlen
+ * associated with unrecognized type to 1; 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);
+ t->info |= 1;
+ 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();
+}