This patch will parse kernel's kallsyms data, and store them into a hash table so they can be referenced later in a fast speed.
Signed-off-by: Tao Liu <[email protected]> --- Makefile | 2 +- kallsyms.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++ kallsyms.h | 22 ++++ makedumpfile.c | 3 + makedumpfile.h | 11 ++ 5 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 kallsyms.c create mode 100644 kallsyms.h diff --git a/Makefile b/Makefile index 18d3a17..2bb17f9 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 +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 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/kallsyms.c b/kallsyms.c new file mode 100644 index 0000000..116f857 --- /dev/null +++ b/kallsyms.c @@ -0,0 +1,266 @@ +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <assert.h> +#include "makedumpfile.h" +#include "kallsyms.h" + +static uint32_t *kallsyms_offsets = NULL; +static uint16_t *kallsyms_token_index = NULL; +static uint8_t *kallsyms_token_table = NULL; +static uint8_t *kallsyms_names = NULL; +static unsigned long kallsyms_relative_base = 0; +static unsigned int kallsyms_num_syms = 0; + +#define NAME_HASH 512 +static struct syment *symtable = NULL; +static struct syment *name_hash_table[NAME_HASH] = {0}; + +static uint64_t absolute_percpu(uint64_t base, int32_t val) +{ + if (val >= 0) + return (uint64_t)val; + else + return base - 1 - val; +} + +static void *get_syment_next(void *entry) +{ + return ((struct syment *)entry)->name_hash_next; +} + +static void set_syment_next(void *entry, void *next) +{ + ((struct syment *)entry)->name_hash_next = next; +} + +static unsigned int name_hash_index(char *name) +{ + return hash_index(name, NAME_HASH); +} + +static void name_hash_install(struct syment *en) +{ + hash_install((void **)name_hash_table, NAME_HASH, en, en->name, + get_syment_next, set_syment_next); +} + +struct syment *search_kallsyms_by_name(char *name) +{ + unsigned int index; + struct syment *sp; + + index = name_hash_index(name); + for (sp = name_hash_table[index]; sp; sp = sp->name_hash_next) { + if (!strcmp(name, sp->name)) { + return sp; + } + } + return sp; +} + +uint64_t get_kallsyms_value_by_name(char *name) +{ + struct syment *sp; + + sp = search_kallsyms_by_name(name); + if (!sp) + return 0; + return sp->value; +} + +#define BUFLEN 1024 +int parse_kernel_kallsyms(void) +{ + char buf[BUFLEN]; + int index = 0, i; + uint8_t *compressd_data; + uint8_t *uncompressd_data; + uint64_t stext; + uint8_t len, len_old; + + symtable = calloc(kallsyms_num_syms, sizeof(struct syment)); + if (!symtable) + goto no_mem; + + for (i = 0; i < kallsyms_num_syms; i++) { + memset(buf, 0, BUFLEN); + len = kallsyms_names[index]; + if (len & 0x80) { + index++; + len_old = len; + len = kallsyms_names[index]; + if (len & 0x80) { + fprintf(stderr, "%s: BUG! Unexpected 3-byte length," + " should be detected in init_kernel_kallsyms()\n", + __func__); + goto out; + } + len = (len_old & 0x7F) | (len << 7); + } + index++; + + compressd_data = &kallsyms_names[index]; + index += len; + while (len--) { + uncompressd_data = &kallsyms_token_table[kallsyms_token_index[*compressd_data]]; + assert(strlen(buf) + strlen((char *)uncompressd_data) < BUFLEN); + strcat(buf, (char *)uncompressd_data); + compressd_data++; + } + symtable[i].value = kallsyms_offsets[i]; + symtable[i].type = buf[0]; + symtable[i].name = strdup(&buf[1]); + if (!symtable[i].name) + goto no_mem; + name_hash_install(&symtable[i]); + } + + /* Now refresh the absolute each kallsyms address */ + stext = get_kallsyms_value_by_name("_stext"); + if (SYMBOL(_stext) == absolute_percpu(kallsyms_relative_base, stext)) { + for (i = 0; i < kallsyms_num_syms; i++) { + symtable[i].value = absolute_percpu(kallsyms_relative_base, + symtable[i].value); + } + } else if (SYMBOL(_stext) == kallsyms_relative_base + stext) { + for (i = 0; i < kallsyms_num_syms; i++) { + symtable[i].value += kallsyms_relative_base; + } + } else { + fprintf(stderr, "%s: Wrong calculate kallsyms symbol value!\n", __func__); + goto out; + } + + return 0; +no_mem: + fprintf(stderr, "%s: Not enough memory!\n", __func__); +out: + return -1; +} + +static bool vmcore_info_ready = false; + +int read_vmcoreinfo_kallsyms(void) +{ + READ_SYMBOL("kallsyms_names", kallsyms_names); + READ_SYMBOL("kallsyms_num_syms", kallsyms_num_syms); + READ_SYMBOL("kallsyms_token_table", kallsyms_token_table); + READ_SYMBOL("kallsyms_token_index", kallsyms_token_index); + READ_SYMBOL("kallsyms_offsets", kallsyms_offsets); + READ_SYMBOL("kallsyms_relative_base", kallsyms_relative_base); + vmcore_info_ready = true; + return true; +} + +int init_kernel_kallsyms(void) +{ + const int token_index_size = (UINT8_MAX + 1) * sizeof(uint16_t); + uint64_t last_token, len; + unsigned char data, data_old; + int i; + int ret = -1; + + if (vmcore_info_ready == false) { + fprintf(stderr, "%s: vmcoreinfo not ready for kallsyms!\n", + __func__); + return ret; + } + + readmem(VADDR, SYMBOL(kallsyms_num_syms), &kallsyms_num_syms, + sizeof(kallsyms_num_syms)); + readmem(VADDR, SYMBOL(kallsyms_relative_base), &kallsyms_relative_base, + sizeof(kallsyms_relative_base)); + + kallsyms_offsets = malloc(sizeof(uint32_t) * kallsyms_num_syms); + if (!kallsyms_offsets) + goto no_mem; + readmem(VADDR, SYMBOL(kallsyms_offsets), kallsyms_offsets, + kallsyms_num_syms * sizeof(uint32_t)); + + kallsyms_token_index = malloc(token_index_size); + if (!kallsyms_token_index) + goto no_mem; + readmem(VADDR, SYMBOL(kallsyms_token_index), kallsyms_token_index, + token_index_size); + + last_token = SYMBOL(kallsyms_token_table) + kallsyms_token_index[UINT8_MAX]; + do { + readmem(VADDR, last_token++, &data, 1); + } while(data); + len = last_token - SYMBOL(kallsyms_token_table); + kallsyms_token_table = malloc(len); + if (!kallsyms_token_table) + goto no_mem; + readmem(VADDR, SYMBOL(kallsyms_token_table), kallsyms_token_table, len); + + for (len = 0, i = 0; i < kallsyms_num_syms; i++) { + readmem(VADDR, SYMBOL(kallsyms_names) + len, &data, 1); + if (data & 0x80) { + len += 1; + data_old = data; + readmem(VADDR, SYMBOL(kallsyms_names) + len, &data, 1); + if (data & 0x80) { + fprintf(stderr, "%s: BUG! Unexpected 3-byte length" + " encoding in kallsyms names\n", __func__); + goto out; + } + data = (data_old & 0x7F) | (data << 7); + } + len += data + 1; + } + kallsyms_names = malloc(len); + if (!kallsyms_names) + goto no_mem; + readmem(VADDR, SYMBOL(kallsyms_names), kallsyms_names, len); + + ret = parse_kernel_kallsyms(); + goto out; + +no_mem: + fprintf(stderr, "%s: Not enough memory!\n", __func__); +out: + if (kallsyms_offsets) + free(kallsyms_offsets); + if (kallsyms_token_index) + free(kallsyms_token_index); + if (kallsyms_token_table) + free(kallsyms_token_table); + if (kallsyms_names) + free(kallsyms_names); + return ret; +} + +/* Hash table utils */ +unsigned int hash_index(const char *name, unsigned int hash_size) +{ + unsigned int len, value; + + len = strlen(name); + value = name[len - 1] * name[len / 2]; + + return (name[0] ^ value) % hash_size; +} + +void hash_install(void **hash_table, unsigned int hash_size, + void *entry, const char *name, + void *(*get_next)(void *), + void (*set_next)(void *, void *)) +{ + unsigned int index = hash_index(name, hash_size); + void *sp = hash_table[index]; + + assert(index < hash_size); + if (sp == NULL) { + hash_table[index] = entry; + } else { + while (sp) { + if (get_next(sp)) { + sp = get_next(sp); + } else { + set_next(sp, entry); + break; + } + } + } +} \ No newline at end of file diff --git a/kallsyms.h b/kallsyms.h new file mode 100644 index 0000000..35bf89e --- /dev/null +++ b/kallsyms.h @@ -0,0 +1,22 @@ +#ifndef _KALLSYMS_H +#define _KALLSYMS_H + +#include <stdint.h> + +struct syment { + uint64_t value; + char *name; + struct syment *name_hash_next; + char type; +}; + +int init_kernel_kallsyms(void); +int read_vmcoreinfo_kallsyms(void); +struct syment *search_kallsyms_by_name(char *); +uint64_t get_kallsyms_value_by_name(char *); + +unsigned int hash_index(const char *, unsigned int); +void hash_install(void **, unsigned int, void *, const char *, + void *(*)(void *), void (*)(void *, void *)); + +#endif /* _KALLSYMS_H */ \ No newline at end of file diff --git a/makedumpfile.c b/makedumpfile.c index 33fad32..cdfcfeb 100644 --- a/makedumpfile.c +++ b/makedumpfile.c @@ -27,6 +27,7 @@ #include <limits.h> #include <assert.h> #include <zlib.h> +#include "kallsyms.h" struct symbol_table symbol_table; struct size_table size_table; @@ -3103,6 +3104,8 @@ read_vmcoreinfo_from_vmcore(off_t offset, unsigned long size, int flag_xen_hv) if (!read_vmcoreinfo()) goto out; } + read_vmcoreinfo_kallsyms(); + close_vmcoreinfo(); ret = TRUE; diff --git a/makedumpfile.h b/makedumpfile.h index 944397a..cc474ad 100644 --- a/makedumpfile.h +++ b/makedumpfile.h @@ -257,6 +257,7 @@ static inline int string_exists(char *s) { return (s ? TRUE : FALSE); } #define UINT(ADDR) *((unsigned int *)(ADDR)) #define ULONG(ADDR) *((unsigned long *)(ADDR)) #define ULONGLONG(ADDR) *((unsigned long long *)(ADDR)) +#define VOID_PTR(ADDR) *((void **)(ADDR)) /* @@ -1917,6 +1918,16 @@ struct symbol_table { * symbols on sparc64 arch */ unsigned long long vmemmap_table; + + /* + * kallsyms related + */ + unsigned long long kallsyms_names; + unsigned long long kallsyms_num_syms; + unsigned long long kallsyms_token_table; + unsigned long long kallsyms_token_index; + unsigned long long kallsyms_offsets; + unsigned long long kallsyms_relative_base; }; struct size_table { -- 2.47.0
