Tao Liu <[email protected]> writes: > This patch will parse kernel's btf data. The data can be located via > __start_BTF and __stop_BTF symbols which have been resolved by kallsyms of > the previous patch. > > The btf data is organized as follows: each one of kernel modules, including > vmlinux itself, will have a btf_file struct describing its btf data, and > holding an array of struct name_entry. By given the btf type id, we can > resolve > its name_entry fast by array index. In addition, name_entry can also be > organized in hash table. So given a type name, we can also resolve its > name_entry in a fast speed. In other words, both btf type id and btf > type name can we get it resolved fast. Once we get the name_entry structure of > the btf type, we can resolve its member/size etc easily. > > Since all name_entry array starting from index 0, which cannot identify a btf > type globally. So a uniq id is used, its value is accumulated by the location > of the btf_file within btf_file_array and total quantity of btf types within > each btf_file. > > Signed-off-by: Tao Liu <[email protected]>
Hi Tao, I haven't read this patch in detail, but I wanted to ask if you had any particular reason to avoid using libbpf for parsing the BTF? For my implementation of BTF parsing for drgn I was not aware of it, so I did the parsing entirely manually, similar to this. But for my recent attempt[1] at adding BTF support to makedumpfile (unaware of this patch), I used libbpf and found that it did reduce the amount of required code by a lot. These are specific functions I found useful: - btf__new() -> returns a "struct btf *" for use with the library - btf__type_by_id() -> returns the "struct btf_type *". Then, the linux/btf.h header provides several inline functions to work with it: - btf_members(), btf_vlen() - btf_member_bit_offset() - btf__pointer_size() - btf__name_by_offset() -> returns the name for a string offset - btf__find_by_name() & btf__find_by_name_kind() -> looks up a type by name. Note that this does a linear search, so your hash table approach is better for random lookups. - btf__resolve_size() So with libbpf, the only thing you'd really need to implement is resolving member offsets (supporting anonyomus structs, recursively, etc), and a hash table for string lookups, if you feel it's necessary. For module BTF, btf__new_split() is available as well. Thanks, Stephen [1]: https://github.com/brenns10/makedumpfile/commit/98129399a3a10ae72408bc4aaec2485f7d220626#diff-e1e6bd59ae956df7d2d42ad3580bd3c04da533f1b90827a04e2cc27bbf24b2a7 > --- > Makefile | 2 +- > btf.c | 798 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > btf.h | 175 ++++++++++++ > 3 files changed, 974 insertions(+), 1 deletion(-) > create mode 100644 btf.c > create mode 100644 btf.h > > diff --git a/Makefile b/Makefile > index 2bb17f9..fbc9f5b 100644 > --- a/Makefile > +++ b/Makefile > @@ -45,7 +45,7 @@ CFLAGS_ARCH += -m32 > endif > > SRC_BASE = makedumpfile.c makedumpfile.h diskdump_mod.h sadump_mod.h > sadump_info.h > -SRC_PART = print_info.c dwarf_info.c elf_info.c erase_info.c sadump_info.c > cache.c tools.c printk.c detect_cycle.c kallsyms.c > +SRC_PART = print_info.c dwarf_info.c elf_info.c erase_info.c sadump_info.c > cache.c tools.c printk.c detect_cycle.c kallsyms.c btf.c > OBJ_PART=$(patsubst %.c,%.o,$(SRC_PART)) > SRC_ARCH = arch/arm.c arch/arm64.c arch/x86.c arch/x86_64.c arch/ia64.c > arch/ppc64.c arch/s390x.c arch/ppc.c arch/sparc64.c arch/mips64.c > arch/loongarch64.c arch/riscv64.c > OBJ_ARCH=$(patsubst %.c,%.o,$(SRC_ARCH)) > diff --git a/btf.c b/btf.c > new file mode 100644 > index 0000000..ba376cf > --- /dev/null > +++ b/btf.c > @@ -0,0 +1,775 @@ > +#include "btf.h" > +#include <stdio.h> > +#include <assert.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/mman.h> > +#include <sys/stat.h> > +#include <fcntl.h> > +#include <libelf.h> > +#include <gelf.h> > +#include <dirent.h> > +#include "kallsyms.h" > +#include "makedumpfile.h" > + > +// btf_files_array[0] must be kernel itself > +// btf_files_array[1..] are kernel modules > +static struct btf_file **btf_files_array = NULL; > +static int btf_files_array_index = 0; > + > +static inline uint16_t btf_vlen(uint32_t info) > +{ > + return info & 0xFFFF; > +} > + > +static inline uint32_t btf_kind_flag(uint32_t info) > +{ > + return info & (1 << 31); > +} > + > +static inline uint32_t btf_member_bit_offset(uint32_t value) > +{ > + return value & 0xffffff; > +} > + > +static inline uint32_t btf_member_bitfield_size(uint32_t value) > +{ > + return value >> 24; > +} > + > +static struct btf_type *btf_next(struct btf_type *tp) > +{ > + uintptr_t next = (uintptr_t)&tp[1]; > + > + switch (btf_kind(tp->info)) { > + case BTF_KIND_INT: > + next += sizeof(uint32_t); > + break; > + > + case BTF_KIND_ARRAY: > + next += sizeof(struct btf_array); > + break; > + > + case BTF_KIND_STRUCT: > + case BTF_KIND_UNION: > + next += btf_vlen(tp->info) * sizeof(struct btf_member); > + break; > + > + case BTF_KIND_ENUM: > + next += btf_vlen(tp->info) * sizeof(struct btf_enum); > + break; > + > + case BTF_KIND_FUNC_PROTO: > + next += btf_vlen(tp->info) * sizeof(struct btf_param); > + break; > + > + case BTF_KIND_VAR: > + next += sizeof(struct btf_var); > + break; > + > + case BTF_KIND_DATASEC: > + next += btf_vlen(tp->info) * sizeof(struct btf_var_secinfo); > + break; > + > + case BTF_KIND_DECL_TAG: > + next += sizeof(struct btf_decl_tag); > + break; > + > + case BTF_KIND_ENUM64: > + next += btf_vlen(tp->info) * sizeof(struct btf_enum64); > + break; > + > + case BTF_KIND_PTR: > + case BTF_KIND_FWD: > + case BTF_KIND_TYPEDEF: > + case BTF_KIND_VOLATILE: > + case BTF_KIND_CONST: > + case BTF_KIND_RESTRICT: > + case BTF_KIND_FUNC: > + case BTF_KIND_FLOAT: > + case BTF_KIND_TYPE_TAG: > + break; // no extra data > + > + default: > + __builtin_unreachable(); > + } > + return (struct btf_type *)next; > +} > + > +#define NAME_HASH 512 > +static struct name_entry *name_hash_table[NAME_HASH] = {0}; > + > +static void *get_name_entry_next(void *entry) > +{ > + return ((struct name_entry *)entry)->name_hash_next; > +} > + > +static void set_name_entry_next(void *entry, void *next) > +{ > + ((struct name_entry *)entry)->name_hash_next = next; > +} > + > +static void name_hash_install(struct name_entry *en) > +{ > + hash_install((void **)name_hash_table, NAME_HASH, en, en->name, > + get_name_entry_next, set_name_entry_next); > +} > + > +static unsigned int name_hash_index(char *name) > +{ > + return hash_index(name, NAME_HASH); > +} > + > +int read_file_at_offset(char *f, int f_off, int r_size, void *buf) > +{ > + int got = 0; > + int r, fd, ret = -1; > + > + fd = open(f, O_RDONLY); > + if (fd < 0) { > + fprintf(stderr, "%s: Failed to open file %s\n", __func__, f); > + goto out; > + } > + > + while (got < r_size) { > + r = pread(fd, buf + got, r_size - got, f_off + got); > + if (r < 0) { > + fprintf(stderr, "%s: Failed to read file %s\n", > __func__, f); > + goto clean_fd; > + } > + got += r; > + } > + ret = got; > + > +clean_fd: > + close(fd); > +out: > + return ret; > +} > + > +static char *get_str_by_name_off(struct btf_file *bf, uint32_t name_off) > +{ > + struct btf_file *bf_vmlinux = btf_files_array[0]; > + > + if (bf != bf_vmlinux && name_off >= bf_vmlinux->str_cache_len) > + return bf->str_cache + name_off - bf_vmlinux->str_cache_len; > + else > + return bf_vmlinux->str_cache + name_off; > +} > + > +int get_btf_type_by_type_id(struct btf_file *bf_in, uint32_t id_in, > + struct btf_type *bt_out, struct name_entry **en_out) > +{ > + struct name_entry *en; > + struct btf_file *bf_vmlinux = btf_files_array[0]; > + > + if (bf_in != bf_vmlinux && id_in > bf_vmlinux->array_len) { > + en = bf_in->array[id_in - bf_vmlinux->array_len - 1]; > + } else { > + en = bf_vmlinux->array[id_in - 1]; > + } > + if (en_out) > + *en_out = en; > + return read_file_at_offset(en->bf->file_name, > + en->btf_type_offset + en->bf->types_data_offset, > + sizeof(*bt_out), bt_out); > +} > + > +/* > +* Parse the btf data and install elements into hashtable and array for quick > +* reference. > +*/ > +static int parse_btf_data(char *file_path, char *data_start, uint32_t > data_len) > +{ > + struct btf_header *hdr = (struct btf_header *)data_start; > + struct btf_file *bf = NULL; > + void *type_start; > + char *str_start; > + struct btf_type *tp; > + struct name_entry *en; > + int i, j, scale; > + > + /* We do some check first */ > + if (hdr->magic != BTF_MAGIC) { > + fprintf(stderr, "%s: Invalid BTF magic in %s\n", > + __func__, file_path); > + goto out; > + } > + if (hdr->hdr_len != sizeof(*hdr)) { > + fprintf(stderr, "%s: Invalid BTF header length in %s\n", > + __func__, file_path); > + goto out; > + } > + if (hdr->hdr_len + hdr->str_off + hdr->str_len > data_len) { > + fprintf(stderr, "%s: String section exceeds data length in > %s\n", > + __func__, file_path); > + goto out; > + } > + if (hdr->hdr_len + hdr->type_off + hdr->type_len > data_len) { > + fprintf(stderr, "%s: Type section exceeds data length in %s\n", > + __func__, file_path); > + goto out; > + } > + > + /* Let's start parsing */ > + bf = (struct btf_file *)malloc(sizeof(*bf)); > + if (!bf) > + goto no_mem; > + memset(bf, 0, sizeof(*bf)); > + > + /* Enlarge array when reach power of 2 */ > + btf_files_array[btf_files_array_index++] = bf; > + if ((btf_files_array_index & (btf_files_array_index - 1)) == 0) { > + struct btf_file **tmp = (struct btf_file > **)reallocarray(btf_files_array, > + btf_files_array_index << 1, > + sizeof(struct btf_file *)); > + if (!tmp) > + goto no_mem; > + btf_files_array = tmp; > + } > + > + type_start = data_start + hdr->hdr_len + hdr->type_off; > + str_start = data_start + hdr->hdr_len + hdr->str_off; > + > + bf->str_cache = malloc(hdr->str_len); > + if (!bf->str_cache) > + goto no_mem; > + memcpy(bf->str_cache, str_start, hdr->str_len); > + bf->str_cache_len = hdr->str_len; > + > + bf->array_len = 64; > + bf->array = (struct name_entry **)calloc(bf->array_len, > + sizeof(struct name_entry *)); > + if (!bf->array) > + goto no_mem; > + > + bf->file_name = strdup(file_path); > + if (!bf->file_name) > + goto no_mem; > + bf->types_data_offset = (char *)type_start - data_start; > + > + for (tp = (struct btf_type *)type_start, i = 0; > + (void *)tp < type_start + hdr->type_len; > + tp = btf_next(tp), i++) { > + en = (struct name_entry *)malloc(sizeof(struct name_entry)); > + if (!en) { > + bf->array_len = i; > + goto no_mem; > + } > + memset(en, 0, sizeof(struct name_entry)); > + > + en->btf_type_offset = (void *)tp - type_start; > + en->id = i + 1; > + en->bf = bf; > + if (tp->name_off) { > + en->name = get_str_by_name_off(bf, tp->name_off); > + if (en->name[0]) > + name_hash_install(en); > + } > + bf->array[i] = en; > + > + /* Now deal with sub elements which also have a name */ > + if (btf_kind(tp->info) == BTF_KIND_ENUM || > + btf_kind(tp->info) == BTF_KIND_ENUM64) { > + scale = btf_kind(tp->info) == BTF_KIND_ENUM ? > + sizeof(struct btf_enum) : sizeof(struct > btf_enum64); > + for (j = 0; j < btf_vlen(tp->info); j++) { > + en = (struct name_entry *)malloc(sizeof(struct > name_entry)); > + if (!en) { > + bf->array_len = i + 1; > + goto no_mem; > + } > + memset(en, 0, sizeof(struct name_entry)); > + en->id = 0; > + en->p_id = i + 1; > + en->p_id += btf_files_array_index == 1 ? > + 0 : btf_files_array[0]->array_len; > + en->bf = bf; > + en->name = get_str_by_name_off(bf, > + *(uint32_t *)((char *)&tp[1] + j * > scale)); > + // printf("%s\n", en->name); > + name_hash_install(en); > + } > + } > + > + /* Enlarge array when over 3/4 */ > + if (i > (bf->array_len >> 2) * 3) { > + struct name_entry **tmp = (struct name_entry > **)reallocarray(bf->array, > + bf->array_len << 1, sizeof(struct name_entry > *)); > + if (!tmp) > + goto no_mem; > + bf->array = tmp; > + bf->array_len <<= 1; > + } > + } > + bf->array_len = i; > + return 0; > +no_mem: > + /* All the memory free will be dealed later. */ > + fprintf(stderr, "%s: Not enough memory!\n", __func__); > +out: > + return -1; > +} > + > +/* > + * Used by search_name_by_cond() for searching specific type: > + * such as struct/union/typedef > + */ > +static bool is_type(struct name_entry *sp, void *data_in, void *data_out) > +{ > + struct btf_type *bt = (struct btf_type *)data_out; > + int type = *(int *)data_in; > + if (sp->id) { > + read_file_at_offset(sp->bf->file_name, > + sp->btf_type_offset + sp->bf->types_data_offset, > + sizeof(*bt), bt); > + return btf_kind(bt->info) == type; > + } > + return false; > +} > + > +static struct name_entry *search_name_by_cond(char *name, > + bool (*fn)(struct name_entry *, void *, void *), > + void *data_in, void *data_out) > +{ > + unsigned int index; > + struct name_entry *sp; > + > + index = name_hash_index(name); > + for (sp = name_hash_table[index]; sp; sp = sp->name_hash_next) { > + if (!strcmp(name, sp->name) && fn(sp, data_in, data_out)) { > + return sp; > + } > + } > + return sp; > +} > + > +// caller should prepare bt > +void resolve_typedef(struct name_entry *en_in, struct name_entry **en_out, > + struct btf_type *bt) > +{ > + uint32_t id; > + struct name_entry *en; > + > + read_file_at_offset(en_in->bf->file_name, > + en_in->btf_type_offset + en_in->bf->types_data_offset, > + sizeof(*bt), bt); > + if (btf_kind(bt->info) == BTF_KIND_TYPEDEF) { > + id = bt->type; > + get_btf_type_by_type_id(en_in->bf, id, bt, &en); > + return resolve_typedef(en, en_out, bt); > + } else { > + *en_out = en_in; > + } > +} > + > +struct cond_args { > + int index; > + char *name; > +}; > + > +static bool cond(struct cond_args *c1, struct cond_args *c2) > +{ > + if (c1->name && c2->name) > + /* Check if the member name is what we want */ > + return !strcmp(c1->name, c2->name); > + else > + /* Check if the member index is what we want */ > + return c1->index == c2->index; > +} > + > +static bool get_internal_member_info(struct name_entry *en, int > base_position, > + int *global_index, struct cond_args > *tar_arg, > + struct member_info *mi, bool initial_dive) > +{ > + struct btf_type bt, sub_bt; > + int member_num; > + char *member_array_buf = NULL; > + int i; > + struct btf_member *bm; > + struct btf_array *ba; > + int bm_position; > + struct name_entry *sub_en; > + bool res; > + struct cond_args cur_arg = {0}; > + > + read_file_at_offset(en->bf->file_name, > + en->btf_type_offset + en->bf->types_data_offset, > + sizeof(bt), &bt); // this struct > + if (initial_dive || > + ((!en->name || en->name[0] == '\0') && > + ((btf_kind(bt.info) == BTF_KIND_STRUCT) || > + (btf_kind(bt.info) == BTF_KIND_UNION)))) { > + /* anonymous struct/union, dive into */ > + member_num = btf_vlen(bt.info); > + member_array_buf = calloc(member_num, sizeof(struct > btf_member)); > + if (!member_array_buf) { > + fprintf(stderr, "%s: Not enough memory!\n", __func__); > + return false; > + } > + read_file_at_offset(en->bf->file_name, > + en->btf_type_offset + en->bf->types_data_offset + > sizeof(bt), > + member_num *sizeof(struct btf_member), > member_array_buf); > + for (i = 0, bm = (struct btf_member *)member_array_buf; > + i < member_num; i++) { > + if (btf_kind_flag(bt.info)) { > + bm_position = base_position + > + btf_member_bit_offset(bm[i].offset); > + mi->bits = > btf_member_bitfield_size(bm[i].offset); > + } else { > + bm_position = base_position + > + bm[i].offset; > + } > + mi->sname = get_str_by_name_off(en->bf, bm[i].name_off); > + get_btf_type_by_type_id(en->bf, bm[i].type, > + &sub_bt, &sub_en); > + // Dive into this member > + res = get_internal_member_info(sub_en, bm_position, > global_index, > + tar_arg, mi, false); > + if (res) { > + free(member_array_buf); > + return res; > + } > + } > + free(member_array_buf); > + return false; > + } > + > + cur_arg.index = *global_index; > + cur_arg.name = mi->sname; > + if (cond(&cur_arg, tar_arg)) { > + mi->bit_pos = base_position; > + resolve_typedef(en, &sub_en, &bt); > + mi->uniq_id = id_to_uniq_id(sub_en->id, sub_en->bf); > + if (btf_kind(bt.info) == BTF_KIND_PTR) { > + /* > + * BUG? No pointer size in btf, > + * 32bit btf target on 64bit machine > + */ > + mi->size = sizeof(void *); > + mi->type = "char *"; > + } else if (btf_kind(bt.info) == BTF_KIND_ARRAY) { > + en = sub_en; > + ba = (struct btf_array *)malloc(sizeof(struct > btf_array)); > + if (!ba) { > + fprintf(stderr, "%s: Not enough memory!\n", > __func__); > + return false; > + } > + read_file_at_offset(en->bf->file_name, > + en->btf_type_offset + en->bf->types_data_offset > + sizeof(bt), > + sizeof(struct btf_array), ba); > + mi->size = ba->nelems; // array elements > + get_btf_type_by_type_id(en->bf, ba->type, &sub_bt, > &sub_en); > + resolve_typedef(sub_en, &en, &bt); > + free(ba); > + mi->size *= bt.size; // element size > + mi->type = NULL; // "char [64]? Leave it NULL for now" > + } else { > + mi->size = bt.size; > + mi->type = get_str_by_name_off(sub_en->bf, bt.name_off); > + } > + return true; > + } else { > + (*global_index)++; > + return false; > + } > +} > + > +static bool get_member_info_by_index(struct name_entry *en, int target_index, > + struct member_info *mi) > +{ > + int g_index = 1; > + struct cond_args args = {0}; > + args.index = target_index; > + memset(mi, 0, sizeof(*mi)); > + return get_internal_member_info(en, 0, &g_index, &args, mi, true); > +} > + > +static bool get_member_info_by_member_name(struct name_entry *en, char > *member_name, > + struct member_info *mi) > +{ > + int g_index = 1; > + struct cond_args args = {0}; > + args.name = member_name; > + memset(mi, 0, sizeof(*mi)); > + return get_internal_member_info(en, 0, &g_index, &args, mi, true); > +} > + > +/* > + * Entry for query struct members > + * Return: struct size, mi_out: member details > + */ > +uint32_t get_struct_member_by_name(char *struct_name, char *member_name, > + struct member_info *mi_out) > +{ > + struct name_entry *en; > + struct btf_type bt = {0}; > + int type = BTF_KIND_STRUCT; > + > + en = search_name_by_cond(struct_name, is_type, (void *)&type, (void > *)&bt); > + if (!en) > + goto out; > + if (get_member_info_by_member_name(en, member_name, mi_out)) > + return bt.size; > +out: > + return 0; > +} > + > +/* > + * Entry for query type members, similar as above > + * Return: found-the-type, mi_out: member details > + */ > +static uint32_t uniq_id_to_id(uint32_t, struct btf_file **); > +bool get_type_member_by_index(uint64_t type_uniq_id, int member_index, > + struct member_info *mi_out) > +{ > + uint32_t id; > + struct btf_file *bf; > + > + id = uniq_id_to_id((uint32_t)type_uniq_id, &bf); > + return get_member_info_by_index(bf->array[id - 1], member_index, > mi_out); > +} > + > +uint32_t id_to_uniq_id(uint32_t id, struct btf_file *bf) > +{ > + int i; > + uint32_t uniq_id = 0; > + for (i = 0; i < btf_files_array_index; i++) { > + if (btf_files_array[i] != bf) > + uniq_id += btf_files_array[i]->array_len; > + else > + return id + uniq_id; > + } > + __builtin_unreachable(); > + assert(false); > +} > + > +static uint32_t uniq_id_to_id(uint32_t uid, struct btf_file **bf) > +{ > + int i; > + > + for (i = 0; i < btf_files_array_index; i++) { > + if (uid > btf_files_array[i]->array_len) { > + uid -= btf_files_array[i]->array_len; > + } else { > + if (bf) > + *bf = btf_files_array[i]; > + return uid; > + } > + > + } > + __builtin_unreachable(); > + assert(false); > +} > + > +// api for eppic > +uint32_t get_type_size_by_name(char *type_name, int type, uint32_t *uniq_id) > +{ > + struct name_entry *en; > + struct btf_type bt = {0}; > + > + en = search_name_by_cond(type_name, is_type, (void *)&type, (void > *)&bt); > + if (!en) > + goto out; > + if (uniq_id) > + *uniq_id = id_to_uniq_id(en->id, en->bf); > + return bt.size; > +out: > + return 0; > +} > + > +// Caller should prepare bt_out > +struct name_entry *get_en_by_uniq_id(uint32_t uniq_id, struct btf_type > *bt_out) > +{ > + uint32_t id; > + struct btf_file *bf; > + struct name_entry *en; > + struct name_entry *en_sub; > + > + id = uniq_id_to_id((uint32_t)uniq_id, &bf); > + en = bf->array[id - 1]; > + resolve_typedef(en, &en_sub, bt_out); > + return en_sub; > +} > + > +/* Deal with btf file */ > +static int btf_file_init(int fd) > +{ > + struct stat f_stat; > + char *buf = NULL; > + char proc_path[32]; > + char real_path[512]; > + int ret = -1; > + > + memset(real_path, 0, sizeof(real_path)); > + if (fstat(fd, &f_stat) < 0) { > + fprintf(stderr, "%s: fstat fail!\n", __func__); > + goto out; > + } > + buf = malloc(f_stat.st_size); > + if (!buf) { > + fprintf(stderr, "%s: Not enough memory!\n", __func__); > + goto out; > + } > + > + snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd); > + readlink(proc_path, real_path, sizeof(real_path) - 1); > + if (read_file_at_offset(real_path, 0, f_stat.st_size, buf) < 0) > + goto out; > + > + ret = parse_btf_data(real_path, buf, f_stat.st_size); > +out: > + if (buf) > + free(buf); > + return ret; > +} > + > +/* Deal with elf file which contains .BTF section */ > +static int elf_file_init(int fd) > +{ > + char proc_path[32]; > + char real_path[512]; > + Elf *elf = NULL; > + Elf_Scn *scn = NULL; > + size_t shstrndx; > + GElf_Shdr shdr; > + Elf_Data *data = NULL; > + char *str, *databuf = NULL; > + int ret = -1; > + > + snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd); > + readlink(proc_path, real_path, sizeof(real_path) - 1); > + > + if (elf_version(EV_CURRENT) == EV_NONE) > + goto elf_err; > + elf = elf_begin(fd, ELF_C_READ, NULL); > + if (!elf || elf_getshdrstrndx(elf, &shstrndx)) > + goto elf_err; > + while ((scn = elf_nextscn(elf, scn)) != NULL) { > + if (!gelf_getshdr(scn, &shdr)) > + continue; > + str = elf_strptr(elf, shstrndx, shdr.sh_name); > + if (!strcmp(str, ".BTF")) > + break; > + } > + if (!scn) { > + fprintf(stderr, "%s: No .BTF found in %s!\n", __func__, > real_path); > + goto out; > + } > + > + data = elf_rawdata(scn, data); > + databuf = malloc(data->d_size); > + if (!databuf) { > + fprintf(stderr, "%s: Not enough memory!\n", __func__); > + goto out; > + } > + memcpy(databuf, data->d_buf, data->d_size); > + elf_end(elf); > + elf = NULL; > + ret = parse_btf_data(real_path, databuf, data->d_size); > + goto out; > + > +elf_err: > + fprintf(stderr, "%s: elf error in %s!\n", __func__, real_path); > +out: > + if (databuf) > + free(databuf); > + if (elf) > + elf_end(elf); > + return ret; > +} > + > +/* > + * Entry for parse btf file and elf file. > + */ > +static int file_init(char *name) > +{ > + int fd = 0, ret = -1; > + char buf[4] = {0}; > + > + /* Will be enlarged automatically */ > + if (!btf_files_array) { > + btf_files_array = calloc(1, sizeof(struct btf_file *)); > + if (!btf_files_array) { > + fprintf(stderr, "%s: Not enough memory!\n", __func__); > + goto out; > + } > + } > + > + fd = open(name, O_RDONLY); > + if (read_file_at_offset(name, 0, sizeof(buf), buf) < 0) > + goto out; > + if (*(u_int16_t *)&buf == BTF_MAGIC) { > + ret = btf_file_init(fd); > + } else if (*(u_int32_t *)&buf == (((u_int32_t)ELFMAG0 << 24) | > + ((u_int32_t)ELFMAG1 << 16) | > + ((u_int32_t)ELFMAG2 << 8) | > + ((u_int32_t)ELFMAG3 << 0))) { > + ret = elf_file_init(fd); > + } else { > + __builtin_unreachable(); > + } > + > +out: > + if (fd) > + close(fd); > + return ret; > +} > + > +/* For pure buffer data, wrap it as a file, and handover to file_init() */ > +char temp[] = "/tmp/btf_XXXXXX"; > +static int init_btf_from_buf(char *mod_name, char *buf, int size) > +{ > + int fd; > + char name_buf[64]; > + int w, got = 0; > + static char *temp_dir = NULL; > + > + if (!temp_dir) > + temp_dir = mkdtemp(temp); > + snprintf(name_buf, sizeof(name_buf), "%s/%s", temp_dir, mod_name); > + fd = open(name_buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); > + if (fd < 0) { > + fprintf(stderr, "%s: open fail in %s!\n", __func__, name_buf); > + return -1; > + } > + > + while (got < size) { > + w = pwrite(fd, buf + got, size - got, got); > + if (w < 0) { > + fprintf(stderr, "%s: pwrite fail in %s!\n", __func__, > name_buf); > + return -1; > + } > + got += w; > + } > + close(fd); > + return file_init(name_buf); > +} > + > +int init_kernel_btf(void) > +{ > + uint64_t size; > + char *buf; > + int ret; > + > + uint64_t start_btf = get_kallsyms_value_by_name("__start_BTF"); > + uint64_t stop_btf = get_kallsyms_value_by_name("__stop_BTF"); > + if (!start_btf || !stop_btf) { > + fprintf(stderr, "%s: symbol __start/stop_BTF not found!\n", > __func__); > + return -1; > + } > + > + size = stop_btf - start_btf; > + buf = (char *)malloc(size); > + if (!buf) { > + fprintf(stderr, "%s: Not enough memory!\n", __func__); > + return -1; > + } > + readmem(VADDR, start_btf, buf, size); > + ret = init_btf_from_buf("vmlinux", buf, size); > + free(buf); > + > + return ret; > +} > + > diff --git a/btf.h b/btf.h > new file mode 100644 > index 0000000..424e9e0 > --- /dev/null > +++ b/btf.h > @@ -0,0 +1,175 @@ > +#ifndef _BTF_H > +#define _BTF_H > +#include <stdint.h> > +#include <stdbool.h> > + > +typedef uint8_t __u8; > +typedef uint16_t __u16; > +typedef uint32_t __u32; > +typedef int32_t __s32; > + > +#define BTF_MAGIC 0xeb9f > + > +enum { > + BTF_KIND_UNKN = 0, /* Unknown */ > + BTF_KIND_INT = 1, /* Integer */ > + BTF_KIND_PTR = 2, /* Pointer */ > + BTF_KIND_ARRAY = 3, /* Array */ > + BTF_KIND_STRUCT = 4, /* Struct */ > + BTF_KIND_UNION = 5, /* Union */ > + BTF_KIND_ENUM = 6, /* Enumeration */ > + BTF_KIND_FWD = 7, /* Forward */ > + BTF_KIND_TYPEDEF = 8, /* Typedef */ > + BTF_KIND_VOLATILE = 9, /* Volatile */ > + BTF_KIND_CONST = 10, /* Const */ > + BTF_KIND_RESTRICT = 11, /* Restrict */ > + BTF_KIND_FUNC = 12, /* Function */ > + BTF_KIND_FUNC_PROTO = 13, /* Function Proto */ > + BTF_KIND_VAR = 14, /* Variable */ > + BTF_KIND_DATASEC = 15, /* Section */ > + BTF_KIND_FLOAT = 16, /* Floating point */ > + BTF_KIND_DECL_TAG = 17, /* Decl Tag */ > + BTF_KIND_TYPE_TAG = 18, /* Type Tag */ > + BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */ > + > + NR_BTF_KINDS, > + BTF_KIND_MAX = NR_BTF_KINDS - 1, > +}; > + > +struct btf_type { > + __u32 name_off; > + > + /* "info" bits arrangement > + * bits 0-15: vlen (e.g. # of struct's members) > + * bits 16-23: unused > + * bits 24-27: kind (e.g. int, ptr, array...etc) > + * bits 28-30: unused > + * bit 31: kind_flag, currently used by > + * struct, union and fwd > + */ > + __u32 info; > + > + /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC. > + * "size" tells the size of the type it is describing. > + * > + * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, > + * FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG. > + * "type" is a type_id referring to another type. > + */ > + union { > + __u32 size; > + __u32 type; > + }; > +}; > + > +struct btf_header { > + __u16 magic; > + __u8 version; > + __u8 flags; > + __u32 hdr_len; > + > + /* All offsets are in bytes relative to the end of this header */ > + __u32 type_off; /* offset of type section */ > + __u32 type_len; /* length of type section */ > + __u32 str_off; /* offset of string section */ > + __u32 str_len; /* length of string section */ > +}; > + > +struct btf_array { > + __u32 type; > + __u32 index_type; > + __u32 nelems; > +}; > + > +struct btf_member { > + __u32 name_off; > + __u32 type; > + /* If the type info kind_flag is set, the btf_member offset > + * contains both member bitfield size and bit offset. The > + * bitfield size is set for bitfield members. If the type > + * info kind_flag is not set, the offset contains only bit > + * offset. > + */ > + __u32 offset; > +}; > + > +struct btf_enum { > + __u32 name_off; > + __s32 val; > +}; > + > +struct btf_enum64 { > + __u32 name_off; > + __u32 val_lo32; > + __u32 val_hi32; > +}; > + > +struct btf_param { > + __u32 name_off; > + __u32 type; > +}; > + > +struct btf_var { > + __u32 linkage; > +}; > + > +struct btf_var_secinfo { > + __u32 type; > + __u32 offset; > + __u32 size; > +}; > + > +struct btf_decl_tag { > + __s32 component_idx; > +}; > + > +/*************************************/ > +struct btf_file { > + char *file_name; > + char *str_cache; > + uint32_t str_cache_len; > + uint32_t types_data_offset; > + uint32_t array_len; > + struct name_entry **array; > +}; > + > +struct name_entry { > + union { > + uint32_t btf_type_offset; > + uint32_t p_id; > + }; > + > + uint32_t id; > + char *name; > + struct btf_file *bf; > + struct name_entry *name_hash_next; > +}; > + > +struct member_info { > + char *sname; // member name > + char *type; // member type: int, long etc > + uint32_t bit_pos; // member position in bits > + uint32_t bits; // member width in bits > + uint32_t size; // member size in bytes > + int uniq_id; // uniq_id of btf > +}; > + > +/*************************************/ > + > +int read_file_at_offset(char *, int, int, void *); > +int init_kernel_btf(void); > +uint32_t get_struct_member_by_name(char *, char *, struct member_info *); > +bool get_type_member_by_index(uint64_t, int, struct member_info *); > +uint32_t get_type_size_by_name(char *, int, uint32_t *); > +struct name_entry *get_en_by_uniq_id(uint32_t, struct btf_type *); > +void resolve_typedef(struct name_entry *, struct name_entry **, struct > btf_type *); > +int get_btf_type_by_type_id(struct btf_file *, uint32_t, > + struct btf_type *, struct name_entry **); > +uint32_t id_to_uniq_id(uint32_t, struct btf_file *); > + > +static inline uint32_t btf_kind(uint32_t info) > +{ > + return (info & 0x1F000000) >> 24; > +} > + > +#endif /* _BTF_H */ > -- > 2.47.0
