From: pengdonglin <pengdonglin@xiaomi.com>
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.
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Ihor Solodrai <ihor.solodrai@linux.dev>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
---
tools/lib/bpf/btf.c | 119 +++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/btf.h | 36 ++++++++++++
tools/lib/bpf/libbpf.map | 1 +
3 files changed, 156 insertions(+)
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index b136572e889a..ab204ca403dc 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -5887,3 +5887,122 @@ 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;
+};
+
+/* 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_type_id = *type_id;
+
+ /* refer to the base BTF or VOID type */
+ if (new_type_id < p->btf->start_id)
+ return 0;
+
+ if (new_type_id >= btf__type_cnt(p->btf))
+ return -EINVAL;
+
+ *type_id = p->id_map[new_type_id - p->btf->start_id];
+ 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 id;
+
+ if (!OPTS_VALID(opts, btf_permute_opts) || id_map_cnt != btf->nr_types)
+ 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 = 0; 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;
+ /* cannot be mapped to the same ID */
+ if (order_map[id]) {
+ err = -EINVAL;
+ goto done;
+ }
+ order_map[id] = i + btf->start_id;
+ }
+
+ p.btf = btf;
+ p.id_map = id_map;
+ nt = new_types;
+ for (i = 0; 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; 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);
+}
diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
index cc01494d6210..5d560571b1b5 100644
--- a/tools/lib/bpf/btf.h
+++ b/tools/lib/bpf/btf.h
@@ -281,6 +281,42 @@ 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()** performs in-place BTF type rearrangement
+ * @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 for BTF extension updates
+ * @return 0 on success, negative error code on failure
+ *
+ * **btf__permute()** rearranges BTF types according to the specified ID mapping.
+ * The @id_map array defines the new type ID for each original type ID.
+ *
+ * @id_map must include all types from ID `start_id` to `btf__type_cnt(btf) - 1`.
+ * @id_map_cnt should be `btf__type_cnt(btf) - start_id`
+ * The mapping is defined as: `id_map[original_id - start_id] = new_id`
+ *
+ * For base BTF, its `start_id` is fixed to 1, i.e. the VOID type can
+ * not be redefined or remapped and its ID is fixed to 0.
+ *
+ * For split BTF, its `start_id` can be retrieved by calling
+ * `btf__type_cnt(btf__base_btf(btf))`.
+ *
+ * On error, returns negative error code and sets errno:
+ * - `-EINVAL`: Invalid parameters or ID mapping (duplicates, out-of-range)
+ * - `-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 {
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 84fb90a016c9..d18fbcea7578 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -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;
--
2.34.1
On Thu, Dec 18, 2025 at 3:31 AM Donglin Peng <dolinux.peng@gmail.com> wrote:
>
> From: pengdonglin <pengdonglin@xiaomi.com>
>
> 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.
>
> Cc: Eduard Zingerman <eddyz87@gmail.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
> Cc: Alan Maguire <alan.maguire@oracle.com>
> Cc: Ihor Solodrai <ihor.solodrai@linux.dev>
> Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
> Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> Acked-by: Eduard Zingerman <eddyz87@gmail.com>
> ---
> tools/lib/bpf/btf.c | 119 +++++++++++++++++++++++++++++++++++++++
> tools/lib/bpf/btf.h | 36 ++++++++++++
> tools/lib/bpf/libbpf.map | 1 +
> 3 files changed, 156 insertions(+)
>
> diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> index b136572e889a..ab204ca403dc 100644
> --- a/tools/lib/bpf/btf.c
> +++ b/tools/lib/bpf/btf.c
> @@ -5887,3 +5887,122 @@ 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;
> +};
> +
> +/* 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_type_id = *type_id;
> +
> + /* refer to the base BTF or VOID type */
> + if (new_type_id < p->btf->start_id)
> + return 0;
> +
> + if (new_type_id >= btf__type_cnt(p->btf))
> + return -EINVAL;
> +
> + *type_id = p->id_map[new_type_id - p->btf->start_id];
> + 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 id;
> +
> + if (!OPTS_VALID(opts, btf_permute_opts) || id_map_cnt != btf->nr_types)
> + 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 = 0; 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;
> + /* cannot be mapped to the same ID */
> + if (order_map[id]) {
> + err = -EINVAL;
> + goto done;
> + }
> + order_map[id] = i + btf->start_id;
> + }
> +
> + p.btf = btf;
> + p.id_map = id_map;
> + nt = new_types;
> + for (i = 0; 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; 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);
> +}
> diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
> index cc01494d6210..5d560571b1b5 100644
> --- a/tools/lib/bpf/btf.h
> +++ b/tools/lib/bpf/btf.h
> @@ -281,6 +281,42 @@ 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()** performs in-place BTF type rearrangement
> + * @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 for BTF extension updates
> + * @return 0 on success, negative error code on failure
> + *
> + * **btf__permute()** rearranges BTF types according to the specified ID mapping.
> + * The @id_map array defines the new type ID for each original type ID.
> + *
> + * @id_map must include all types from ID `start_id` to `btf__type_cnt(btf) - 1`.
> + * @id_map_cnt should be `btf__type_cnt(btf) - start_id`
> + * The mapping is defined as: `id_map[original_id - start_id] = new_id`
Would you mind paying attention to the feedback I left in [0]? Thank you.
The contract should be id_map[original_id] = new_id for base BTF and
id_map[original_id - btf__type_cnt(base_btf)] = new_id for split BTF.
Special BTF type #0 (VOID) is considered to be part of base BTF,
having id_map[0] = 0 is easy to check and enforce. And then it leaves
us with a simple and logical rule for id_map. For split BTF we make
necessary type ID shifts to avoid tons of wasted memory. But for base
BTF there is no need to shift anything. So mapping the original type
#X to #Y is id_map[X] = Y. Literally, "map X to Y", as simple as that.
[0] https://lore.kernel.org/bpf/CAEf4BzY_k721TBfRSUeq5mB-7fgJhVKCeXVKO-W2EjQ0aS9AgA@mail.gmail.com/
> + *
> + * For base BTF, its `start_id` is fixed to 1, i.e. the VOID type can
> + * not be redefined or remapped and its ID is fixed to 0.
> + *
> + * For split BTF, its `start_id` can be retrieved by calling
> + * `btf__type_cnt(btf__base_btf(btf))`.
> + *
> + * On error, returns negative error code and sets errno:
> + * - `-EINVAL`: Invalid parameters or ID mapping (duplicates, out-of-range)
> + * - `-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 {
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 84fb90a016c9..d18fbcea7578 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -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;
> --
> 2.34.1
>
On Fri, Dec 19, 2025 at 7:02 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Thu, Dec 18, 2025 at 3:31 AM Donglin Peng <dolinux.peng@gmail.com> wrote:
> >
> > From: pengdonglin <pengdonglin@xiaomi.com>
> >
> > 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.
> >
> > Cc: Eduard Zingerman <eddyz87@gmail.com>
> > Cc: Alexei Starovoitov <ast@kernel.org>
> > Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
> > Cc: Alan Maguire <alan.maguire@oracle.com>
> > Cc: Ihor Solodrai <ihor.solodrai@linux.dev>
> > Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
> > Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> > Acked-by: Eduard Zingerman <eddyz87@gmail.com>
> > ---
> > tools/lib/bpf/btf.c | 119 +++++++++++++++++++++++++++++++++++++++
> > tools/lib/bpf/btf.h | 36 ++++++++++++
> > tools/lib/bpf/libbpf.map | 1 +
> > 3 files changed, 156 insertions(+)
> >
> > diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> > index b136572e889a..ab204ca403dc 100644
> > --- a/tools/lib/bpf/btf.c
> > +++ b/tools/lib/bpf/btf.c
> > @@ -5887,3 +5887,122 @@ 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;
> > +};
> > +
> > +/* 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_type_id = *type_id;
> > +
> > + /* refer to the base BTF or VOID type */
> > + if (new_type_id < p->btf->start_id)
> > + return 0;
> > +
> > + if (new_type_id >= btf__type_cnt(p->btf))
> > + return -EINVAL;
> > +
> > + *type_id = p->id_map[new_type_id - p->btf->start_id];
> > + 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 id;
> > +
> > + if (!OPTS_VALID(opts, btf_permute_opts) || id_map_cnt != btf->nr_types)
> > + 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 = 0; 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;
> > + /* cannot be mapped to the same ID */
> > + if (order_map[id]) {
> > + err = -EINVAL;
> > + goto done;
> > + }
> > + order_map[id] = i + btf->start_id;
> > + }
> > +
> > + p.btf = btf;
> > + p.id_map = id_map;
> > + nt = new_types;
> > + for (i = 0; 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; 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);
> > +}
> > diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
> > index cc01494d6210..5d560571b1b5 100644
> > --- a/tools/lib/bpf/btf.h
> > +++ b/tools/lib/bpf/btf.h
> > @@ -281,6 +281,42 @@ 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()** performs in-place BTF type rearrangement
> > + * @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 for BTF extension updates
> > + * @return 0 on success, negative error code on failure
> > + *
> > + * **btf__permute()** rearranges BTF types according to the specified ID mapping.
> > + * The @id_map array defines the new type ID for each original type ID.
> > + *
> > + * @id_map must include all types from ID `start_id` to `btf__type_cnt(btf) - 1`.
> > + * @id_map_cnt should be `btf__type_cnt(btf) - start_id`
> > + * The mapping is defined as: `id_map[original_id - start_id] = new_id`
>
> Would you mind paying attention to the feedback I left in [0]? Thank you.
Apologies for the delayed response, I would like to hear if someone has a
different idea.
>
> The contract should be id_map[original_id] = new_id for base BTF and
> id_map[original_id - btf__type_cnt(base_btf)] = new_id for split BTF.
> Special BTF type #0 (VOID) is considered to be part of base BTF,
> having id_map[0] = 0 is easy to check and enforce. And then it leaves
> us with a simple and logical rule for id_map. For split BTF we make
> necessary type ID shifts to avoid tons of wasted memory. But for base
> BTF there is no need to shift anything. So mapping the original type
> #X to #Y is id_map[X] = Y. Literally, "map X to Y", as simple as that.
>
> [0] https://lore.kernel.org/bpf/CAEf4BzY_k721TBfRSUeq5mB-7fgJhVKCeXVKO-W2EjQ0aS9AgA@mail.gmail.com/
Thanks. I implemented the approach in v6, but it had inconsistent internal
details for base and split BTF. It seems we prioritize external contract
consistency over internal inconsistencies, so I’ll revert to the v6 approach
and refine it for clarity.
>
> > + *
> > + * For base BTF, its `start_id` is fixed to 1, i.e. the VOID type can
> > + * not be redefined or remapped and its ID is fixed to 0.
> > + *
> > + * For split BTF, its `start_id` can be retrieved by calling
> > + * `btf__type_cnt(btf__base_btf(btf))`.
> > + *
> > + * On error, returns negative error code and sets errno:
> > + * - `-EINVAL`: Invalid parameters or ID mapping (duplicates, out-of-range)
> > + * - `-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 {
> > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > index 84fb90a016c9..d18fbcea7578 100644
> > --- a/tools/lib/bpf/libbpf.map
> > +++ b/tools/lib/bpf/libbpf.map
> > @@ -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;
> > --
> > 2.34.1
> >
On Fri, Dec 19, 2025 at 11:14 AM Donglin Peng <dolinux.peng@gmail.com> wrote:
>
> On Fri, Dec 19, 2025 at 7:02 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Thu, Dec 18, 2025 at 3:31 AM Donglin Peng <dolinux.peng@gmail.com> wrote:
> > >
> > > From: pengdonglin <pengdonglin@xiaomi.com>
> > >
> > > 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.
> > >
> > > Cc: Eduard Zingerman <eddyz87@gmail.com>
> > > Cc: Alexei Starovoitov <ast@kernel.org>
> > > Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
> > > Cc: Alan Maguire <alan.maguire@oracle.com>
> > > Cc: Ihor Solodrai <ihor.solodrai@linux.dev>
> > > Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
> > > Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> > > Acked-by: Eduard Zingerman <eddyz87@gmail.com>
> > > ---
> > > tools/lib/bpf/btf.c | 119 +++++++++++++++++++++++++++++++++++++++
> > > tools/lib/bpf/btf.h | 36 ++++++++++++
> > > tools/lib/bpf/libbpf.map | 1 +
> > > 3 files changed, 156 insertions(+)
> > >
> > > diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
> > > index b136572e889a..ab204ca403dc 100644
> > > --- a/tools/lib/bpf/btf.c
> > > +++ b/tools/lib/bpf/btf.c
> > > @@ -5887,3 +5887,122 @@ 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;
> > > +};
> > > +
> > > +/* 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_type_id = *type_id;
> > > +
> > > + /* refer to the base BTF or VOID type */
> > > + if (new_type_id < p->btf->start_id)
> > > + return 0;
> > > +
> > > + if (new_type_id >= btf__type_cnt(p->btf))
> > > + return -EINVAL;
> > > +
> > > + *type_id = p->id_map[new_type_id - p->btf->start_id];
> > > + 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 id;
> > > +
> > > + if (!OPTS_VALID(opts, btf_permute_opts) || id_map_cnt != btf->nr_types)
> > > + 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 = 0; 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;
> > > + /* cannot be mapped to the same ID */
> > > + if (order_map[id]) {
> > > + err = -EINVAL;
> > > + goto done;
> > > + }
> > > + order_map[id] = i + btf->start_id;
> > > + }
> > > +
> > > + p.btf = btf;
> > > + p.id_map = id_map;
> > > + nt = new_types;
> > > + for (i = 0; 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; 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);
> > > +}
> > > diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h
> > > index cc01494d6210..5d560571b1b5 100644
> > > --- a/tools/lib/bpf/btf.h
> > > +++ b/tools/lib/bpf/btf.h
> > > @@ -281,6 +281,42 @@ 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()** performs in-place BTF type rearrangement
> > > + * @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 for BTF extension updates
> > > + * @return 0 on success, negative error code on failure
> > > + *
> > > + * **btf__permute()** rearranges BTF types according to the specified ID mapping.
> > > + * The @id_map array defines the new type ID for each original type ID.
> > > + *
> > > + * @id_map must include all types from ID `start_id` to `btf__type_cnt(btf) - 1`.
> > > + * @id_map_cnt should be `btf__type_cnt(btf) - start_id`
> > > + * The mapping is defined as: `id_map[original_id - start_id] = new_id`
> >
> > Would you mind paying attention to the feedback I left in [0]? Thank you.
>
> Apologies for the delayed response, I would like to hear if someone has a
> different idea.
>
> >
> > The contract should be id_map[original_id] = new_id for base BTF and
> > id_map[original_id - btf__type_cnt(base_btf)] = new_id for split BTF.
> > Special BTF type #0 (VOID) is considered to be part of base BTF,
> > having id_map[0] = 0 is easy to check and enforce. And then it leaves
> > us with a simple and logical rule for id_map. For split BTF we make
> > necessary type ID shifts to avoid tons of wasted memory. But for base
> > BTF there is no need to shift anything. So mapping the original type
> > #X to #Y is id_map[X] = Y. Literally, "map X to Y", as simple as that.
> >
> > [0] https://lore.kernel.org/bpf/CAEf4BzY_k721TBfRSUeq5mB-7fgJhVKCeXVKO-W2EjQ0aS9AgA@mail.gmail.com/
>
> Thanks. I implemented the approach in v6, but it had inconsistent internal
> details for base and split BTF. It seems we prioritize external contract
> consistency over internal inconsistencies, so I’ll revert to the v6 approach
> and refine it for clarity.
Link to v6: https://lore.kernel.org/all/20251117132623.3807094-2-dolinux.peng@gmail.com/
>
> >
> > > + *
> > > + * For base BTF, its `start_id` is fixed to 1, i.e. the VOID type can
> > > + * not be redefined or remapped and its ID is fixed to 0.
> > > + *
> > > + * For split BTF, its `start_id` can be retrieved by calling
> > > + * `btf__type_cnt(btf__base_btf(btf))`.
> > > + *
> > > + * On error, returns negative error code and sets errno:
> > > + * - `-EINVAL`: Invalid parameters or ID mapping (duplicates, out-of-range)
> > > + * - `-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 {
> > > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> > > index 84fb90a016c9..d18fbcea7578 100644
> > > --- a/tools/lib/bpf/libbpf.map
> > > +++ b/tools/lib/bpf/libbpf.map
> > > @@ -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;
> > > --
> > > 2.34.1
> > >
© 2016 - 2025 Red Hat, Inc.