[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