[PATCH v3 2/8] Implement kernel btf resolving
Tao Liu
ltao at redhat.com
Mon Jan 19 18:54:54 PST 2026
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 <ltao at redhat.com>
---
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
More information about the kexec
mailing list