[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