libbpf: Refactor CO-RE relocs to not assume a single BTF object
authorAndrii Nakryiko <andrii@kernel.org>
Thu, 3 Dec 2020 20:46:24 +0000 (12:46 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 4 Dec 2020 01:38:20 +0000 (17:38 -0800)
Refactor CO-RE relocation candidate search to not expect a single BTF, rather
return all candidate types with their corresponding BTF objects. This will
allow to extend CO-RE relocations to accommodate kernel module BTFs.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20201203204634.1325171-5-andrii@kernel.org
tools/lib/bpf/libbpf.c

index b2e16ef..2e4fa3c 100644 (file)
@@ -462,11 +462,14 @@ struct bpf_object {
        struct list_head list;
 
        struct btf *btf;
+       struct btf_ext *btf_ext;
+
        /* Parse and load BTF vmlinux if any of the programs in the object need
         * it at load time.
         */
        struct btf *btf_vmlinux;
-       struct btf_ext *btf_ext;
+       /* vmlinux BTF override for CO-RE relocations */
+       struct btf *btf_vmlinux_override;
 
        void *priv;
        bpf_object_clear_priv_t clear_priv;
@@ -4600,46 +4603,43 @@ static size_t bpf_core_essential_name_len(const char *name)
        return n;
 }
 
-/* dynamically sized list of type IDs */
-struct ids_vec {
-       __u32 *data;
+struct core_cand
+{
+       const struct btf *btf;
+       const struct btf_type *t;
+       const char *name;
+       __u32 id;
+};
+
+/* dynamically sized list of type IDs and its associated struct btf */
+struct core_cand_list {
+       struct core_cand *cands;
        int len;
 };
 
-static void bpf_core_free_cands(struct ids_vec *cand_ids)
+static void bpf_core_free_cands(struct core_cand_list *cands)
 {
-       free(cand_ids->data);
-       free(cand_ids);
+       free(cands->cands);
+       free(cands);
 }
 
-static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf,
-                                          __u32 local_type_id,
-                                          const struct btf *targ_btf)
+static int bpf_core_add_cands(struct core_cand *local_cand,
+                             size_t local_essent_len,
+                             const struct btf *targ_btf,
+                             const char *targ_btf_name,
+                             int targ_start_id,
+                             struct core_cand_list *cands)
 {
-       size_t local_essent_len, targ_essent_len;
-       const char *local_name, *targ_name;
-       const struct btf_type *t, *local_t;
-       struct ids_vec *cand_ids;
-       __u32 *new_ids;
-       int i, err, n;
-
-       local_t = btf__type_by_id(local_btf, local_type_id);
-       if (!local_t)
-               return ERR_PTR(-EINVAL);
-
-       local_name = btf__name_by_offset(local_btf, local_t->name_off);
-       if (str_is_empty(local_name))
-               return ERR_PTR(-EINVAL);
-       local_essent_len = bpf_core_essential_name_len(local_name);
-
-       cand_ids = calloc(1, sizeof(*cand_ids));
-       if (!cand_ids)
-               return ERR_PTR(-ENOMEM);
+       struct core_cand *new_cands, *cand;
+       const struct btf_type *t;
+       const char *targ_name;
+       size_t targ_essent_len;
+       int n, i;
 
        n = btf__get_nr_types(targ_btf);
-       for (i = 1; i <= n; i++) {
+       for (i = targ_start_id; i <= n; i++) {
                t = btf__type_by_id(targ_btf, i);
-               if (btf_kind(t) != btf_kind(local_t))
+               if (btf_kind(t) != btf_kind(local_cand->t))
                        continue;
 
                targ_name = btf__name_by_offset(targ_btf, t->name_off);
@@ -4650,25 +4650,62 @@ static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf,
                if (targ_essent_len != local_essent_len)
                        continue;
 
-               if (strncmp(local_name, targ_name, local_essent_len) == 0) {
-                       pr_debug("CO-RE relocating [%d] %s %s: found target candidate [%d] %s %s\n",
-                                local_type_id, btf_kind_str(local_t),
-                                local_name, i, btf_kind_str(t), targ_name);
-                       new_ids = libbpf_reallocarray(cand_ids->data,
-                                                     cand_ids->len + 1,
-                                                     sizeof(*cand_ids->data));
-                       if (!new_ids) {
-                               err = -ENOMEM;
-                               goto err_out;
-                       }
-                       cand_ids->data = new_ids;
-                       cand_ids->data[cand_ids->len++] = i;
-               }
+               if (strncmp(local_cand->name, targ_name, local_essent_len) != 0)
+                       continue;
+
+               pr_debug("CO-RE relocating [%d] %s %s: found target candidate [%d] %s %s in [%s]\n",
+                        local_cand->id, btf_kind_str(local_cand->t),
+                        local_cand->name, i, btf_kind_str(t), targ_name,
+                        targ_btf_name);
+               new_cands = libbpf_reallocarray(cands->cands, cands->len + 1,
+                                             sizeof(*cands->cands));
+               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 cand_ids;
-err_out:
-       bpf_core_free_cands(cand_ids);
-       return ERR_PTR(err);
+       return 0;
+}
+
+static struct core_cand_list *
+bpf_core_find_cands(struct bpf_object *obj, const struct btf *local_btf, __u32 local_type_id)
+{
+       struct core_cand local_cand = {};
+       struct core_cand_list *cands;
+       size_t local_essent_len;
+       int err;
+
+       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 = calloc(1, sizeof(*cands));
+       if (!cands)
+               return ERR_PTR(-ENOMEM);
+
+       /* Attempt to find target candidates in vmlinux BTF first */
+       err = bpf_core_add_cands(&local_cand, local_essent_len,
+                                obj->btf_vmlinux_override ?: obj->btf_vmlinux,
+                                "vmlinux", 1, cands);
+       if (err) {
+               bpf_core_free_cands(cands);
+               return ERR_PTR(err);
+       }
+
+       return cands;
 }
 
 /* Check two types for compatibility for the purpose of field access
@@ -5661,7 +5698,6 @@ static int bpf_core_apply_relo(struct bpf_program *prog,
                               const struct bpf_core_relo *relo,
                               int relo_idx,
                               const struct btf *local_btf,
-                              const struct btf *targ_btf,
                               struct hashmap *cand_cache)
 {
        struct bpf_core_spec local_spec, cand_spec, targ_spec = {};
@@ -5669,8 +5705,8 @@ static int bpf_core_apply_relo(struct bpf_program *prog,
        struct bpf_core_relo_res cand_res, targ_res;
        const struct btf_type *local_type;
        const char *local_name;
-       struct ids_vec *cand_ids;
-       __u32 local_id, cand_id;
+       struct core_cand_list *cands = NULL;
+       __u32 local_id;
        const char *spec_str;
        int i, j, err;
 
@@ -5717,24 +5753,24 @@ static int bpf_core_apply_relo(struct bpf_program *prog,
                return -EOPNOTSUPP;
        }
 
-       if (!hashmap__find(cand_cache, type_key, (void **)&cand_ids)) {
-               cand_ids = bpf_core_find_cands(local_btf, local_id, targ_btf);
-               if (IS_ERR(cand_ids)) {
+       if (!hashmap__find(cand_cache, type_key, (void **)&cands)) {
+               cands = bpf_core_find_cands(prog->obj, local_btf, local_id);
+               if (IS_ERR(cands)) {
                        pr_warn("prog '%s': relo #%d: target candidate search failed for [%d] %s %s: %ld",
                                prog->name, relo_idx, local_id, btf_kind_str(local_type),
-                               local_name, PTR_ERR(cand_ids));
-                       return PTR_ERR(cand_ids);
+                               local_name, PTR_ERR(cands));
+                       return PTR_ERR(cands);
                }
-               err = hashmap__set(cand_cache, type_key, cand_ids, NULL, NULL);
+               err = hashmap__set(cand_cache, type_key, cands, NULL, NULL);
                if (err) {
-                       bpf_core_free_cands(cand_ids);
+                       bpf_core_free_cands(cands);
                        return err;
                }
        }
 
-       for (i = 0, j = 0; i < cand_ids->len; i++) {
-               cand_id = cand_ids->data[i];
-               err = bpf_core_spec_match(&local_spec, targ_btf, cand_id, &cand_spec);
+       for (i = 0, j = 0; i < cands->len; i++) {
+               err = bpf_core_spec_match(&local_spec, cands->cands[i].btf,
+                                         cands->cands[i].id, &cand_spec);
                if (err < 0) {
                        pr_warn("prog '%s': relo #%d: error matching candidate #%d ",
                                prog->name, relo_idx, i);
@@ -5778,7 +5814,7 @@ static int bpf_core_apply_relo(struct bpf_program *prog,
                        return -EINVAL;
                }
 
-               cand_ids->data[j++] = cand_spec.root_type_id;
+               cands->cands[j++] = cands->cands[i];
        }
 
        /*
@@ -5790,7 +5826,7 @@ static int bpf_core_apply_relo(struct bpf_program *prog,
         * depending on relo's kind.
         */
        if (j > 0)
-               cand_ids->len = j;
+               cands->len = j;
 
        /*
         * If no candidates were found, it might be both a programmer error,
@@ -5834,20 +5870,19 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
        struct hashmap_entry *entry;
        struct hashmap *cand_cache = NULL;
        struct bpf_program *prog;
-       struct btf *targ_btf;
        const char *sec_name;
        int i, err = 0, insn_idx, sec_idx;
 
        if (obj->btf_ext->core_relo_info.len == 0)
                return 0;
 
-       if (targ_btf_path)
-               targ_btf = btf__parse(targ_btf_path, NULL);
-       else
-               targ_btf = obj->btf_vmlinux;
-       if (IS_ERR_OR_NULL(targ_btf)) {
-               pr_warn("failed to get target BTF: %ld\n", PTR_ERR(targ_btf));
-               return PTR_ERR(targ_btf);
+       if (targ_btf_path) {
+               obj->btf_vmlinux_override = btf__parse(targ_btf_path, NULL);
+               if (IS_ERR_OR_NULL(obj->btf_vmlinux_override)) {
+                       err = PTR_ERR(obj->btf_vmlinux_override);
+                       pr_warn("failed to parse target BTF: %d\n", err);
+                       return err;
+               }
        }
 
        cand_cache = hashmap__new(bpf_core_hash_fn, bpf_core_equal_fn, NULL);
@@ -5899,8 +5934,7 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
                        if (!prog->load)
                                continue;
 
-                       err = bpf_core_apply_relo(prog, rec, i, obj->btf,
-                                                 targ_btf, cand_cache);
+                       err = bpf_core_apply_relo(prog, rec, i, obj->btf, cand_cache);
                        if (err) {
                                pr_warn("prog '%s': relo #%d: failed to relocate: %d\n",
                                        prog->name, i, err);
@@ -5911,8 +5945,9 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
 
 out:
        /* obj->btf_vmlinux is freed at the end of object load phase */
-       if (targ_btf != obj->btf_vmlinux)
-               btf__free(targ_btf);
+       btf__free(obj->btf_vmlinux_override);
+       obj->btf_vmlinux_override = NULL;
+
        if (!IS_ERR_OR_NULL(cand_cache)) {
                hashmap__for_each_entry(cand_cache, entry, i) {
                        bpf_core_free_cands(entry->value);