[PATCH v2][makedumpfile 09/14] Implement kernel module's kallsyms resolving

Tao Liu ltao at redhat.com
Mon Oct 20 15:24:05 PDT 2025


With kernel's kallsyms and btf ready, we can get any kernel types and
symbol addresses. So we can iterate kernel modules' linked list, and
parse & install each one of kernel module's structure to get its kallsyms
data.

Signed-off-by: Tao Liu <ltao at redhat.com>
---
 kallsyms.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 kallsyms.h |  19 ++++++++
 2 files changed, 145 insertions(+)

diff --git a/kallsyms.c b/kallsyms.c
index 116f857..f5859e9 100644
--- a/kallsyms.c
+++ b/kallsyms.c
@@ -4,6 +4,7 @@
 #include <assert.h>
 #include "makedumpfile.h"
 #include "kallsyms.h"
+#include "btf.h"
 
 static uint32_t *kallsyms_offsets = NULL;
 static uint16_t *kallsyms_token_index = NULL;
@@ -231,6 +232,131 @@ out:
 	return ret;
 }
 
+uint64_t next_list(uint64_t list)
+{
+	static int list_head_next_offset = 0;
+	static int list_head_next_size = 0;
+
+	struct member_info mi;
+	uint64_t next = 0;
+
+	if (!list_head_next_size) {
+		get_struct_member_by_name("list_head", "next", &mi);
+		list_head_next_size = mi.size;
+		list_head_next_offset = mi.bit_pos / 8;
+	}
+	readmem(VADDR, list + list_head_next_offset, &next, list_head_next_size);
+	return next;
+}
+
+int init_module_kallsyms(void)
+{
+	struct member_info mi;
+	uint64_t modules, list, value = 0, symtab = 0,
+		strtab = 0, typetab = 0;
+	uint32_t st_name = 0;
+	int num_symtab, i, j;
+	struct syment *mod_syment;
+	char symname[512], ch;
+	int ret = -1;
+
+	modules = get_kallsyms_value_by_name("modules");
+	if (!modules) {
+		/* Not a failure if no module enabled */
+		ret = 0;
+		goto out;
+	}
+
+	INIT_MEMBER_OFF_SIZE(module, list);
+	INIT_MEMBER_OFF_SIZE(module, core_kallsyms);
+	INIT_MEMBER_OFF_SIZE(mod_kallsyms, symtab);
+	INIT_MEMBER_OFF_SIZE(mod_kallsyms, num_symtab);
+	INIT_MEMBER_OFF_SIZE(mod_kallsyms, strtab);
+	INIT_MEMBER_OFF_SIZE(mod_kallsyms, typetab);
+	INIT_MEMBER_OFF_SIZE(elf64_sym, st_name);
+	INIT_MEMBER_OFF_SIZE(elf64_sym, st_value);
+
+	for (list = next_list(modules); list != modules; list = next_list(list)) {
+		readmem(VADDR, list - GET_MEMBER_OFF(module, list) +
+				GET_MEMBER_OFF(module, core_kallsyms) +
+				GET_MEMBER_OFF(mod_kallsyms, num_symtab),
+			&num_symtab, GET_MEMBER_SIZE(mod_kallsyms, num_symtab));
+		readmem(VADDR, list - GET_MEMBER_OFF(module, list) +
+				GET_MEMBER_OFF(module, core_kallsyms) +
+				GET_MEMBER_OFF(mod_kallsyms, symtab),
+			&symtab, GET_MEMBER_SIZE(mod_kallsyms, symtab));
+		readmem(VADDR, list - GET_MEMBER_OFF(module, list) +
+				GET_MEMBER_OFF(module, core_kallsyms) +
+				GET_MEMBER_OFF(mod_kallsyms, strtab),
+			&strtab, GET_MEMBER_SIZE(mod_kallsyms, strtab));
+		readmem(VADDR, list - GET_MEMBER_OFF(module, list) +
+				GET_MEMBER_OFF(module, core_kallsyms) +
+				GET_MEMBER_OFF(mod_kallsyms, typetab),
+			&typetab, GET_MEMBER_SIZE(mod_kallsyms, typetab));
+		for (i = 0; i < num_symtab; i++) {
+			j = 0;
+			readmem(VADDR, symtab + i * GET_STRUCT_SIZE(elf64_sym, st_value) +
+					GET_MEMBER_OFF(elf64_sym, st_value),
+				&value, GET_MEMBER_SIZE(elf64_sym, st_value));
+			readmem(VADDR, symtab + i * GET_STRUCT_SIZE(elf64_sym, st_name) +
+					GET_MEMBER_OFF(elf64_sym, st_name),
+				&st_name, GET_MEMBER_SIZE(elf64_sym, st_name));
+			do {
+				readmem(VADDR, strtab + st_name + j++, &ch, 1);
+			} while (ch != '\0');
+			if (j == 1)
+				/* Skip empty string */
+				continue;
+			assert(j <= sizeof(symname));
+			mod_syment = (struct syment *)calloc(1, sizeof(struct syment));
+			if (!mod_syment)
+				goto no_mem;
+			readmem(VADDR, strtab + st_name, symname, j);
+			mod_syment->name = strdup(symname);
+			if (!mod_syment->name) {
+				free(mod_syment);
+				goto no_mem;
+			}
+			mod_syment->value = value;
+			readmem(VADDR, typetab + i, &mod_syment->type, 1);
+			name_hash_install(mod_syment);
+		}
+	}
+	ret = 0;
+	goto out;
+no_mem:
+	/* Hashtable will be cleaned later */
+	fprintf(stderr, "%s: Not enough memory!\n", __func__);
+out:
+	return ret;
+}
+
+void cleanup_kallsyms(void)
+{
+	struct syment *en, *en_tmp;
+	int i;
+
+	/* Free the module's kallsyms first */
+	for (i = 0; i < NAME_HASH; i++) {
+		for (en = name_hash_table[i]; en;) {
+			en_tmp = en;
+			en = en->name_hash_next;
+			if (en_tmp <= &symtable[kallsyms_num_syms - 1] &&
+				en_tmp >= &symtable[0])
+				continue;
+			free(en_tmp->name);
+			free(en_tmp);
+		}
+	}
+
+	/* Free the kernel ones */
+	for (i = 0; i < kallsyms_num_syms; i++) {
+		if (symtable[i].name)
+			free(symtable[i].name);
+	}
+	free(symtable);
+}
+
 /* Hash table utils */
 unsigned int hash_index(const char *name, unsigned int hash_size)
 {
diff --git a/kallsyms.h b/kallsyms.h
index 35bf89e..bc8f58b 100644
--- a/kallsyms.h
+++ b/kallsyms.h
@@ -13,7 +13,26 @@ struct syment {
 int init_kernel_kallsyms(void);
 int read_vmcoreinfo_kallsyms(void);
 struct syment *search_kallsyms_by_name(char *);
+uint64_t next_list(uint64_t);
 uint64_t get_kallsyms_value_by_name(char *);
+int init_module_kallsyms(void);
+void cleanup_kallsyms(void);
+int read_vmcoreinfo_kallsyms(void);
+
+struct member_off_size {
+	int m_off;
+	int m_size;
+	int s_size;
+};
+#define QUATE(x) #x
+#define INIT_MEMBER_OFF_SIZE(S, M) \
+	struct member_off_size S##_##M; \
+	S##_##M.s_size = get_struct_member_by_name(QUATE(S), QUATE(M), &mi); \
+	S##_##M.m_off = mi.bit_pos / 8; \
+	S##_##M.m_size = mi.size;
+#define GET_MEMBER_OFF(S, M) (S##_##M.m_off)
+#define GET_MEMBER_SIZE(S, M) (S##_##M.m_size)
+#define GET_STRUCT_SIZE(S, M) (S##_##M.s_size)
 
 unsigned int hash_index(const char *, unsigned int);
 void hash_install(void **, unsigned int, void *, const char *,
-- 
2.47.0




More information about the kexec mailing list