summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorEmil Tsalapatis <emil@etsalapatis.com>2026-02-10 13:45:32 -0500
committerAlexei Starovoitov <ast@kernel.org>2026-02-13 14:14:27 -0800
commit728ff167910ef16e97717719c749ddf4064c653b (patch)
tree0aa0fbb7bc5780f0e19420d02670cae885049707 /tools
parent44331bd6a6107a33f8082521b227ffa4ec063a40 (diff)
libbpf: Add gating for arena globals relocation feature
Add feature gating for the arena globals relocation introduced in commit c1f61171d44b. The commit depends on a previous commit in the same patchset that is absent from older kernels (12a1fe6e12db "bpf/verifier: Do not limit maximum direct offset into arena map"). Without this commit, arena globals relocation with arenas >= 512MiB fails to load and breaks libbpf's backwards compatibility. Introduce a libbpf feature to check whether the running kernel allows for full range ldimm64 offset, and only relocate arena globals if it does. Fixes: c1f61171d44b ("libbpf: Move arena globals to the end of the arena") Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20260210184532.255475-1-emil@etsalapatis.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/lib/bpf/features.c64
-rw-r--r--tools/lib/bpf/libbpf.c7
-rw-r--r--tools/lib/bpf/libbpf_internal.h2
3 files changed, 71 insertions, 2 deletions
diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c
index b842b83e2480..b65ab109e3ff 100644
--- a/tools/lib/bpf/features.c
+++ b/tools/lib/bpf/features.c
@@ -506,6 +506,67 @@ static int probe_kern_arg_ctx_tag(int token_fd)
return probe_fd(prog_fd);
}
+static int probe_ldimm64_full_range_off(int token_fd)
+{
+ char log_buf[1024];
+ int prog_fd, map_fd;
+ int ret;
+ LIBBPF_OPTS(bpf_map_create_opts, map_opts,
+ .token_fd = token_fd,
+ .map_flags = token_fd ? BPF_F_TOKEN_FD : 0,
+ );
+ LIBBPF_OPTS(bpf_prog_load_opts, prog_opts,
+ .token_fd = token_fd,
+ .prog_flags = token_fd ? BPF_F_TOKEN_FD : 0,
+ .log_buf = log_buf,
+ .log_size = sizeof(log_buf),
+ );
+ struct bpf_insn insns[] = {
+ BPF_LD_MAP_VALUE(BPF_REG_1, 0, 1UL << 30),
+ BPF_EXIT_INSN(),
+ };
+ int insn_cnt = ARRAY_SIZE(insns);
+
+ map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr", sizeof(int), 1, 1, &map_opts);
+ if (map_fd < 0) {
+ ret = -errno;
+ pr_warn("Error in %s(): %s. Couldn't create simple array map.\n",
+ __func__, errstr(ret));
+ return ret;
+ }
+ insns[0].imm = map_fd;
+
+ prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, "global_reloc", "GPL", insns, insn_cnt, &prog_opts);
+ ret = -errno;
+
+ close(map_fd);
+ close(prog_fd);
+
+ if (prog_fd >= 0) {
+ pr_warn("Error in %s(): Program loading unexpectedly succeeded.\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Feature is allowed if we're not failing with the error message
+ * "direct value offset of %u is not allowed" removed in
+ * 12a1fe6e12db ("bpf/verifier: Do not limit maximum direct offset into arena map").
+ * We should instead fail with "invalid access to map value pointer".
+ * Ensure we match with one of the two and we're not failing with a
+ * different, unexpected message.
+ */
+ if (strstr(log_buf, "direct value offset of"))
+ return 0;
+
+ if (!strstr(log_buf, "invalid access to map value pointer")) {
+ pr_warn("Error in %s(): Program unexpectedly failed with message: %s.\n",
+ __func__, log_buf);
+ return ret;
+ }
+
+ return 1;
+}
+
typedef int (*feature_probe_fn)(int /* token_fd */);
static struct kern_feature_cache feature_cache;
@@ -581,6 +642,9 @@ static struct kern_feature_desc {
[FEAT_BTF_QMARK_DATASEC] = {
"BTF DATASEC names starting from '?'", probe_kern_btf_qmark_datasec,
},
+ [FEAT_LDIMM64_FULL_RANGE_OFF] = {
+ "full range LDIMM64 support", probe_ldimm64_full_range_off,
+ },
};
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 0c8bf0b5cce4..93e59ed8d9a1 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -3009,8 +3009,11 @@ static int init_arena_map_data(struct bpf_object *obj, struct bpf_map *map,
memcpy(obj->arena_data, data, data_sz);
obj->arena_data_sz = data_sz;
- /* place globals at the end of the arena */
- obj->arena_data_off = mmap_sz - data_alloc_sz;
+ /* place globals at the end of the arena (if supported) */
+ if (kernel_supports(obj, FEAT_LDIMM64_FULL_RANGE_OFF))
+ obj->arena_data_off = mmap_sz - data_alloc_sz;
+ else
+ obj->arena_data_off = 0;
/* make bpf_map__init_value() work for ARENA maps */
map->mmaped = obj->arena_data;
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index fc59b21b51b5..974147e8a8aa 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -392,6 +392,8 @@ enum kern_feature_id {
FEAT_ARG_CTX_TAG,
/* Kernel supports '?' at the front of datasec names */
FEAT_BTF_QMARK_DATASEC,
+ /* Kernel supports LDIMM64 imm offsets past 512 MiB. */
+ FEAT_LDIMM64_FULL_RANGE_OFF,
__FEAT_CNT,
};