[PATCH v5][makedumpfile 3/9] Implement kernel btf resolving

Tao Liu
2026-04-14T10:27:37Z

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. Same as the previous
one, the .init_ktypes section of makedumpfile and the extensions will
be iterated, and any types which belongs to vmlinux can be resolved
at this time.

Another primary function implemented in this patch, is recursively
diving into anonymous struct/union when encountered any, to find a
member by given its name.

Suggested-by: Stephen Brennan <stephen.s.brennan@oracle.com>
Signed-off-by: Tao Liu <ltao@redhat.com>
---
Makefile | 2 +-
btf_info.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++++
btf_info.h | 75 +++++++++++++++++
3 files changed, 318 insertions(+), 1 deletion(-)
create mode 100644 btf_info.c
create mode 100644 btf_info.h

diff --git a/Makefile b/Makefile
index a57185e..690ef3e 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_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))
diff --git a/btf_info.c b/btf_info.c
new file mode 100644
index 0000000..7243674
--- /dev/null
+++ b/btf_info.c
@@ -0,0 +1,242 @@
+#ifdef EXTENSION
+#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;
+
+/* makedumpfile & extensions' .init_ktypes section range array */
+static struct section_range **sr = NULL;
+static int sr_len = 0;
+static int sr_cap = 0;
+
+/* Which mod's btf should be inited? */
+static char **mods = NULL;
+static int mods_len = 0;
+static int mods_cap = 0;
+
+static bool add_ktype_modname(char *modname)
+{
+ return push_uniq_str((void ***)&mods, &mods_len, &mods_cap, modname);
+}
+
+bool check_ktypes_require_modname(char *modname, int *total)
+{
+ if (total)
+ *total = mods_len;
+ for (int i = 0; i < mods_len; i++) {
+ if (!strcmp(modname, mods[i]))
+ return true;
+ }
+ return false;
+}
+
+static void cleanup_ktypes_modname(void)
+{
+ if (mods) {
+ free(mods);
+ mods = NULL;
+ }
+ mods_len = 0;
+ mods_cap = 0;
+}
+
+/*
+ * Used by makedumpfile and extensions, to register their .init_ktypes section,
+ * so btf_info can know which module/type should be inited.
+*/
+REGISTER_SECTION(ktype)
+
+static void cleanup_ktypes_section_range(void)
+{
+ for (int i = 0; i < sr_len; i++) {
+ free(sr[i]);
+ }
+ if (sr) {
+ free(sr);
+ sr = NULL;
+ }
+ sr_len = 0;
+ sr_cap = 0;
+}
+
+static void find_member_recursive(struct btf *btf, int struct_typeid,
+ int base_offset, struct ktype_info *ki)
+{
+ 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;
+
+ if (BTF_INFO_KIND(st->info) != BTF_KIND_STRUCT &&
+ BTF_INFO_KIND(st->info) != BTF_KIND_UNION)
+ return;
+
+ 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, ki->member_name) == 0) {
+ ki->member_bit_offset = member_bit_offset;
+ ki->member_bit_sz = btf_member_bitfield_size(st, i);
+ ki->member_size = btf__resolve_size(btf, member_typeid);
+ ki->index = i;
+ return;
+ }
+
+ if (!name || !name[0]) {
+ if (BTF_INFO_KIND(mt->info) == BTF_KIND_STRUCT ||
+ BTF_INFO_KIND(mt->info) == BTF_KIND_UNION) {
+ find_member_recursive(btf, member_typeid,
+ member_bit_offset, ki);
+ }
+ }
+ }
+}
+
+static void get_ktype_info(struct ktype_info *ki, char *mod_to_resolve)
+{
+ int i, j, start_id;
+
+ if (mod_to_resolve != NULL) {
+ if (strcmp(ki->modname, mod_to_resolve) != 0)
+ /* Exit safely */
+ return;
+ }
+
+ for (i = 0; i < btf_arr_len; i++) {
+ if (strcmp(btf_arr[i]->module, ki->modname) != 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(ki->struct_name, name) == 0) {
+ if (ki->member_name != NULL) {
+ /* Retrieve member info */
+ find_member_recursive(btf_arr[i]->btf, j, 0, ki);
+ } else {
+ ki->index = j;
+ }
+ ki->struct_size = btf__resolve_size(btf_arr[i]->btf, j);
+ return;
+ }
+ }
+ }
+}
+
+static bool add_to_btf_arr(struct btf *btf, char *module_name)
+{
+ struct btf_arr_elem *new_p;
+
+ new_p = malloc(sizeof(struct btf_arr_elem));
+ if (!new_p)
+ goto no_mem;
+
+ new_p->btf = btf;
+ new_p->module = module_name;
+
+ return add_to_arr((void ***)&btf_arr, &btf_arr_len, &btf_arr_cap, new_p);
+
+no_mem:
+ ERRMSG("Not enough memory!\n");
+ return false;
+}
+
+INIT_MOD_SYM(vmlinux, __start_BTF);
+INIT_MOD_SYM(vmlinux, __stop_BTF);
+
+#define GET_KERN_SYM(SYM) GET_MOD_SYM(vmlinux, SYM)
+#define KERN_SYM_EXIST(SYM) MOD_SYM_EXIST(vmlinux, SYM)
+
+/*
+ * Makedumpfile's .init_ktypes section
+*/
+extern struct ktype_info *__start_init_ktypes[];
+extern struct ktype_info *__stop_init_ktypes[];
+
+bool init_kernel_btf(void)
+{
+ uint64_t size;
+ struct btf *btf;
+ int i;
+ struct ktype_info **p;
+ char *buf = NULL;
+ bool ret = false;
+
+ uint64_t start_btf = GET_KERN_SYM(__start_BTF);
+ uint64_t stop_btf = GET_KERN_SYM(__stop_BTF);
+ if (!KERN_SYM_EXIST(__start_BTF) ||
+ !KERN_SYM_EXIST(__stop_BTF)) {
+ ERRMSG("symbol __start/stop_BTF not found!\n");
+ goto out;
+ }
+
+ if (!register_ktype_section((char *)__start_init_ktypes,
+ (char *)__stop_init_ktypes))
+ return ret;
+
+ size = stop_btf - start_btf;
+ buf = (char *)malloc(size);
+ if (!buf) {
+ ERRMSG("Not enough memory!\n");
+ goto out;
+ }
+ if (!readmem(VADDR, start_btf, buf, size)) {
+ ERRMSG("Can't get kernel btf data!\n");
+ goto out;
+ }
+ btf = btf__new(buf, size);
+
+ if (libbpf_get_error(btf) != 0 ||
+ add_to_btf_arr(btf, strdup("vmlinux")) == false) {
+ ERRMSG("init vmlinux btf fail\n");
+ goto out;
+ }
+
+ for (i = 0; i < sr_len; i++) {
+ for (p = (struct ktype_info **)(sr[i]->start);
+ p < (struct ktype_info **)(sr[i]->stop);
+ p++) {
+ get_ktype_info(*p, "vmlinux");
+ }
+ }
+
+ ret = true;
+out:
+ if (buf)
+ free(buf);
+ return ret;
+}
+#endif /* EXTENSION */
+
diff --git a/btf_info.h b/btf_info.h
new file mode 100644
index 0000000..8eace67
--- /dev/null
+++ b/btf_info.h
@@ -0,0 +1,75 @@
+#ifndef _BTF_INFO_H
+#define _BTF_INFO_H
+#include <stdint.h>
+#include <stdbool.h>
+
+struct ktype_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
+ bool struct_required : 1;
+ bool member_required : 1;
+ /********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
+ int index; // -1 if type not found
+};
+
+bool check_ktypes_require_modname(char *modname, int *total);
+bool register_ktype_section(char *start, char *stop);
+bool init_kernel_btf(void);
+
+#define _GEN_NAME_PTR_IMPL(PTR, NAME) PTR##NAME
+#define _GEN_NAME_PTR(PTR, NAME) _GEN_NAME_PTR_IMPL(PTR, NAME)
+#define ___GEN_NAME_2(MOD, S) _##MOD##_##S
+#define ___GEN_NAME_3(MOD, S, M) _##MOD##_##S##_##M
+#define __GEN_NAME_SELECTOR(_1, _2, _3, NAME, ...) NAME
+#define _GEN_NAME(...) __GEN_NAME_SELECTOR(__VA_ARGS__, ___GEN_NAME_3, ___GEN_NAME_2)(__VA_ARGS__)
+
+#define _INIT_MOD_STRUCT_MEMBER_RQD(MOD, S, M, R) \
+ struct ktype_info _GEN_NAME(MOD, S, M) = { \
+ #MOD, #S, #M, R, R, 0, 0, 0, 0, -1 \
+ }; \
+ __attribute__((section(".init_ktypes"), used)) \
+ struct ktype_info * _GEN_NAME_PTR(_ptr, _GEN_NAME(MOD, S, M)) = &_GEN_NAME(MOD, S, M)
+
+/*
+ * Required types will be checked automatically before extension running.
+ * Optinal types should be checked manually at extension runtime.
+ */
+#define INIT_MOD_STRUCT_MEMBER(MOD, S, M) \
+ _INIT_MOD_STRUCT_MEMBER_RQD(MOD, S, M, 1)
+#define INIT_OPT_MOD_STRUCT_MEMBER(MOD, S, M) \
+ _INIT_MOD_STRUCT_MEMBER_RQD(MOD, S, M, 0)
+
+#define DECLARE_MOD_STRUCT_MEMBER(MOD, S, M) \
+ extern struct ktype_info _GEN_NAME(MOD, S, M)
+
+#define GET_MOD_STRUCT_MEMBER_MOFF(MOD, S, M) (_GEN_NAME(MOD, S, M).member_bit_offset)
+#define GET_MOD_STRUCT_MEMBER_MSIZE(MOD, S, M) (_GEN_NAME(MOD, S, M).member_size)
+#define GET_MOD_STRUCT_MEMBER_SSIZE(MOD, S, M) (_GEN_NAME(MOD, S, M).struct_size)
+#define MOD_STRUCT_MEMBER_EXIST(MOD, S, M) (_GEN_NAME(MOD, S, M).index >= 0)
+#define TYPE_EXIST(p) ((p)->index >= 0)
+
+
+#define _INIT_MOD_STRUCT_RQD(MOD, S, R) \
+ struct ktype_info _GEN_NAME(MOD, S) = { \
+ #MOD, #S, 0, R, 0, 0, 0, 0, 0, -1 \
+ }; \
+ __attribute__((section(".init_ktypes"), used)) \
+ struct ktype_info * _GEN_NAME_PTR(_ptr, _GEN_NAME(MOD, S)) = &_GEN_NAME(MOD, S)
+
+#define INIT_MOD_STRUCT(MOD, S) _INIT_MOD_STRUCT_RQD(MOD, S, 1)
+#define INIT_OPT_MOD_STRUCT(MOD, S) _INIT_MOD_STRUCT_RQD(MOD, S, 0)
+
+#define DECLARE_MOD_STRUCT(MOD, S) extern struct ktype_info _GEN_NAME(MOD, S);
+
+#define GET_MOD_STRUCT_SSIZE(MOD, S) (_GEN_NAME(MOD, S).struct_size)
+#define MOD_STRUCT_EXIST(MOD, S) (_GEN_NAME(MOD, S).index >= 0)
+
+#endif /* _BTF_INFO_H */
+
--
2.47.0