When adding a kfunc prototype to BTF, check for the flags indicating bpf_arena pointers and emit a type tag encoding __attribute__((address_space(1))) for them. This also requires updating BTF type ids in the btf_encoder_func_state, which is done as a side effect in the tagging functions. This feature depends on recent update in libbpf, supporting arbitrarty attribute encoding [1]. [1] https://lore.kernel.org/bpf/20250130201239.1429648-1-ihor.solodrai@xxxxxxxxx/ Signed-off-by: Ihor Solodrai <ihor.solodrai@xxxxxxxxx> Reviewed-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- btf_encoder.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/btf_encoder.c b/btf_encoder.c index 965e8f0..3cec106 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -40,7 +40,13 @@ #define BTF_SET8_KFUNCS (1 << 0) #define BTF_KFUNC_TYPE_TAG "bpf_kfunc" #define BTF_FASTCALL_TAG "bpf_fastcall" -#define KF_FASTCALL (1 << 12) +#define BPF_ARENA_ATTR "address_space(1)" + +/* kfunc flags, see include/linux/btf.h in the kernel source */ +#define KF_FASTCALL (1 << 12) +#define KF_ARENA_RET (1 << 13) +#define KF_ARENA_ARG1 (1 << 14) +#define KF_ARENA_ARG2 (1 << 15) struct btf_id_and_flag { uint32_t id; @@ -731,6 +737,78 @@ static int32_t btf_encoder__tag_type(struct btf_encoder *encoder, uint32_t tag_t return encoder->type_id_off + tag_type; } +#if LIBBPF_MAJOR_VERSION >= 1 && LIBBPF_MINOR_VERSION >= 6 +static int btf__tag_bpf_arena_ptr(struct btf *btf, int ptr_id) +{ + const struct btf_type *ptr; + int tagged_type_id; + + ptr = btf__type_by_id(btf, ptr_id); + if (!btf_is_ptr(ptr)) + return -EINVAL; + + tagged_type_id = btf__add_type_attr(btf, BPF_ARENA_ATTR, ptr->type); + if (tagged_type_id < 0) + return tagged_type_id; + + return btf__add_ptr(btf, tagged_type_id); +} + +static int btf__tag_bpf_arena_arg(struct btf *btf, struct btf_encoder_func_state *state, int idx) +{ + int id; + + if (state->nr_parms <= idx) + return -EINVAL; + + id = btf__tag_bpf_arena_ptr(btf, state->parms[idx].type_id); + if (id < 0) { + btf__log_err(btf, BTF_KIND_TYPE_TAG, BPF_ARENA_ATTR, true, id, + "Error adding BPF_ARENA_ATTR for an argument of kfunc '%s'", state->elf->name); + return id; + } + state->parms[idx].type_id = id; + + return id; +} + +static int btf__add_bpf_arena_type_tags(struct btf *btf, struct btf_encoder_func_state *state) +{ + uint32_t flags = state->elf->kfunc_flags; + int ret_type_id; + int err; + + if (KF_ARENA_RET & flags) { + ret_type_id = btf__tag_bpf_arena_ptr(btf, state->ret_type_id); + if (ret_type_id < 0) { + btf__log_err(btf, BTF_KIND_TYPE_TAG, BPF_ARENA_ATTR, true, ret_type_id, + "Error adding BPF_ARENA_ATTR for return type of kfunc '%s'", state->elf->name); + return ret_type_id; + } + state->ret_type_id = ret_type_id; + } + + if (KF_ARENA_ARG1 & flags) { + err = btf__tag_bpf_arena_arg(btf, state, 0); + if (err < 0) + return err; + } + + if (KF_ARENA_ARG2 & flags) { + err = btf__tag_bpf_arena_arg(btf, state, 1); + if (err < 0) + return err; + } + + return 0; +} +#endif // LIBBPF_MAJOR_VERSION >= 1 && LIBBPF_MINOR_VERSION >= 6 + +static inline bool is_kfunc_state(struct btf_encoder_func_state *state) +{ + return state && state->elf && state->elf->kfunc; +} + static int32_t btf_encoder__add_func_proto(struct btf_encoder *encoder, struct ftype *ftype, struct btf_encoder_func_state *state) { @@ -744,6 +822,12 @@ static int32_t btf_encoder__add_func_proto(struct btf_encoder *encoder, struct f assert(ftype != NULL || state != NULL); +#if LIBBPF_MAJOR_VERSION >= 1 && LIBBPF_MINOR_VERSION >= 6 + if (is_kfunc_state(state) && encoder->tag_kfuncs) + if (btf__add_bpf_arena_type_tags(encoder->btf, state) < 0) + return -1; +#endif + /* add btf_type for func_proto */ if (ftype) { btf = encoder->btf; -- 2.48.1