Commit 6fbf129c authored by Donglin Peng's avatar Donglin Peng Committed by Andrii Nakryiko
Browse files

libbpf: Add BTF permutation support for type reordering



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: default avatarDonglin Peng <pengdonglin@xiaomi.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/bpf/20260109130003.3313716-2-dolinux.peng@gmail.com
parent c9c9f6bf
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
@@ -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);
}
+42 −0
Original line number Diff line number Diff line
@@ -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 {
+1 −0
Original line number Diff line number Diff line
@@ -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;