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 <[email protected]> --- 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
