[PATCH] GSoC: retrieving BTF information for map manipuate

ANOLASC anolasc13 at gmail.com
Fri Sep 30 13:17:32 UTC 2022


From: SuHsueyu <anolasc13 at gmail.com>

Unit tests will be in another patch

---
 src/Makefile.am |   2 +
 src/bpf.c       |   7 +
 src/bpf_btf.c   | 728 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/bpf_btf.h   |  85 ++++++
 4 files changed, 822 insertions(+)
 create mode 100644 src/bpf_btf.c
 create mode 100644 src/bpf_btf.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 03da75f5f..60e41db18 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,6 +65,8 @@ libstrace_a_SOURCES =	\
 	block.c		\
 	bpf.c		\
 	bpf_attr.h	\
+	bpf_btf.h	\
+	bpf_btf.c	\
 	bpf_filter.c	\
 	bpf_filter.h	\
 	bpf_fprog.h	\
diff --git a/src/bpf.c b/src/bpf.c
index 7bdeb4f65..3ab542a4b 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -11,6 +11,8 @@
 
 #ifdef HAVE_LINUX_BPF_H
 # include <linux/bpf.h>
+# include <linux/btf.h>
+# include "bpf_btf.h"
 #endif
 #include <linux/filter.h>
 
@@ -266,6 +268,7 @@ BEGIN_BPF_CMD_DECODER(BPF_MAP_LOOKUP_ELEM)
 {
 	tprint_struct_begin();
 	PRINT_FIELD_FD(attr, map_fd, tcp);
+	print_map_btf(tcp, attr.map_fd);
 	tprint_struct_next();
 	PRINT_FIELD_ADDR64(attr, key);
 	tprint_struct_next();
@@ -306,6 +309,7 @@ BEGIN_BPF_CMD_DECODER(BPF_MAP_GET_NEXT_KEY)
 {
 	tprint_struct_begin();
 	PRINT_FIELD_FD(attr, map_fd, tcp);
+	print_map_btf(tcp, attr.map_fd);
 	tprint_struct_next();
 	PRINT_FIELD_ADDR64(attr, key);
 	tprint_struct_next();
@@ -317,6 +321,7 @@ BEGIN_BPF_CMD_DECODER(BPF_MAP_FREEZE)
 {
 	tprint_struct_begin();
 	PRINT_FIELD_FD(attr, map_fd, tcp);
+	print_map_btf(tcp, attr.map_fd);
 }
 END_BPF_CMD_DECODER(RVAL_DECODED)
 
@@ -695,6 +700,7 @@ print_bpf_map_info(struct tcb * const tcp, uint32_t bpf_fd,
 	tprint_struct_next();
 	PRINT_FIELD_U(info, btf_value_type_id);
 
+	tprint_struct_next();
 	decode_attr_extra_data(tcp, info_buf, size, bpf_map_info_struct_size);
 
 print_bpf_map_info_end:
@@ -1305,6 +1311,7 @@ BEGIN_BPF_CMD_DECODER(BPF_MAP_UPDATE_BATCH)
 		PRINT_FIELD_U(attr, count);
 		tprint_struct_next();
 		PRINT_FIELD_FD(attr, map_fd, tcp);
+		print_map_btf(tcp, attr.map_fd);
 		tprint_struct_next();
 		PRINT_FIELD_FLAGS(attr, elem_flags,
 				  bpf_map_lookup_elem_flags, "BPF_???");
diff --git a/src/bpf_btf.c b/src/bpf_btf.c
new file mode 100644
index 000000000..80806f642
--- /dev/null
+++ b/src/bpf_btf.c
@@ -0,0 +1,728 @@
+#include "bpf_btf.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <byteswap.h>
+
+#define BTF_MAX_NR_TYPES 0x7fffffffU
+#define BTF_MAX_STR_OFFSET 0x7fffffffU
+
+static inline uint64_t ptr_to_u64(const void *ptr)
+{
+	return (uint64_t) (unsigned long) ptr;
+}
+
+static inline int bpf_err_errno(int ret)
+{
+	return ret < 0 ? errno : ret;
+}
+
+static int bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size)
+{
+    return syscall(__NR_bpf, cmd, attr, size);
+}
+
+int bpf_obj_get_info_by_fd(int fd, void *info, uint32_t *info_len)
+{
+	union bpf_attr attr;
+	int err;
+
+	memset(&attr, 0, sizeof(attr));
+	
+	attr.info.bpf_fd = fd;
+	attr.info.info_len = *info_len;
+	attr.info.info = ptr_to_u64(info);
+	err = bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
+	if (!err)
+		*info_len = attr.info.info_len;
+	return bpf_err_errno(err);
+}
+
+int bpf_btf_get_fd_by_id(uint32_t id)
+{
+	union bpf_attr attr;
+
+	memset(&attr, 0, sizeof(attr));
+	attr.btf_id = id;
+
+	return bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+static const char* __btf_kind_str(uint16_t kind)
+{
+	switch (kind) {
+	case BTF_KIND_UNKN: return "void";
+	case BTF_KIND_INT: return "int";
+	case BTF_KIND_PTR: return "ptr";
+	case BTF_KIND_ARRAY: return "array";
+	case BTF_KIND_STRUCT: return "struct";
+	case BTF_KIND_UNION: return "union";
+	case BTF_KIND_ENUM: return "enum";
+	case BTF_KIND_FWD: return "fwd";
+	case BTF_KIND_TYPEDEF: return "typedef";
+	case BTF_KIND_VOLATILE: return "volatile";
+	case BTF_KIND_CONST: return "const";
+	case BTF_KIND_RESTRICT: return "restrict";
+	case BTF_KIND_FUNC: return "func";
+	case BTF_KIND_FUNC_PROTO: return "func_proto";
+	case BTF_KIND_VAR: return "var";
+	case BTF_KIND_DATASEC: return "datasec";
+	case BTF_KIND_FLOAT: return "float";
+	case BTF_KIND_DECL_TAG: return "decl_tag";
+	case BTF_KIND_TYPE_TAG: return "type_tag";
+	case BTF_KIND_ENUM64: return "enum64";
+	default: return "unknown";
+	}
+}
+
+static inline uint16_t btf_kind(const struct btf_type* btf_t)
+{
+	return BTF_INFO_KIND(btf_t->info);
+}
+
+const char* btf_kind_str(const struct btf_type* btf_t)
+{
+	return __btf_kind_str(btf_kind(btf_t));
+}
+
+static struct btf_type* btf_type_by_id(const struct btf* btf, uint32_t type_id)
+{
+	if (type_id == 0)
+		return NULL;
+	if (type_id < (uint32_t)btf->start_id)
+		return btf_type_by_id(btf->base_btf, type_id);
+	return btf->types_data + btf->type_offs[type_id - btf->start_id];
+}
+
+const struct btf_type* get_btf_type_by_id(const struct btf* btf, uint32_t type_id)
+{
+	if (type_id >= (uint32_t)btf->start_id + btf->nr_types)
+		return errno = EINVAL, NULL;
+	return btf_type_by_id((struct btf*)btf, type_id);
+}
+
+static void btf_bswap_hdr(struct btf_header *h)
+{
+	h->magic = bswap_16(h->magic);
+	h->hdr_len = bswap_32(h->hdr_len);
+	h->type_off = bswap_32(h->type_off);
+	h->type_len = bswap_32(h->type_len);
+	h->str_off = bswap_32(h->str_off);
+	h->str_len = bswap_32(h->str_len);
+}
+
+static uint32_t btf__type_cnt(const struct btf *btf)
+{
+	return btf->start_id + btf->nr_types;
+}
+
+static int btf_parse_hdr(struct btf *btf)
+{
+	struct btf_header *hdr = btf->hdr;
+	uint32_t meta_left;
+
+	if (btf->raw_size < sizeof(struct btf_header)) {
+		error_msg("BTF header not found\n");
+		return -EINVAL;
+	}
+
+	if (hdr->magic == bswap_16(BTF_MAGIC)) {
+		btf->swapped_endian = true;
+		if (bswap_32(hdr->hdr_len) != sizeof(struct btf_header)) {
+			error_msg("Can't load BTF with non-native endianness due to unsupported header length %u\n",
+				bswap_32(hdr->hdr_len));
+			return -ENOTSUP;
+		}
+		btf_bswap_hdr(hdr);
+	} else if (hdr->magic != BTF_MAGIC) {
+		error_msg("Invalid BTF magic: %x\n", hdr->magic);
+		return -EINVAL;
+	}
+
+	if (btf->raw_size < hdr->hdr_len) {
+		error_msg("BTF header len %u larger than data size %u\n",
+			 hdr->hdr_len, btf->raw_size);
+		return -EINVAL;
+	}
+
+	meta_left = btf->raw_size - hdr->hdr_len;
+	if (meta_left < (long long)hdr->str_off + hdr->str_len) {
+		error_msg("Invalid BTF total size: %u\n", btf->raw_size);
+		return -EINVAL;
+	}
+
+	if ((long long)hdr->type_off + hdr->type_len > hdr->str_off) {
+		error_msg("Invalid BTF data sections layout: type data at %u + %u, strings data at %u + %u\n",
+			 hdr->type_off, hdr->type_len, hdr->str_off, hdr->str_len);
+		return -EINVAL;
+	}
+
+	if (hdr->type_off % 4) {
+		error_msg("BTF type section is not aligned to 4 bytes\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int btf_parse_str_sec(struct btf *btf)
+{
+	const struct btf_header *hdr = btf->hdr;
+	const char *start = btf->strs_data;
+	const char *end = start + btf->hdr->str_len;
+
+	if (btf->base_btf && hdr->str_len == 0)
+		return 0;
+	if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_STR_OFFSET || end[-1]) {
+		error_msg("Invalid BTF string section\n");
+		return -EINVAL;
+	}
+	if (!btf->base_btf && start[0]) {
+		error_msg("Invalid BTF string section\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static bool btf_is_modifiable(const struct btf *btf)
+{
+	return (void *)btf->hdr != btf->raw_data;
+}
+
+#define MAX_ERRNO       4095
+
+#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
+
+static inline bool IS_ERR_OR_NULL(const void *ptr)
+{
+	return (!ptr) || IS_ERR_VALUE((unsigned long)ptr);
+}
+
+#define hashmap__for_each_entry_safe(map, cur, tmp, bkt)		    \
+	for (bkt = 0; bkt < map->cap; bkt++)				    \
+		for (cur = map->buckets[bkt];				    \
+		     cur && ({tmp = cur->next; true; });		    \
+		     cur = tmp)
+
+static inline void * ERR_PTR(long error_)
+{
+	return (void *) error_;
+}
+
+static void hashmap__clear(struct hashmap *map)
+{
+	struct hashmap_entry *cur, *tmp;
+	size_t bkt;
+
+	hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
+		free(cur);
+	}
+	free(map->buckets);
+	map->buckets = NULL;
+	map->cap = map->cap_bits = map->sz = 0;
+}
+
+static void hashmap__free(struct hashmap *map)
+{
+	if (IS_ERR_OR_NULL(map))
+		return;
+
+	hashmap__clear(map);
+	free(map);
+}
+
+static void strset__free(struct strset *set)
+{
+	if (IS_ERR_OR_NULL(set))
+		return;
+
+	hashmap__free(set->strs_hash);
+	free(set->strs_data);
+	free(set);
+}
+
+static void btf__free(struct btf *btf)
+{
+	if (IS_ERR_OR_NULL(btf))
+		return;
+
+	if (btf->fd >= 0)
+		close(btf->fd);
+
+	if (btf_is_modifiable(btf)) {
+		free(btf->hdr);
+		free(btf->types_data);
+		strset__free(btf->strs_set);
+	}
+	free(btf->raw_data);
+	free(btf->raw_data_swapped);
+	free(btf->type_offs);
+	free(btf);
+}
+
+static void btf_bswap_type_base(struct btf_type *t)
+{
+	t->name_off = bswap_32(t->name_off);
+	t->info = bswap_32(t->info);
+	t->type = bswap_32(t->type);
+}
+
+static inline uint16_t btf_vlen(const struct btf_type *t)
+{
+	return BTF_INFO_VLEN(t->info);
+}
+
+#ifndef BTF_KIND_FUNC
+#define BTF_KIND_FUNC		12	/* Function	*/
+#define BTF_KIND_FUNC_PROTO	13	/* Function Proto	*/
+#endif
+#ifndef BTF_KIND_VAR
+#define BTF_KIND_VAR		14	/* Variable	*/
+#define BTF_KIND_DATASEC	15	/* Section	*/
+#endif
+#ifndef BTF_KIND_FLOAT
+#define BTF_KIND_FLOAT		16	/* Floating point	*/
+#endif
+/* The kernel header switched to enums, so the following were never #defined */
+#define BTF_KIND_DECL_TAG	17	/* Decl Tag */
+#define BTF_KIND_TYPE_TAG	18	/* Type Tag */
+#define BTF_KIND_ENUM64		19	/* Enum for up-to 64bit values */
+
+struct btf_decl_tag {
+    int32_t   component_idx;
+};
+
+/* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64".
+ * The exact number of btf_enum64 is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_enum64 {
+	uint32_t	name_off;
+	uint32_t	val_lo32;
+	uint32_t	val_hi32;
+};
+static inline struct btf_decl_tag *btf_decl_tag(const struct btf_type *t)
+{
+	return (struct btf_decl_tag *)(t + 1);
+}
+
+static int btf_type_size(const struct btf_type *t)
+{
+	const int base_size = sizeof(struct btf_type);
+	uint16_t vlen = btf_vlen(t);
+
+	switch (btf_kind(t)) {
+	case BTF_KIND_FWD:
+	case BTF_KIND_CONST:
+	case BTF_KIND_VOLATILE:
+	case BTF_KIND_RESTRICT:
+	case BTF_KIND_PTR:
+	case BTF_KIND_TYPEDEF:
+	case BTF_KIND_FUNC:
+	case BTF_KIND_FLOAT:
+	case BTF_KIND_TYPE_TAG:
+		return base_size;
+	case BTF_KIND_INT:
+		return base_size + sizeof(uint32_t);
+	case BTF_KIND_ENUM:
+		return base_size + vlen * sizeof(struct btf_enum);
+	case BTF_KIND_ENUM64:
+		return base_size + vlen * sizeof(struct btf_enum64);
+	case BTF_KIND_ARRAY:
+		return base_size + sizeof(struct btf_array);
+	case BTF_KIND_STRUCT:
+	case BTF_KIND_UNION:
+		return base_size + vlen * sizeof(struct btf_member);
+	case BTF_KIND_FUNC_PROTO:
+		return base_size + vlen * sizeof(struct btf_param);
+	case BTF_KIND_VAR:
+		return base_size + sizeof(struct btf_var);
+	case BTF_KIND_DATASEC:
+		return base_size + vlen * sizeof(struct btf_var_secinfo);
+	case BTF_KIND_DECL_TAG:
+		return base_size + sizeof(struct btf_decl_tag);
+	default:
+		error_msg("Unsupported BTF_KIND:%u\n", btf_kind(t));
+		return -EINVAL;
+	}
+}
+
+static inline struct btf_enum *btf_enum(const struct btf_type *t)
+{
+	return (struct btf_enum *)(t + 1);
+}
+
+static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
+{
+	return (struct btf_enum64 *)(t + 1);
+}
+
+static inline struct btf_param *btf_params(const struct btf_type *t)
+{
+	return (struct btf_param *)(t + 1);
+}
+
+static inline struct btf_var_secinfo *
+btf_var_secinfos(const struct btf_type *t)
+{
+	return (struct btf_var_secinfo *)(t + 1);
+}
+
+static inline struct btf_array *btf_array(const struct btf_type *t)
+{
+	return (struct btf_array *)(t + 1);
+}
+
+static inline void *bpf_reallocarray(void *ptr, size_t nmemb, size_t size)
+{
+	size_t total;
+
+	if (size == 0 || nmemb > ULONG_MAX / size)
+		return NULL;
+	total = nmemb * size;
+	return realloc(ptr, total);
+}
+
+static void *bpf_add_mem(void **data, size_t *cap_cnt, size_t elem_sz,
+		     size_t cur_cnt, size_t max_cnt, size_t add_cnt)
+{
+	size_t new_cnt;
+	void *new_data;
+
+	if (cur_cnt + add_cnt <= *cap_cnt)
+		return *data + cur_cnt * elem_sz;
+
+	/* requested more than the set limit */
+	if (cur_cnt + add_cnt > max_cnt)
+		return NULL;
+
+	new_cnt = *cap_cnt;
+	new_cnt += new_cnt / 4;		  /* expand by 25% */
+	if (new_cnt < 16)		  /* but at least 16 elements */
+		new_cnt = 16;
+	if (new_cnt > max_cnt)		  /* but not exceeding a set limit */
+		new_cnt = max_cnt;
+	if (new_cnt < cur_cnt + add_cnt)  /* also ensure we have enough memory */
+		new_cnt = cur_cnt + add_cnt;
+
+	new_data = bpf_reallocarray(*data, new_cnt, elem_sz);
+	if (!new_data)
+		return NULL;
+
+	/* zero out newly allocated portion of memory */
+	memset(new_data + (*cap_cnt) * elem_sz, 0, (new_cnt - *cap_cnt) * elem_sz);
+
+	*data = new_data;
+	*cap_cnt = new_cnt;
+	return new_data + cur_cnt * elem_sz;
+}
+
+static void *btf_add_type_offs_mem(struct btf *btf, size_t add_cnt)
+{
+	return bpf_add_mem((void **)&btf->type_offs, &btf->type_offs_cap, sizeof(uint32_t),
+			      btf->nr_types, BTF_MAX_NR_TYPES, add_cnt);
+}
+
+static int btf_add_type_idx_entry(struct btf *btf, uint32_t type_off)
+{
+	uint32_t *p;
+
+	p = btf_add_type_offs_mem(btf, 1);
+	if (!p)
+		return -ENOMEM;
+
+	*p = type_off;
+	return 0;
+}
+
+static inline struct btf_member *btf_members(const struct btf_type *t)
+{
+	return (struct btf_member *)(t + 1);
+}
+
+static inline struct btf_var *btf_var(const struct btf_type *t)
+{
+	return (struct btf_var *)(t + 1);
+}
+
+static int btf_bswap_type_rest(struct btf_type *t)
+{
+	struct btf_var_secinfo *v;
+	struct btf_enum64 *e64;
+	struct btf_member *m;
+	struct btf_array *a;
+	struct btf_param *p;
+	struct btf_enum *e;
+	uint16_t vlen = btf_vlen(t);
+	int i;
+
+	switch (btf_kind(t)) {
+	case BTF_KIND_FWD:
+	case BTF_KIND_CONST:
+	case BTF_KIND_VOLATILE:
+	case BTF_KIND_RESTRICT:
+	case BTF_KIND_PTR:
+	case BTF_KIND_TYPEDEF:
+	case BTF_KIND_FUNC:
+	case BTF_KIND_FLOAT:
+	case BTF_KIND_TYPE_TAG:
+		return 0;
+	case BTF_KIND_INT:
+		*(uint32_t *)(t + 1) = bswap_32(*(uint32_t *)(t + 1));
+		return 0;
+	case BTF_KIND_ENUM:
+		for (i = 0, e = btf_enum(t); i < vlen; i++, e++) {
+			e->name_off = bswap_32(e->name_off);
+			e->val = bswap_32(e->val);
+		}
+		return 0;
+	case BTF_KIND_ENUM64:
+		for (i = 0, e64 = btf_enum64(t); i < vlen; i++, e64++) {
+			e64->name_off = bswap_32(e64->name_off);
+			e64->val_lo32 = bswap_32(e64->val_lo32);
+			e64->val_hi32 = bswap_32(e64->val_hi32);
+		}
+		return 0;
+	case BTF_KIND_ARRAY:
+		a = btf_array(t);
+		a->type = bswap_32(a->type);
+		a->index_type = bswap_32(a->index_type);
+		a->nelems = bswap_32(a->nelems);
+		return 0;
+	case BTF_KIND_STRUCT:
+	case BTF_KIND_UNION:
+		for (i = 0, m = btf_members(t); i < vlen; i++, m++) {
+			m->name_off = bswap_32(m->name_off);
+			m->type = bswap_32(m->type);
+			m->offset = bswap_32(m->offset);
+		}
+		return 0;
+	case BTF_KIND_FUNC_PROTO:
+		for (i = 0, p = btf_params(t); i < vlen; i++, p++) {
+			p->name_off = bswap_32(p->name_off);
+			p->type = bswap_32(p->type);
+		}
+		return 0;
+	case BTF_KIND_VAR:
+		btf_var(t)->linkage = bswap_32(btf_var(t)->linkage);
+		return 0;
+	case BTF_KIND_DATASEC:
+		for (i = 0, v = btf_var_secinfos(t); i < vlen; i++, v++) {
+			v->type = bswap_32(v->type);
+			v->offset = bswap_32(v->offset);
+			v->size = bswap_32(v->size);
+		}
+		return 0;
+	case BTF_KIND_DECL_TAG:
+		btf_decl_tag(t)->component_idx = bswap_32(btf_decl_tag(t)->component_idx);
+		return 0;
+	default:
+		error_msg("Unsupported BTF_KIND:%u\n", btf_kind(t));
+		return -EINVAL;
+	}
+}
+
+static int btf_parse_type_sec(struct btf *btf)
+{
+	struct btf_header *hdr = btf->hdr;
+	void *next_type = btf->types_data;
+	void *end_type = next_type + hdr->type_len;
+	int err, type_size;
+
+	while (next_type + sizeof(struct btf_type) <= end_type) {
+		if (btf->swapped_endian)
+			btf_bswap_type_base(next_type);
+
+		type_size = btf_type_size(next_type);
+		if (type_size < 0)
+			return type_size;
+		if (next_type + type_size > end_type) {
+			error_msg("BTF type [%d] is malformed\n", btf->start_id + btf->nr_types);
+			return -EINVAL;
+		}
+
+		if (btf->swapped_endian && btf_bswap_type_rest(next_type))
+			return -EINVAL;
+
+		err = btf_add_type_idx_entry(btf, next_type - btf->types_data);
+		if (err)
+			return err;
+
+		next_type += type_size;
+		btf->nr_types++;
+	}
+
+	if (next_type != end_type) {
+		error_msg("BTF types data is malformed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct btf *btf_new(const void *data, uint32_t size, struct btf *base_btf)
+{
+	struct btf *btf;
+	int err;
+
+	btf = calloc(1, sizeof(struct btf));
+	if (!btf)
+		return NULL;
+
+	btf->nr_types = 0;
+	btf->start_id = 1;
+	btf->start_str_off = 0;
+	btf->fd = -1;
+
+	if (base_btf) {
+		btf->base_btf = base_btf;
+		btf->start_id = btf__type_cnt(base_btf);
+		btf->start_str_off = base_btf->hdr->str_len;
+	}
+
+	btf->raw_data = malloc(size);
+	if (!btf->raw_data) {
+		err = -ENOMEM;
+		goto done;
+	}
+	memcpy(btf->raw_data, data, size);
+	btf->raw_size = size;
+
+	btf->hdr = btf->raw_data;
+	err = btf_parse_hdr(btf);
+	if (err)
+		goto done;
+
+	btf->strs_data = btf->raw_data + btf->hdr->hdr_len + btf->hdr->str_off;
+	btf->types_data = btf->raw_data + btf->hdr->hdr_len + btf->hdr->type_off;
+
+	err = btf_parse_str_sec(btf);
+	err = err ?: btf_parse_type_sec(btf);
+	if (err)
+		goto done;
+
+done:
+	if (err) {
+		btf__free(btf);
+		return ERR_PTR(err);
+	}
+
+	return btf;
+}
+
+struct btf* fetch_btf_from_fd(int btf_fd, struct btf *base_btf)
+{
+	struct bpf_btf_info btf_info;
+	uint32_t len = sizeof(btf_info);
+	uint32_t last_size;
+	struct btf *btf;
+	void *ptr;
+	int err;
+
+	last_size = 4096;
+	ptr = malloc(last_size);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	memset(&btf_info, 0, sizeof(btf_info));
+	btf_info.btf = ptr_to_u64(ptr);
+	btf_info.btf_size = last_size;
+	err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+
+	if (!err && btf_info.btf_size > last_size) {
+		void *temp_ptr;
+
+		last_size = btf_info.btf_size;
+		temp_ptr = realloc(ptr, last_size);
+		if (!temp_ptr) {
+			btf = ERR_PTR(-ENOMEM);
+			goto exit_free;
+		}
+		ptr = temp_ptr;
+
+		len = sizeof(btf_info);
+		memset(&btf_info, 0, sizeof(btf_info));
+		btf_info.btf = ptr_to_u64(ptr);
+		btf_info.btf_size = last_size;
+
+		err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+	}
+
+	if (err || btf_info.btf_size > last_size) {
+		btf = err ? ERR_PTR(-errno) : ERR_PTR(-E2BIG);
+		goto exit_free;
+	}
+
+	btf = btf_new(ptr, btf_info.btf_size, base_btf);
+
+exit_free:
+	free(ptr);
+	return btf;
+}
+
+int open_pidfd_and_get_fd(int pid, int fd)
+{
+	int ret = syscall(SYS_pidfd_open, pid, 0);
+	if (ret == -1) {
+		int err = -errno;
+		error_msg("%s\n", strerror(ret));
+		return err;
+	}
+
+	int pidfd = ret;
+
+	int ret1 = syscall(438, pidfd, fd, 0);
+	if (ret1 == -1) {
+		int err = -errno;
+		error_msg("%s\n", strerror(ret1));
+		return err;
+	}
+
+	close(pidfd);
+
+	return ret1;
+}
+
+void print_map_btf(struct tcb * const tcp, int map_fd)
+{
+	int tracee_map_fds = open_pidfd_and_get_fd(tcp->pid, map_fd);
+	struct bpf_map_info info = {};
+	uint32_t len = sizeof(info);
+	bpf_obj_get_info_by_fd(tracee_map_fds, &info, &len);
+	close(tracee_map_fds);
+
+	tprint_struct_begin();
+	PRINT_FIELD_STRING(info, name, strlen(info.name), 0);
+	tprint_struct_next();
+	PRINT_FIELD_U(info, type);
+	tprint_struct_next();
+	PRINT_FIELD_U(info, id);
+	tprint_struct_next();
+	PRINT_FIELD_U(info, key_size);
+	tprint_struct_next();
+	PRINT_FIELD_U(info, value_size);
+	tprint_struct_next();
+	PRINT_FIELD_U(info, max_entries);
+	tprint_struct_next();
+	PRINT_FIELD_U(info, btf_id);
+	tprint_struct_next();
+
+	uint32_t btf_fd = bpf_btf_get_fd_by_id(info.btf_id);
+	struct bpf_btf_info btf_info = {};
+	uint32_t btf_len = sizeof(btf_info);
+	bpf_obj_get_info_by_fd(btf_fd, &btf_info, &btf_len);
+	struct btf* btf = fetch_btf_from_fd(btf_fd, NULL);
+	close(btf_fd);
+
+	PRINT_FIELD_U(info, btf_key_type_id);
+	const struct btf_type* btf_key_type = get_btf_type_by_id(btf, info.btf_key_type_id);
+	if (btf_key_type)
+		tprintf(", btf_kind=%s", btf_kind_str(btf_key_type));
+	tprint_struct_next();
+	PRINT_FIELD_U(info, btf_value_type_id);
+	const struct btf_type* btf_value_type = get_btf_type_by_id(btf, info.btf_value_type_id);
+	if (btf_value_type)
+		tprintf(", btf_kind=%s", btf_kind_str(btf_value_type));
+
+	tprint_struct_end();
+}
\ No newline at end of file
diff --git a/src/bpf_btf.h b/src/bpf_btf.h
new file mode 100644
index 000000000..e49c8dbfc
--- /dev/null
+++ b/src/bpf_btf.h
@@ -0,0 +1,85 @@
+#ifndef STRACE_BPF_BTF_H
+# define STRACE_BPF_BTF_H
+
+#include "defs.h"
+
+#ifdef HAVE_LINUX_BPF_H
+# include <linux/bpf.h>
+# include <linux/btf.h>
+#endif
+
+#ifndef BTF_KIND_FLOAT
+#define BTF_KIND_FLOAT 16	/* Floating point	*/
+#endif
+
+#ifndef BTF_KIND_DECL_TAG
+#define BTF_KIND_DECL_TAG 17	/* Decl Tag */
+#endif
+
+#ifndef BTF_KIND_TYPE_TAG
+#define BTF_KIND_TYPE_TAG 18	/* Type Tag */
+#endif
+
+#ifndef BTF_KIND_ENUM64
+#define BTF_KIND_ENUM64 19	/* Enumeration up to 64-bit values */
+#endif
+
+extern int open_pidfd_and_get_fd(int pid, int fd);
+extern int bpf_obj_get_info_by_fd(int fd, void *info, uint32_t *info_len);
+extern int bpf_btf_get_fd_by_id(uint32_t id);
+extern struct btf* fetch_btf_from_fd(int btf_fd, struct btf *base_btf);
+extern const struct btf_type* get_btf_type_by_id(const struct btf* btf, uint32_t type_id);
+// extern const char *btf_str(const struct btf *btf, uint32_t off);
+extern const char* btf_kind_str(const struct btf_type* btf_t);
+extern void print_map_btf(struct tcb * const tcp, int map_fd);
+
+struct strset {
+	void* strs_data;
+	size_t strs_data_len;
+	size_t strs_data_cap;
+	size_t strs_data_max_len;
+	struct hashmap* strs_hash;
+};
+
+struct btf {
+	void* raw_data;
+	void* raw_data_swapped;
+	uint32_t raw_size;
+	bool swapped_endian;
+	struct btf_header* hdr;
+	void* types_data;
+	size_t types_data_cap;
+	uint32_t* type_offs;
+	size_t type_offs_cap;
+	uint32_t nr_types;
+	struct btf* base_btf;
+	int start_id;
+	int start_str_off;
+	void* strs_data;
+	struct strset* strs_set;
+	bool strs_deduped;
+	int fd;
+	int ptr_sz;
+};
+
+typedef size_t(*hashmap_hash_fn)(const void* key, void* ctx);
+typedef bool (*hashmap_equal_fn)(const void* key1, const void* key2, void* ctx);
+
+struct hashmap_entry {
+	const void* key;
+	void* value;
+	struct hashmap_entry* next;
+};
+
+struct hashmap {
+	hashmap_hash_fn hash_fn;
+	hashmap_equal_fn equal_fn;
+	void* ctx;
+
+	struct hashmap_entry** buckets;
+	size_t cap;
+	size_t cap_bits;
+	size_t sz;
+};
+
+#endif /* !STRACE_BPF_BTF_H */
\ No newline at end of file
-- 
2.25.1



More information about the Strace-devel mailing list