]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
libbpf: Add BTF permutation support for type reordering
authorDonglin Peng <pengdonglin@xiaomi.com>
Fri, 9 Jan 2026 12:59:53 +0000 (20:59 +0800)
committerAndrii Nakryiko <andrii@kernel.org>
Wed, 14 Jan 2026 00:10:39 +0000 (16:10 -0800)
Introduce btf__permute() API to allow in-place rearrangement of BTF types.
This function reorganizes BTF type order according to a provided array of
type IDs, updating all type references to maintain consistency.

Signed-off-by: Donglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/bpf/20260109130003.3313716-2-dolinux.peng@gmail.com
tools/lib/bpf/btf.c
tools/lib/bpf/btf.h
tools/lib/bpf/libbpf.map

index b136572e889aba5a469dc8dc4e85112fa9a474cd..bf75f770d29a3c51353082a762a017d466d8b7d0 100644 (file)
@@ -5887,3 +5887,136 @@ int btf__relocate(struct btf *btf, const struct btf *base_btf)
                btf->owns_base = false;
        return libbpf_err(err);
 }
+
+struct btf_permute {
+       struct btf *btf;
+       __u32 *id_map;
+       __u32 start_offs;
+};
+
+/* Callback function to remap individual type ID references */
+static int btf_permute_remap_type_id(__u32 *type_id, void *ctx)
+{
+       struct btf_permute *p = ctx;
+       __u32 new_id = *type_id;
+
+       /* refer to the base BTF or VOID type */
+       if (new_id < p->btf->start_id)
+               return 0;
+
+       if (new_id >= btf__type_cnt(p->btf))
+               return -EINVAL;
+
+       *type_id = p->id_map[new_id - p->btf->start_id + p->start_offs];
+       return 0;
+}
+
+int btf__permute(struct btf *btf, __u32 *id_map, __u32 id_map_cnt,
+                const struct btf_permute_opts *opts)
+{
+       struct btf_permute p;
+       struct btf_ext *btf_ext;
+       void *nt, *new_types = NULL;
+       __u32 *order_map = NULL;
+       int err = 0, i;
+       __u32 n, id, start_offs = 0;
+
+       if (!OPTS_VALID(opts, btf_permute_opts))
+               return libbpf_err(-EINVAL);
+
+       if (btf__base_btf(btf)) {
+               n = btf->nr_types;
+       } else {
+               if (id_map[0] != 0)
+                       return libbpf_err(-EINVAL);
+               n = btf__type_cnt(btf);
+               start_offs = 1;
+       }
+
+       if (id_map_cnt != n)
+               return libbpf_err(-EINVAL);
+
+       /* record the sequence of types */
+       order_map = calloc(id_map_cnt, sizeof(*id_map));
+       if (!order_map) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       new_types = calloc(btf->hdr->type_len, 1);
+       if (!new_types) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       if (btf_ensure_modifiable(btf)) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       for (i = start_offs; i < id_map_cnt; i++) {
+               id = id_map[i];
+               if (id < btf->start_id || id >= btf__type_cnt(btf)) {
+                       err = -EINVAL;
+                       goto done;
+               }
+               id -= btf->start_id - start_offs;
+               /* cannot be mapped to the same ID */
+               if (order_map[id]) {
+                       err = -EINVAL;
+                       goto done;
+               }
+               order_map[id] = i + btf->start_id - start_offs;
+       }
+
+       p.btf = btf;
+       p.id_map = id_map;
+       p.start_offs = start_offs;
+       nt = new_types;
+       for (i = start_offs; i < id_map_cnt; i++) {
+               struct btf_field_iter it;
+               const struct btf_type *t;
+               __u32 *type_id;
+               int type_size;
+
+               id = order_map[i];
+               t = btf__type_by_id(btf, id);
+               type_size = btf_type_size(t);
+               memcpy(nt, t, type_size);
+
+               /* fix up referenced IDs for BTF */
+               err = btf_field_iter_init(&it, nt, BTF_FIELD_ITER_IDS);
+               if (err)
+                       goto done;
+               while ((type_id = btf_field_iter_next(&it))) {
+                       err = btf_permute_remap_type_id(type_id, &p);
+                       if (err)
+                               goto done;
+               }
+
+               nt += type_size;
+       }
+
+       /* fix up referenced IDs for btf_ext */
+       btf_ext = OPTS_GET(opts, btf_ext, NULL);
+       if (btf_ext) {
+               err = btf_ext_visit_type_ids(btf_ext, btf_permute_remap_type_id, &p);
+               if (err)
+                       goto done;
+       }
+
+       for (nt = new_types, i = 0; i < id_map_cnt - start_offs; i++) {
+               btf->type_offs[i] = nt - new_types;
+               nt += btf_type_size(nt);
+       }
+
+       free(order_map);
+       free(btf->types_data);
+       btf->types_data = new_types;
+       return 0;
+
+done:
+       free(order_map);
+       free(new_types);
+       return libbpf_err(err);
+}
index cc01494d62107ef7e36898c99ec1af1ac09a68b4..b30008c267c0d8a0471425a3869ce2028bc2d980 100644 (file)
@@ -281,6 +281,48 @@ LIBBPF_API int btf__dedup(struct btf *btf, const struct btf_dedup_opts *opts);
  */
 LIBBPF_API int btf__relocate(struct btf *btf, const struct btf *base_btf);
 
+struct btf_permute_opts {
+       size_t sz;
+       /* optional .BTF.ext info along the main BTF info */
+       struct btf_ext *btf_ext;
+       size_t :0;
+};
+#define btf_permute_opts__last_field btf_ext
+
+/**
+ * @brief **btf__permute()** rearranges BTF types in-place according to a specified ID mapping
+ * @param btf BTF object to permute
+ * @param id_map Array mapping original type IDs to new IDs
+ * @param id_map_cnt Number of elements in @id_map
+ * @param opts Optional parameters, including BTF extension data for reference updates
+ * @return 0 on success, negative error code on failure
+ *
+ * **btf__permute()** reorders BTF types based on the provided @id_map array,
+ * updating all internal type references to maintain consistency. The function
+ * operates in-place, modifying the BTF object directly.
+ *
+ * For **base BTF**:
+ * - @id_map must include all types from ID 0 to `btf__type_cnt(btf) - 1`
+ * - @id_map_cnt must be `btf__type_cnt(btf)`
+ * - Mapping is defined as `id_map[original_id] = new_id`
+ * - `id_map[0]` must be 0 (void type cannot be moved)
+ *
+ * For **split BTF**:
+ * - @id_map must include only split types (types added on top of the base BTF)
+ * - @id_map_cnt must be `btf__type_cnt(btf) - btf__type_cnt(btf__base_btf(btf))`
+ * - Mapping is defined as `id_map[original_id - start_id] = new_id`
+ * - `start_id` equals `btf__type_cnt(btf__base_btf(btf))`
+ *
+ * After permutation, all type references within the BTF data and optional
+ * BTF extension (if provided via @opts) are updated automatically.
+ *
+ * On error, returns a negative error code and sets errno:
+ *   - `-EINVAL`: Invalid parameters or invalid ID mapping
+ *   - `-ENOMEM`: Memory allocation failure
+ */
+LIBBPF_API int btf__permute(struct btf *btf, __u32 *id_map, __u32 id_map_cnt,
+                           const struct btf_permute_opts *opts);
+
 struct btf_dump;
 
 struct btf_dump_opts {
index 84fb90a016c9c042179e42ef521ee714d4123c5f..d18fbcea7578d5f75d5fc925ea9edff52e423faf 100644 (file)
@@ -453,4 +453,5 @@ LIBBPF_1.7.0 {
                bpf_map__exclusive_program;
                bpf_prog_assoc_struct_ops;
                bpf_program__assoc_struct_ops;
+               btf__permute;
 } LIBBPF_1.6.0;