| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
- /* Copyright (c) 2024, Oracle and/or its affiliates. */
- #ifndef _GNU_SOURCE
- #define _GNU_SOURCE
- #endif
- #ifdef __KERNEL__
- #include <linux/bpf.h>
- #include <linux/bsearch.h>
- #include <linux/btf.h>
- #include <linux/sort.h>
- #include <linux/string.h>
- #include <linux/bpf_verifier.h>
- #define btf_type_by_id (struct btf_type *)btf_type_by_id
- #define btf__type_cnt btf_nr_types
- #define btf__base_btf btf_base_btf
- #define btf__name_by_offset btf_name_by_offset
- #define btf__str_by_offset btf_str_by_offset
- #define btf_kflag btf_type_kflag
- #define calloc(nmemb, sz) kvcalloc(nmemb, sz, GFP_KERNEL | __GFP_NOWARN)
- #define free(ptr) kvfree(ptr)
- #define qsort(base, num, sz, cmp) sort(base, num, sz, cmp, NULL)
- #else
- #include "btf.h"
- #include "bpf.h"
- #include "libbpf.h"
- #include "libbpf_internal.h"
- #endif /* __KERNEL__ */
- struct btf;
- struct btf_relocate {
- struct btf *btf;
- const struct btf *base_btf;
- const struct btf *dist_base_btf;
- unsigned int nr_base_types;
- unsigned int nr_split_types;
- unsigned int nr_dist_base_types;
- int dist_str_len;
- int base_str_len;
- __u32 *id_map;
- __u32 *str_map;
- };
- /* Set temporarily in relocation id_map if distilled base struct/union is
- * embedded in a split BTF struct/union; in such a case, size information must
- * match between distilled base BTF and base BTF representation of type.
- */
- #define BTF_IS_EMBEDDED ((__u32)-1)
- /* <name, size, id> triple used in sorting/searching distilled base BTF. */
- struct btf_name_info {
- const char *name;
- /* set when search requires a size match */
- bool needs_size: 1;
- unsigned int size: 31;
- __u32 id;
- };
- static int btf_relocate_rewrite_type_id(struct btf_relocate *r, __u32 i)
- {
- struct btf_type *t = btf_type_by_id(r->btf, i);
- struct btf_field_iter it;
- __u32 *id;
- int err;
- err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_IDS);
- if (err)
- return err;
- while ((id = btf_field_iter_next(&it)))
- *id = r->id_map[*id];
- return 0;
- }
- /* Simple string comparison used for sorting within BTF, since all distilled
- * types are named. If strings match, and size is non-zero for both elements
- * fall back to using size for ordering.
- */
- static int cmp_btf_name_size(const void *n1, const void *n2)
- {
- const struct btf_name_info *ni1 = n1;
- const struct btf_name_info *ni2 = n2;
- int name_diff = strcmp(ni1->name, ni2->name);
- if (!name_diff && ni1->needs_size && ni2->needs_size)
- return ni2->size - ni1->size;
- return name_diff;
- }
- /* Binary search with a small twist; find leftmost element that matches
- * so that we can then iterate through all exact matches. So for example
- * searching { "a", "bb", "bb", "c" } we would always match on the
- * leftmost "bb".
- */
- static struct btf_name_info *search_btf_name_size(struct btf_name_info *key,
- struct btf_name_info *vals,
- int nelems)
- {
- struct btf_name_info *ret = NULL;
- int high = nelems - 1;
- int low = 0;
- while (low <= high) {
- int mid = (low + high)/2;
- struct btf_name_info *val = &vals[mid];
- int diff = cmp_btf_name_size(key, val);
- if (diff == 0)
- ret = val;
- /* even if found, keep searching for leftmost match */
- if (diff <= 0)
- high = mid - 1;
- else
- low = mid + 1;
- }
- return ret;
- }
- /* If a member of a split BTF struct/union refers to a base BTF
- * struct/union, mark that struct/union id temporarily in the id_map
- * with BTF_IS_EMBEDDED. Members can be const/restrict/volatile/typedef
- * reference types, but if a pointer is encountered, the type is no longer
- * considered embedded.
- */
- static int btf_mark_embedded_composite_type_ids(struct btf_relocate *r, __u32 i)
- {
- struct btf_type *t = btf_type_by_id(r->btf, i);
- struct btf_field_iter it;
- __u32 *id;
- int err;
- if (!btf_is_composite(t))
- return 0;
- err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_IDS);
- if (err)
- return err;
- while ((id = btf_field_iter_next(&it))) {
- __u32 next_id = *id;
- while (next_id) {
- t = btf_type_by_id(r->btf, next_id);
- switch (btf_kind(t)) {
- case BTF_KIND_CONST:
- case BTF_KIND_RESTRICT:
- case BTF_KIND_VOLATILE:
- case BTF_KIND_TYPEDEF:
- case BTF_KIND_TYPE_TAG:
- next_id = t->type;
- break;
- case BTF_KIND_ARRAY: {
- struct btf_array *a = btf_array(t);
- next_id = a->type;
- break;
- }
- case BTF_KIND_STRUCT:
- case BTF_KIND_UNION:
- if (next_id < r->nr_dist_base_types)
- r->id_map[next_id] = BTF_IS_EMBEDDED;
- next_id = 0;
- break;
- default:
- next_id = 0;
- break;
- }
- }
- }
- return 0;
- }
- /* Build a map from distilled base BTF ids to base BTF ids. To do so, iterate
- * through base BTF looking up distilled type (using binary search) equivalents.
- */
- static int btf_relocate_map_distilled_base(struct btf_relocate *r)
- {
- struct btf_name_info *info, *info_end;
- struct btf_type *base_t, *dist_t;
- __u8 *base_name_cnt = NULL;
- int err = 0;
- __u32 id;
- /* generate a sort index array of name/type ids sorted by name for
- * distilled base BTF to speed name-based lookups.
- */
- info = calloc(r->nr_dist_base_types, sizeof(*info));
- if (!info) {
- err = -ENOMEM;
- goto done;
- }
- info_end = info + r->nr_dist_base_types;
- for (id = 0; id < r->nr_dist_base_types; id++) {
- dist_t = btf_type_by_id(r->dist_base_btf, id);
- info[id].name = btf__name_by_offset(r->dist_base_btf, dist_t->name_off);
- info[id].id = id;
- info[id].size = dist_t->size;
- info[id].needs_size = true;
- }
- qsort(info, r->nr_dist_base_types, sizeof(*info), cmp_btf_name_size);
- /* Mark distilled base struct/union members of split BTF structs/unions
- * in id_map with BTF_IS_EMBEDDED; this signals that these types
- * need to match both name and size, otherwise embedding the base
- * struct/union in the split type is invalid.
- */
- for (id = r->nr_dist_base_types; id < r->nr_dist_base_types + r->nr_split_types; id++) {
- err = btf_mark_embedded_composite_type_ids(r, id);
- if (err)
- goto done;
- }
- /* Collect name counts for composite types in base BTF. If multiple
- * instances of a struct/union of the same name exist, we need to use
- * size to determine which to map to since name alone is ambiguous.
- */
- base_name_cnt = calloc(r->base_str_len, sizeof(*base_name_cnt));
- if (!base_name_cnt) {
- err = -ENOMEM;
- goto done;
- }
- for (id = 1; id < r->nr_base_types; id++) {
- base_t = btf_type_by_id(r->base_btf, id);
- if (!btf_is_composite(base_t) || !base_t->name_off)
- continue;
- if (base_name_cnt[base_t->name_off] < 255)
- base_name_cnt[base_t->name_off]++;
- }
- /* Now search base BTF for matching distilled base BTF types. */
- for (id = 1; id < r->nr_base_types; id++) {
- struct btf_name_info *dist_info, base_info = {};
- int dist_kind, base_kind;
- base_t = btf_type_by_id(r->base_btf, id);
- /* distilled base consists of named types only. */
- if (!base_t->name_off)
- continue;
- base_kind = btf_kind(base_t);
- base_info.id = id;
- base_info.name = btf__name_by_offset(r->base_btf, base_t->name_off);
- switch (base_kind) {
- case BTF_KIND_INT:
- case BTF_KIND_FLOAT:
- case BTF_KIND_ENUM:
- case BTF_KIND_ENUM64:
- /* These types should match both name and size */
- base_info.needs_size = true;
- base_info.size = base_t->size;
- break;
- case BTF_KIND_FWD:
- /* No size considerations for fwds. */
- break;
- case BTF_KIND_STRUCT:
- case BTF_KIND_UNION:
- /* Size only needs to be used for struct/union if there
- * are multiple types in base BTF with the same name.
- * If there are multiple _distilled_ types with the same
- * name (a very unlikely scenario), that doesn't matter
- * unless corresponding _base_ types to match them are
- * missing.
- */
- base_info.needs_size = base_name_cnt[base_t->name_off] > 1;
- base_info.size = base_t->size;
- break;
- default:
- continue;
- }
- /* iterate over all matching distilled base types */
- for (dist_info = search_btf_name_size(&base_info, info, r->nr_dist_base_types);
- dist_info != NULL && dist_info < info_end &&
- cmp_btf_name_size(&base_info, dist_info) == 0;
- dist_info++) {
- if (!dist_info->id || dist_info->id >= r->nr_dist_base_types) {
- pr_warn("base BTF id [%d] maps to invalid distilled base BTF id [%d]\n",
- id, dist_info->id);
- err = -EINVAL;
- goto done;
- }
- dist_t = btf_type_by_id(r->dist_base_btf, dist_info->id);
- dist_kind = btf_kind(dist_t);
- /* Validate that the found distilled type is compatible.
- * Do not error out on mismatch as another match may
- * occur for an identically-named type.
- */
- switch (dist_kind) {
- case BTF_KIND_FWD:
- switch (base_kind) {
- case BTF_KIND_FWD:
- if (btf_kflag(dist_t) != btf_kflag(base_t))
- continue;
- break;
- case BTF_KIND_STRUCT:
- if (btf_kflag(base_t))
- continue;
- break;
- case BTF_KIND_UNION:
- if (!btf_kflag(base_t))
- continue;
- break;
- default:
- continue;
- }
- break;
- case BTF_KIND_INT:
- if (dist_kind != base_kind ||
- btf_int_encoding(base_t) != btf_int_encoding(dist_t))
- continue;
- break;
- case BTF_KIND_FLOAT:
- if (dist_kind != base_kind)
- continue;
- break;
- case BTF_KIND_ENUM:
- /* ENUM and ENUM64 are encoded as sized ENUM in
- * distilled base BTF.
- */
- if (base_kind != dist_kind && base_kind != BTF_KIND_ENUM64)
- continue;
- break;
- case BTF_KIND_STRUCT:
- case BTF_KIND_UNION:
- /* size verification is required for embedded
- * struct/unions.
- */
- if (r->id_map[dist_info->id] == BTF_IS_EMBEDDED &&
- base_t->size != dist_t->size)
- continue;
- break;
- default:
- continue;
- }
- if (r->id_map[dist_info->id] &&
- r->id_map[dist_info->id] != BTF_IS_EMBEDDED) {
- /* we already have a match; this tells us that
- * multiple base types of the same name
- * have the same size, since for cases where
- * multiple types have the same name we match
- * on name and size. In this case, we have
- * no way of determining which to relocate
- * to in base BTF, so error out.
- */
- pr_warn("distilled base BTF type '%s' [%u], size %u has multiple candidates of the same size (ids [%u, %u]) in base BTF\n",
- base_info.name, dist_info->id,
- base_t->size, id, r->id_map[dist_info->id]);
- err = -EINVAL;
- goto done;
- }
- /* map id and name */
- r->id_map[dist_info->id] = id;
- r->str_map[dist_t->name_off] = base_t->name_off;
- }
- }
- /* ensure all distilled BTF ids now have a mapping... */
- for (id = 1; id < r->nr_dist_base_types; id++) {
- const char *name;
- if (r->id_map[id] && r->id_map[id] != BTF_IS_EMBEDDED)
- continue;
- dist_t = btf_type_by_id(r->dist_base_btf, id);
- name = btf__name_by_offset(r->dist_base_btf, dist_t->name_off);
- pr_warn("distilled base BTF type '%s' [%d] is not mapped to base BTF id\n",
- name, id);
- err = -EINVAL;
- break;
- }
- done:
- free(base_name_cnt);
- free(info);
- return err;
- }
- /* distilled base should only have named int/float/enum/fwd/struct/union types. */
- static int btf_relocate_validate_distilled_base(struct btf_relocate *r)
- {
- unsigned int i;
- for (i = 1; i < r->nr_dist_base_types; i++) {
- struct btf_type *t = btf_type_by_id(r->dist_base_btf, i);
- int kind = btf_kind(t);
- switch (kind) {
- case BTF_KIND_INT:
- case BTF_KIND_FLOAT:
- case BTF_KIND_ENUM:
- case BTF_KIND_STRUCT:
- case BTF_KIND_UNION:
- case BTF_KIND_FWD:
- if (t->name_off)
- break;
- pr_warn("type [%d], kind [%d] is invalid for distilled base BTF; it is anonymous\n",
- i, kind);
- return -EINVAL;
- default:
- pr_warn("type [%d] in distilled based BTF has unexpected kind [%d]\n",
- i, kind);
- return -EINVAL;
- }
- }
- return 0;
- }
- static int btf_relocate_rewrite_strs(struct btf_relocate *r, __u32 i)
- {
- struct btf_type *t = btf_type_by_id(r->btf, i);
- struct btf_field_iter it;
- __u32 *str_off;
- int off, err;
- err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_STRS);
- if (err)
- return err;
- while ((str_off = btf_field_iter_next(&it))) {
- if (!*str_off)
- continue;
- if (*str_off >= r->dist_str_len) {
- *str_off += r->base_str_len - r->dist_str_len;
- } else {
- off = r->str_map[*str_off];
- if (!off) {
- pr_warn("string '%s' [offset %u] is not mapped to base BTF\n",
- btf__str_by_offset(r->btf, off), *str_off);
- return -ENOENT;
- }
- *str_off = off;
- }
- }
- return 0;
- }
- /* If successful, output of relocation is updated BTF with base BTF pointing
- * at base_btf, and type ids, strings adjusted accordingly.
- */
- int btf_relocate(struct btf *btf, const struct btf *base_btf, __u32 **id_map)
- {
- unsigned int nr_types = btf__type_cnt(btf);
- const struct btf_header *dist_base_hdr;
- const struct btf_header *base_hdr;
- struct btf_relocate r = {};
- int err = 0;
- __u32 id, i;
- r.dist_base_btf = btf__base_btf(btf);
- if (!base_btf || r.dist_base_btf == base_btf)
- return -EINVAL;
- r.nr_dist_base_types = btf__type_cnt(r.dist_base_btf);
- r.nr_base_types = btf__type_cnt(base_btf);
- r.nr_split_types = nr_types - r.nr_dist_base_types;
- r.btf = btf;
- r.base_btf = base_btf;
- r.id_map = calloc(nr_types, sizeof(*r.id_map));
- r.str_map = calloc(btf_header(r.dist_base_btf)->str_len, sizeof(*r.str_map));
- dist_base_hdr = btf_header(r.dist_base_btf);
- base_hdr = btf_header(r.base_btf);
- r.dist_str_len = dist_base_hdr->str_len;
- r.base_str_len = base_hdr->str_len;
- if (!r.id_map || !r.str_map) {
- err = -ENOMEM;
- goto err_out;
- }
- err = btf_relocate_validate_distilled_base(&r);
- if (err)
- goto err_out;
- /* Split BTF ids need to be adjusted as base and distilled base
- * have different numbers of types, changing the start id of split
- * BTF.
- */
- for (id = r.nr_dist_base_types; id < nr_types; id++)
- r.id_map[id] = id + r.nr_base_types - r.nr_dist_base_types;
- /* Build a map from distilled base ids to actual base BTF ids; it is used
- * to update split BTF id references. Also build a str_map mapping from
- * distilled base BTF names to base BTF names.
- */
- err = btf_relocate_map_distilled_base(&r);
- if (err)
- goto err_out;
- /* Next, rewrite type ids in split BTF, replacing split ids with updated
- * ids based on number of types in base BTF, and base ids with
- * relocated ids from base_btf.
- */
- for (i = 0, id = r.nr_dist_base_types; i < r.nr_split_types; i++, id++) {
- err = btf_relocate_rewrite_type_id(&r, id);
- if (err)
- goto err_out;
- }
- /* String offsets now need to be updated using the str_map. */
- for (i = 0; i < r.nr_split_types; i++) {
- err = btf_relocate_rewrite_strs(&r, i + r.nr_dist_base_types);
- if (err)
- goto err_out;
- }
- /* Finally reset base BTF to be base_btf */
- btf_set_base_btf(btf, base_btf);
- if (id_map) {
- *id_map = r.id_map;
- r.id_map = NULL;
- }
- err_out:
- free(r.id_map);
- free(r.str_map);
- return err;
- }
|