libbpf: Support sanitization of BTF layout for older kernels

Add a FEAT_BTF_LAYOUT feature check which checks if the
kernel supports BTF layout information.  Also sanitize
BTF if it contains layout data but the kernel does not
support it.  The sanitization requires rewriting raw
BTF data to update the header and eliminate the layout
section (since it lies between the types and strings),
so refactor sanitization to do the raw BTF retrieval
and creation of updated BTF, returning that new BTF
on success.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20260326145444.2076244-7-alan.maguire@oracle.com
This commit is contained in:
Alan Maguire
2026-03-26 14:54:41 +00:00
committed by Andrii Nakryiko
parent 6ad8928599
commit 081677d03d
4 changed files with 131 additions and 40 deletions

View File

@@ -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) {