[PATCH bpf-next 07/13] libbpf: generate prog BTF for loader programs
From: KP Singh <kpsingh@kernel.org>
Date: 2026-05-22 02:32:51
Also in:
bpf
Subsystem:
bpf [general] (safe dynamic programs and tools), bpf [library] (libbpf), the rest · Maintainers:
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi, Linus Torvalds
For signed loaders, build a minimal prog BTF containing one FUNC entry for bpf_loader_verify_metadata with a FUNC_PROTO of primitives (int, void *) so the bytes are reproducible across build hosts. In emit_verify_metadata, spill four signed ld_imm64 immediates into a 32-byte stack buffer and invoke the kfunc once with (map, &buf, 32), replacing ~24 inline dword comparisons. Expose the serialized BTF on gen_loader_opts from bpf_gen__finish so bpftool gen can embed it in the lskel. Signed-off-by: KP Singh <kpsingh@kernel.org> --- tools/lib/bpf/bpf_gen_internal.h | 2 + tools/lib/bpf/gen_loader.c | 127 ++++++++++++++++++++++++------- tools/lib/bpf/libbpf.h | 4 +- 3 files changed, 105 insertions(+), 28 deletions(-)
diff --git a/tools/lib/bpf/bpf_gen_internal.h b/tools/lib/bpf/bpf_gen_internal.h
index 49af4260b8e6..e4aed9e99b56 100644
--- a/tools/lib/bpf/bpf_gen_internal.h
+++ b/tools/lib/bpf/bpf_gen_internal.h@@ -52,6 +52,8 @@ struct bpf_gen { int fd_array; int nr_fd_array; int hash_insn_offset[SHA256_DWORD_SIZE]; + struct btf *loader_btf; + int loader_btf_func_id; }; void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps);
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index fee35c26deb8..48ac25c058e3 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c@@ -35,6 +35,7 @@ struct loader_stack { __u32 btf_fd; __u32 inner_map_fd; __u32 prog_fd[MAX_USED_PROGS]; + __u64 metadata_hash[SHA256_DWORD_SIZE]; }; #define stack_off(field) \
@@ -109,7 +110,7 @@ static void emit2(struct bpf_gen *gen, struct bpf_insn insn1, struct bpf_insn in static int add_data(struct bpf_gen *gen, const void *data, __u32 size); static void emit_sys_close_blob(struct bpf_gen *gen, int blob_off); -static void emit_signature_match(struct bpf_gen *gen); +static void emit_verify_metadata(struct bpf_gen *gen); void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps) {
@@ -152,8 +153,68 @@ void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps /* R7 contains the error code from sys_bpf. Copy it into R0 and exit. */ emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_7)); emit(gen, BPF_EXIT_INSN()); - if (OPTS_GET(gen->opts, gen_hash, false)) - emit_signature_match(gen); + if (OPTS_GET(gen->opts, gen_hash, false)) { + int int_id, u32_id, ptr_id, proto_id, err; + + /* Prog BTF built from primitives so the bytes are reproducible + * across build hosts. + */ + gen->loader_btf = btf__new_empty(); + if (libbpf_get_error(gen->loader_btf)) { + gen->error = -ENOMEM; + gen->loader_btf = NULL; + return; + } + if (gen->swapped_endian) { + enum btf_endianness target = + btf__endianness(gen->loader_btf) == BTF_LITTLE_ENDIAN + ? BTF_BIG_ENDIAN : BTF_LITTLE_ENDIAN; + + err = btf__set_endianness(gen->loader_btf, target); + if (err) { + gen->error = err; + return; + } + } + int_id = btf__add_int(gen->loader_btf, "int", 4, BTF_INT_SIGNED); + if (int_id < 0) { + gen->error = int_id; + return; + } + u32_id = btf__add_int(gen->loader_btf, "u32", 4, 0); + if (u32_id < 0) { + gen->error = u32_id; + return; + } + ptr_id = btf__add_ptr(gen->loader_btf, 0); + if (ptr_id < 0) { + gen->error = ptr_id; + return; + } + proto_id = btf__add_func_proto(gen->loader_btf, int_id); + if (proto_id < 0) { + gen->error = proto_id; + return; + } + err = btf__add_func_param(gen->loader_btf, "map", ptr_id); + if (!err) + err = btf__add_func_param(gen->loader_btf, "hash", ptr_id); + if (!err) + err = btf__add_func_param(gen->loader_btf, "hash__sz", u32_id); + if (err) { + gen->error = err; + return; + } + gen->loader_btf_func_id = btf__add_func(gen->loader_btf, + "bpf_loader_verify_metadata", + BTF_FUNC_GLOBAL, proto_id); + if (gen->loader_btf_func_id < 0) { + gen->error = gen->loader_btf_func_id; + gen->loader_btf_func_id = 0; + return; + } + emit_verify_metadata(gen); + } } static int add_data(struct bpf_gen *gen, const void *data, __u32 size)
@@ -398,7 +459,7 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) blob_fd_array_off(gen, i)); emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0)); emit(gen, BPF_EXIT_INSN()); - if (OPTS_GET(gen->opts, gen_hash, false)) + if (!gen->error && OPTS_GET(gen->opts, gen_hash, false)) compute_sha_update_offsets(gen); pr_debug("gen: finish %s\n", errstr(gen->error));
@@ -410,6 +471,19 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) opts->data = gen->data_start; opts->data_sz = gen->data_cur - gen->data_start; + if (gen->loader_btf) { + __u32 btf_sz = 0; + const void *btf_data; + + btf_data = btf__raw_data(gen->loader_btf, &btf_sz); + if (!btf_data) { + gen->error = -ENOMEM; + return gen->error; + } + OPTS_SET(opts, btf, btf_data); + OPTS_SET(opts, btf_sz, btf_sz); + } + /* use target endianness for embedded loader */ if (gen->swapped_endian) { struct bpf_insn *insn = (struct bpf_insn *)opts->insns;
@@ -426,6 +500,7 @@ void bpf_gen__free(struct bpf_gen *gen) { if (!gen) return; + btf__free(gen->loader_btf); free(gen->data_start); free(gen->insn_start); free(gen);
@@ -580,39 +655,37 @@ void bpf_gen__map_create(struct bpf_gen *gen, emit_sys_close_stack(gen, stack_off(inner_map_fd)); } -static void emit_signature_match(struct bpf_gen *gen) +static void emit_verify_metadata(struct bpf_gen *gen) { __s64 off; int i; + /* arg1: metadata struct bpf_map */ + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, + 0, 0, 0, 0)); + + /* arg2: hash buffer on our BPF stack, populated from ld_imm64 + * immediates patched in by compute_sha_update_offsets() before signing. + */ + emit(gen, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10)); + emit(gen, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, stack_off(metadata_hash))); for (i = 0; i < SHA256_DWORD_SIZE; i++) { - emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, - 0, 0, 0, 0)); - emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, i * sizeof(__u64))); gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start; emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0)); - - off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2; - if (is_simm16(off)) { - emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL)); - emit(gen, BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off)); - } else { - gen->error = -ERANGE; - } + emit(gen, BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, + i * sizeof(__u64))); } - /* Reject if the metadata map is not exclusive. Without exclusivity - * the cached map->sha[] verified above can be stale: another BPF - * program with map access could have mutated the contents between - * BPF_OBJ_GET_INFO_BY_FD and loader execution. - */ - emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, - 0, 0, 0, 0)); - emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, SHA256_DWORD_SIZE * sizeof(__u64))); - off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2; + /* arg3: hash length */ + emit(gen, BPF_MOV64_IMM(BPF_REG_3, SHA256_DWORD_SIZE * sizeof(__u64))); + + emit(gen, BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, + BPF_PSEUDO_KFUNC_CALL_PROG_BTF, 0, + gen->loader_btf_func_id)); + emit(gen, BPF_MOV64_REG(BPF_REG_7, BPF_REG_0)); + off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1; if (is_simm16(off)) { - emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL)); - emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, off)); + emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, off)); } else { gen->error = -ERANGE; }
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index bba4e8464396..25906fb695f9 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h@@ -1899,9 +1899,11 @@ struct gen_loader_opts { __u32 data_sz; __u32 insns_sz; bool gen_hash; + const void *btf; + __u32 btf_sz; }; -#define gen_loader_opts__last_field gen_hash +#define gen_loader_opts__last_field btf_sz LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj, struct gen_loader_opts *opts);
--
2.53.0