From: Alexei Starovoitov <ast@xxxxxxxxxx> Given BPF program's BTF perform a linear search through kernel BTFs for a possible candidate. Then wire the result into bpf_core_apply_relo_insn(). Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx> --- kernel/bpf/btf.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index efb7fa2f81a2..aeb591579282 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -25,6 +25,7 @@ #include <linux/kobject.h> #include <linux/sysfs.h> #include <net/sock.h> +#include "../tools/lib/bpf/relo_core.h" /* BTF (BPF Type Format) is the meta data format which describes * the data types of BPF program/map. Hence, it basically focus @@ -6440,9 +6441,144 @@ size_t bpf_core_essential_name_len(const char *name) return n; } +static void bpf_core_free_cands(struct bpf_core_cand_list *cands) +{ + if (!cands) + return; + kfree(cands->cands); + kfree(cands); +} + +static int bpf_core_add_cands(struct bpf_verifier_log *log, + struct bpf_core_cand *local_cand, + size_t local_essent_len, + const struct btf *targ_btf, + int targ_start_id, + struct bpf_core_cand_list *cands) +{ + struct bpf_core_cand *new_cands, *cand; + const struct btf_type *t; + const char *targ_name; + size_t targ_essent_len; + int n, i; + + n = btf_nr_types(targ_btf); + for (i = targ_start_id; i < n; i++) { + t = btf_type_by_id(targ_btf, i); + if (btf_kind(t) != btf_kind(local_cand->t)) + continue; + + targ_name = btf_name_by_offset(targ_btf, t->name_off); + if (str_is_empty(targ_name)) + continue; + + targ_essent_len = bpf_core_essential_name_len(targ_name); + if (targ_essent_len != local_essent_len) + continue; + + if (strncmp(local_cand->name, targ_name, local_essent_len) != 0) + continue; + + bpf_log(log, + "CO-RE relocating [%d] %s %s: found target candidate [%d] %s %s\n", + local_cand->id, btf_type_str(local_cand->t), + local_cand->name, i, btf_type_str(t), targ_name); + new_cands = krealloc(cands->cands, + (cands->len + 1) * sizeof(*cands->cands), GFP_KERNEL); + if (!new_cands) + return -ENOMEM; + + cand = &new_cands[cands->len]; + cand->btf = targ_btf; + cand->t = t; + cand->name = targ_name; + cand->id = i; + + cands->cands = new_cands; + cands->len++; + } + return 0; +} + +static struct bpf_core_cand_list * +bpf_core_find_cands(struct bpf_verifier_log *log, const struct btf *local_btf, u32 local_type_id) +{ + struct bpf_core_cand local_cand = {}; + struct bpf_core_cand_list *cands; + const struct btf *main_btf; + size_t local_essent_len; + struct btf *mod_btf; + int err; + int id; + + local_cand.btf = local_btf; + local_cand.t = btf_type_by_id(local_btf, local_type_id); + if (!local_cand.t) + return ERR_PTR(-EINVAL); + + local_cand.name = btf_name_by_offset(local_btf, local_cand.t->name_off); + if (str_is_empty(local_cand.name)) + return ERR_PTR(-EINVAL); + local_essent_len = bpf_core_essential_name_len(local_cand.name); + + cands = kcalloc(1, sizeof(*cands), GFP_KERNEL); + if (!cands) + return ERR_PTR(-ENOMEM); + + /* Attempt to find target candidates in vmlinux BTF first */ + main_btf = bpf_get_btf_vmlinux(); + err = bpf_core_add_cands(log, &local_cand, local_essent_len, main_btf, 1, cands); + if (err) + goto err_out; + + /* if vmlinux BTF has any candidate, don't go for module BTFs */ + if (cands->len) + return cands; + + /* If candidate is not found in vmlinux's BTF then search in module's BTFs */ + spin_lock_bh(&btf_idr_lock); + idr_for_each_entry(&btf_idr, mod_btf, id) { + if (!btf_is_module(mod_btf)) + continue; + /* linear search could be slow hence unlock/lock + * the IDR to avoiding holding it for too long + */ + btf_get(mod_btf); + spin_unlock_bh(&btf_idr_lock); + err = bpf_core_add_cands(log, &local_cand, local_essent_len, + mod_btf, btf_nr_types(main_btf), cands); + if (err) { + btf_put(mod_btf); + goto err_out; + } + spin_lock_bh(&btf_idr_lock); + btf_put(mod_btf); + } + spin_unlock_bh(&btf_idr_lock); + + return cands; +err_out: + bpf_core_free_cands(cands); + return ERR_PTR(err); +} + int bpf_core_relo_apply(struct bpf_verifier_log *log, const struct btf *btf, const struct bpf_core_relo *relo, int relo_idx, void *insn) { - return -EOPNOTSUPP; + struct bpf_core_cand_list *cands = NULL; + int err; + + if (relo->kind != BPF_CORE_TYPE_ID_LOCAL) { + cands = bpf_core_find_cands(log, btf, relo->type_id); + if (IS_ERR(cands)) { + bpf_log(log, "target candidate search failed for %d\n", + relo->type_id); + return PTR_ERR(cands); + } + } + err = bpf_core_apply_relo_insn((void *)log, insn, relo->insn_off / 8, + relo, relo_idx, btf, cands); + bpf_core_free_cands(cands); + return err; } -- 2.30.2