This patch will parse kernel's btf data using libbpf. The kernel's btf data is located between __start_BTF and __stop_BTF symbols which are resolved by kallsyms of the previous patch. The primary function implemented in this patch, is recursively diving into anonymous struct/union when encountered any, to find a member by given its name.
Signed-off-by: Tao Liu <[email protected]> --- Makefile | 4 +- btf_info.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++ btf_info.h | 64 ++++++++++++++++++ 3 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 btf_info.c create mode 100644 btf_info.h diff --git a/Makefile b/Makefile index 6c450ac..f3f4da8 100644 --- a/Makefile +++ b/Makefile @@ -45,12 +45,12 @@ 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_info.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)) -LIBS = -ldw -lbz2 -ldl -lelf -lz +LIBS = -ldw -lbz2 -ldl -lelf -lz -lbpf ifneq ($(LINKTYPE), dynamic) LIBS := -static $(LIBS) -llzma endif diff --git a/btf_info.c b/btf_info.c new file mode 100644 index 0000000..e7f8d9a --- /dev/null +++ b/btf_info.c @@ -0,0 +1,186 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <bpf/btf.h> +#include <bpf/libbpf_legacy.h> +#include "makedumpfile.h" +#include "kallsyms.h" +#include "btf_info.h" + +struct btf_arr_elem { + struct btf *btf; + char *module; +}; + +static struct btf_arr_elem* btf_arr = NULL; +static int btf_arr_len = 0; +static int btf_arr_cap = 0; + +static bool find_member_recursive(struct btf *btf, + int struct_typeid, + int base_offset, + char *member_name, + uint32_t *out_bit_offset, + uint32_t *out_bit_sz, + uint32_t *out_member_size) +{ + const struct btf_type *st; + struct btf_member *bm; + int i, vlen; + + struct_typeid = btf__resolve_type(btf, struct_typeid); + st = btf__type_by_id(btf, struct_typeid); + + if (!st) + return false; + + if (BTF_INFO_KIND(st->info) != BTF_KIND_STRUCT && + BTF_INFO_KIND(st->info) != BTF_KIND_UNION) + return false; + + vlen = BTF_INFO_VLEN(st->info); + bm = btf_members(st); + + for (i = 0; i < vlen; i++, bm++) { + const char *name = btf__name_by_offset(btf, bm->name_off); + int member_bit_offset = btf_member_bit_offset(st, i) + base_offset; + int member_typeid = btf__resolve_type(btf, bm->type); + const struct btf_type *mt = btf__type_by_id(btf, member_typeid); + + if (name && strcmp(name, member_name) == 0) { + *out_bit_offset = member_bit_offset; + *out_bit_sz = btf_member_bitfield_size(st, i); + *out_member_size = btf__resolve_size(btf, member_typeid); + return true; + } + + if (!name || !name[0]) { + if (BTF_INFO_KIND(mt->info) == BTF_KIND_STRUCT || + BTF_INFO_KIND(mt->info) == BTF_KIND_UNION) { + if (find_member_recursive(btf, member_typeid, + member_bit_offset, + member_name, + out_bit_offset, + out_bit_sz, + out_member_size)) + return true; + } + } + } + return false; +} + +bool get_struct_member_by_name(struct struct_member_info *smi) +{ + int i, j, start_id; + char *fmt; + + for (i = 0; i < btf_arr_len; i++) { + if (smi->modname != NULL) { + if (strcmp(smi->modname, btf_arr[i].module) != 0) + continue; + } + /* + * vmlinux(btf_arr[0])'s typeid is 1~vmlinux_type_cnt, + * modules(btf_arr[1...])'s typeid is vmlinux_type_cnt~btf__type_cnt + */ + start_id = (i == 0 ? 1 : btf__type_cnt(btf_arr[0].btf)); + + for (j = start_id; j < btf__type_cnt(btf_arr[i].btf); j++) { + const struct btf_type *bt = btf__type_by_id(btf_arr[i].btf, j); + const char *name = btf__name_by_offset(btf_arr[i].btf, bt->name_off); + + if (name && strcmp(smi->struct_name, name) == 0) { + if (smi->member_name != NULL) { + /* Retrieve member info */ + if (!find_member_recursive(btf_arr[i].btf, j, + 0, + smi->member_name, + &(smi->member_bit_offset), + &(smi->member_bit_sz), + &(smi->member_size))) { + fprintf(stderr, "%s: Not find member %s in %s\n", + __func__, smi->struct_name, + smi->member_name); + return false; + } + } + smi->struct_size = btf__resolve_size(btf_arr[i].btf, j); + return true; + } + } + } + fmt = smi->modname ? + "%s: Not find struct/union %s in %s\n" : + "%s: Not find struct/union %s%s\n"; + + fprintf(stderr, fmt, __func__, smi->struct_name, + smi->modname ? smi->modname : ""); + return false; +} + +static bool add_to_btf_arr(struct btf *btf, char *module_name) +{ + struct btf_arr_elem* tmp; + int new_cap = 0; + + if (btf_arr == NULL) { + new_cap = 4; + } else if (btf_arr_len >= btf_arr_cap) { + new_cap = btf_arr_cap + (btf_arr_cap >> 1); + } + + if (!module_name) + goto no_mem; + + if (new_cap) { + tmp = reallocarray(btf_arr, new_cap, sizeof(struct btf_arr_elem)); + if (!tmp) + goto no_mem; + btf_arr = tmp; + btf_arr_cap = new_cap; + } + + btf_arr[btf_arr_len].btf = btf; + btf_arr[btf_arr_len++].module = module_name; + return true; + +no_mem: + fprintf(stderr, "%s: Not enough memory!\n", __func__); + return false; +} + +bool init_kernel_btf(void) +{ + uint64_t size; + struct btf *btf; + char *buf = NULL; + bool ret = false; + + 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__); + goto out; + } + + size = stop_btf - start_btf; + buf = (char *)malloc(size); + if (!buf) { + fprintf(stderr, "%s: Not enough memory!\n", __func__); + goto out; + } + readmem(VADDR, start_btf, buf, size); + btf = btf__new(buf, size); + + if (libbpf_get_error(btf) != 0 || + add_to_btf_arr(btf, strdup("vmlinux")) == false) { + fprintf(stderr, "%s: init vmlinux btf fail\n", __func__); + goto out; + } + ret = true; +out: + if (buf) + free(buf); + return ret; +} diff --git a/btf_info.h b/btf_info.h new file mode 100644 index 0000000..1fc6829 --- /dev/null +++ b/btf_info.h @@ -0,0 +1,64 @@ +#ifndef _BTF_INFO_H +#define _BTF_INFO_H +#include <stdint.h> + +struct struct_member_info { + /********in******/ + char *modname; // Set to search within the module, in case + // name conflict of different modules + char *struct_name; // Search by struct name + char *member_name; // Search by member name + /********out*****/ + uint32_t member_bit_offset; // member offset in bits + uint32_t member_bit_sz; // member width in bits + uint32_t member_size; // member size in bytes + uint32_t struct_size; // struct size in bytes +}; + +bool init_kernel_btf(void); +bool get_struct_member_by_name(struct struct_member_info *smi); + +struct member_off_size { + int m_off; + int m_size; + int s_size; +}; +#define QUATE(x) #x +#define INIT_MOD_STRUCT_MEMBER(MOD, S, M) \ + struct member_off_size _##MOD##_##S##_##M; \ + memset(&smi, 0, sizeof(struct struct_member_info)); \ + smi.modname = QUATE(MOD); \ + smi.struct_name = QUATE(S); \ + smi.member_name = QUATE(M); \ + get_struct_member_by_name(&smi); \ + _##MOD##_##S##_##M.s_size = smi.struct_size; \ + _##MOD##_##S##_##M.m_size = smi.member_size; \ + _##MOD##_##S##_##M.m_off = smi.member_bit_offset; +#define GET_MOD_STRUCT_MEMBER_MOFF(MOD, S, M) (_##MOD##_##S##_##M.m_off) +#define GET_MOD_STRUCT_MEMBER_MSIZE(MOD, S, M) (_##MOD##_##S##_##M.m_size) +#define GET_MOD_STRUCT_MEMBER_SSIZE(MOD, S, M) (_##MOD##_##S##_##M.s_size) + +#define INIT_STRUCT_MEMBER(S, M) \ + struct member_off_size _##S##_##M; \ + memset(&smi, 0, sizeof(struct struct_member_info)); \ + smi.modname = NULL; \ + smi.struct_name = QUATE(S); \ + smi.member_name = QUATE(M); \ + get_struct_member_by_name(&smi); \ + _##S##_##M.s_size = smi.struct_size; \ + _##S##_##M.m_size = smi.member_size; \ + _##S##_##M.m_off = smi.member_bit_offset; +#define GET_STRUCT_MEMBER_MOFF(S, M) (_##S##_##M.m_off) +#define GET_STRUCT_MEMBER_MSIZE(S, M) (_##S##_##M.m_size) +#define GET_STRUCT_MEMBER_SSIZE(S, M) (_##S##_##M.s_size) + +#define INIT_STRUCT(S) \ + struct member_off_size _##S; \ + memset(&smi, 0, sizeof(struct struct_member_info)); \ + smi.modname = NULL; \ + smi.member_name = NULL; \ + smi.struct_name = QUATE(S); \ + get_struct_member_by_name(&smi); \ + _##S.s_size = smi.struct_size; +#define GET_STRUCT_SSIZE(S) (_##S.s_size) +#endif /* _BTF_INFO_H */ -- 2.47.0
