mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
Merge branch 'add-btf-layout-to-btf'
Alan Maguire says: ==================== Add BTF layout to BTF Update struct btf_header to add a new "layout" section containing a description of how to parse the BTF kinds known about at BTF encoding time. This provides the opportunity for tools that might not know all of these kinds - as is the case when older tools run on more newly-generated BTF - to still parse the BTF provided, even if it cannot all be used. The ideas here were discussed at [1], with further discussion at [2]. Patches for pahole will enable the layout addition during BTF generation are at [3], but even absent these the addition of the layout feature in the final patch in this series should not break anything since such unknown features are simply ignored during pahole BTF generation. Separately tested sanitization of BTF location info with separate small series which simulates feature absence to support testing of features for older kernels; will follow up with that shortly. Changes since v15 [4]: - Fixed endian issues for layout section by swapping flags fields where needed (sashiko.dev, patch 2) - Fixed string size issue with swapped endian case, use btf->magic for comparison to determine endian mismatch (bpf review bot, sashiko.dev, patch 6) Changes since v14 [5]: - Fix potential overflow for swapped endian case (BPF review bot, patch 2) - Add global: keyword to libbpf.map (sashiko.dev, patch 4) - Fix endian issues in sanitization; we use the endian safe btf->hdr and check for endian mismatch between it and raw original BTF header to inform how we write the change str_off. Also fix potential truncation issues due to not including hdr->type_off (sashiko.dev, patch 6) - Fix issues with selftests raw BTF file interactions (sashiko.dev, patch 8) - Drop feature test test since it will be covered by another series Changes since v13: [6]: - add feature check/sanitization of BTF with layout info (Andrii, patch 6) - added feature check test for layout support (patch 9) Changes since v12: [7]: - add logging of layout off/len to kernel header logging (review bot, patch 6) - add mode to open() in selftest (review bot, patch 7) Changes since v11 [8]: - Revert unneeded changes to btf__new_empty() (Eduard, review bot, patch 4) - Reorder btf_parse_layout_sec() checks to ensure min size check occurs before multiple check (review bot, patch 6) Changes since v10 [9]: - deal with read overflow with small header (review bot, patch 2) - validate layout length is a multiple of sizeof(struct btf_layout) (review bot, patch 6) - fix comment style (Alexei, patches 4,7) - remove bpftool BTF metadata subcommands for now (Alexei) Changes since v9: [10]: - fix memcpy header size overrun (review bot, patch 2) - return size computation directly (Andrii, patch 333) - revert to original unknown kind logging (Alexei/review bot, patch 6) - gap-checking logic can be simplified now that we have 4-byte aligned types and layout together (patch 6) - fix naming of layout offset, unconditionally emit a layouts array in json (Quentin, review bot, patch 8) - fix metadata output in man page to include flags (review bot, patch 9) Changes since v8: [11]: - updated name from "kind_layout" to "layout" (Andrii) - moved layout info to inbetween types and strings since both types and layout info align on 4 bytes (Andrii) - use embedded btf_header (Eduard) - when consulting layout, fall back to base BTF if none found in split BTF; this will allow us to only encode layout info in vmlinux rather than repeating it for each module. Changes since v7: [12]: - Fixed comment style in UAPI headers (Mykyta, patch 1) - Simplify calcuation of header size using min() (Mykyta, patch 2) - simplify computation of bounds for kind (Mykyta, patch 3) - Added utility functions for updating type, string offsets when data is added; this simplifies the code and encapsulates such updates more clearly (patch 2) Changes since v6: [13]: - BPF review bot caught some memory leaks around freeing of kind layout; more importantly, it noted that we were breaking with the contiguous BTF representation for btf_new_empty_opts(). Doing so meant that freeing kind_layout could not be predicated on having btf->modifiable set, so adpoted the contiguous raw data layout for BTF to be consistent with type/string storage (patches 2,4) - Moved checks for kind overflow prior to referencing kinds to avoid any risk of overrun (patches 3, 8) - Tightened up kind layout header offset/len header validation to catch invalid combinations early in btf_parse_hdr() (patch 2) - Fixed selftest to verify calloc success (patch 7) Changes since v5: [14]: - removed flags field from kind layout; it is not really workable since we would have to define semantics of all possible future flags today to be usable. Instead stick to parsing only, which means each kind just needs the length of the singular and vlen-specified objects (Alexei) - added documentation for bpftool BTF metadata dump (Quentin, patch 9) Changes since v4: [15]: - removed CRC generation since it is not needed to handle modules built at different time than kernel; distilled base BTF supports this now - fixed up bpftool display of empty kind names, comment/documentation indentation (Quentin, patches 8, 9) Changes since v3 [16]: - fixed mismerge issues with kbuild changes for BTF generation (patches 9, 14) - fixed a few small issues in libbpf with kind layout representation (patches 2, 4) Changes since v2 [17]: - drop "optional" kind flag (Andrii, patch 1) - allocate "struct btf_header" for struct btf to ensure we can always access new fields (Andrii, patch 2) - use an internal BTF kind array in btf.c to simplify kind encoding (Andrii, patch 2) - drop use of kind layout information for in-kernel parsing, since the kernel needs to be strict in what it accepts (Andrii, patch 6) - added CRC verification for BTF objects and for matching with base object (Alexei, patches 7,8) - fixed bpftool json output (Quentin, patch 10) - added standalone module BTF support, tests (patches 13-17) Changes since RFC - Terminology change from meta -> kind_layout (Alexei and Andrii) - Simplify representation, removing meta header and just having kind layout section (Alexei) - Fixed bpftool to have JSON support, support prefix match, documented changes (Quentin) - Separated metadata opts into add_kind_layout and add_crc - Added additional positive/negative tests to cover basic unknown kind, one with an info_sz object following it and one with N elem_sz elements following it. - Updated pahole-flags to use help output rather than version to see if features are present [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/ [3] https://lore.kernel.org/dwarves/20260226085240.1908874-1-alan.maguire@oracle.com/ [4] https://lore.kernel.org/bpf/20260324174450.1570809-1-alan.maguire@oracle.com/ [5] https://lore.kernel.org/bpf/20260318132927.1142388-1-alan.maguire@oracle.com/ [6] https://lore.kernel.org/bpf/20260306113630.1281527-1-alan.maguire@oracle.com/ [7] https://lore.kernel.org/bpf/20260303182003.117483-1-alan.maguire@oracle.com/ [8] https://lore.kernel.org/bpf/20260302114059.3697879-1-alan.maguire@oracle.com/ [9] https://lore.kernel.org/bpf/20260227100426.2585191-1-alan.maguire@oracle.com/ [10] https://lore.kernel.org/bpf/20260226085624.1909682-1-alan.maguire@oracle.com/ [11] https://lore.kernel.org/bpf/20251215091730.1188790-1-alan.maguire@oracle.com/ [12] https://lore.kernel.org/dwarves/20251211164646.1219122-1-alan.maguire@oracle.com/ [13] https://lore.kernel.org/bpf/20251210203243.814529-1-alan.maguire@oracle.com/ [14] https://lore.kernel.org/bpf/20250528095743.791722-1-alan.maguire@oracle.com/ [15] https://lore.kernel.org/bpf/20231112124834.388735-1-alan.maguire@oracle.com/ [16] https://lore.kernel.org/bpf/20231110110304.63910-1-alan.maguire@oracle.com/ [17] https://lore.kernel.org/bpf/20230616171728.530116-1-alan.maguire@oracle.com/ ==================== Link: https://patch.msgid.link/20260326145444.2076244-1-alan.maguire@oracle.com Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
This commit is contained in:
commit
01504da43e
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -218,6 +218,34 @@ int libbpf_probe_bpf_prog_type(enum bpf_prog_type prog_type, const void *opts)
|
|||
return libbpf_err(ret);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_btf_load_opts, opts,
|
||||
.token_fd = token_fd,
|
||||
.btf_flags = token_fd ? BPF_F_TOKEN_FD : 0,
|
||||
);
|
||||
int btf_fd, btf_len;
|
||||
__u8 *raw_btf;
|
||||
|
||||
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 + 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);
|
||||
|
||||
free(raw_btf);
|
||||
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)
|
||||
|
|
@ -230,26 +258,8 @@ int libbpf__load_raw_btf(const char *raw_types, size_t 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,
|
||||
);
|
||||
int btf_fd, btf_len;
|
||||
__u8 *raw_btf;
|
||||
|
||||
btf_len = hdr.hdr_len + hdr.type_len + hdr.str_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);
|
||||
|
||||
btf_fd = bpf_btf_load(raw_btf, btf_len, &opts);
|
||||
|
||||
free(raw_btf);
|
||||
return btf_fd;
|
||||
return libbpf__load_raw_btf_hdr(&hdr, raw_types, str_sec, NULL, token_fd);
|
||||
}
|
||||
|
||||
static int load_local_storage_btf(void)
|
||||
|
|
|
|||
226
tools/testing/selftests/bpf/prog_tests/btf_kind.c
Normal file
226
tools/testing/selftests/bpf/prog_tests/btf_kind.c
Normal file
|
|
@ -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();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user