From: Stephen Brennan <stephen.s.brennan@oracle.com>
To: Tao Liu <ltao@redhat.com>,
yamazaki-msmt@nec.com, k-hagio-ab@nec.com,
kexec@lists.infradead.org
Cc: aravinda@linux.vnet.ibm.com, Tao Liu <ltao@redhat.com>
Subject: Re: [PATCH v4][makedumpfile 2/7] Implement kernel kallsyms resolving
Date: Thu, 02 Apr 2026 16:32:17 -0700 [thread overview]
Message-ID: <87341dey66.fsf@oracle.com> (raw)
In-Reply-To: <20260317150743.69590-3-ltao@redhat.com>
Tao Liu <ltao@redhat.com> writes:
> This patch will parse kernel's kallsyms data. During the parsing
> process, the .init_ksyms sections of makedumpfile and the
> extensions will be iterated, so the kallsyms symbols which belongs
> to vmlinux can be resolved at this moment.
>
> Suggested-by: Stephen Brennan <stephen.s.brennan@oracle.com>
> Signed-off-by: Tao Liu <ltao@redhat.com>
Reviewed-by: Stephen Brennan <stephen.s.brennan@oracle.com>
> ---
> Makefile | 2 +-
> kallsyms.c | 350 +++++++++++++++++++++++++++++++++++++++++++++++++
> kallsyms.h | 91 +++++++++++++
> makedumpfile.c | 3 +
> makedumpfile.h | 11 ++
> 5 files changed, 456 insertions(+), 1 deletion(-)
> create mode 100644 kallsyms.c
> create mode 100644 kallsyms.h
>
> diff --git a/Makefile b/Makefile
> index 15a4ba0..a57185e 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..f7737cb
> --- /dev/null
> +++ b/kallsyms.c
> @@ -0,0 +1,350 @@
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <string.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;
> +
> +/* makedumpfile & extensions' .init_ksyms section range array */
> +static struct section_range **sr = NULL;
> +static int sr_len = 0;
> +static int sr_cap = 0;
> +
> +/* Which mod's kallsyms should be inited? */
> +static char **mods = NULL;
> +static int mods_len = 0;
> +static int mods_cap = 0;
> +
> +INIT_KERN_SYM(_stext);
> +
> +/*
> + * Utility: add elem to arr, which can auto extend its capacity.
> + * (*arr) is a pointer array, holding pointers of elem
> +*/
> +bool add_to_arr(void ***arr, int *arr_len, int *arr_cap, void *elem)
> +{
> + void *tmp;
> + int new_cap = 0;
> +
> + if (*arr == NULL) {
> + *arr_len = 0;
> + new_cap = 4;
> + } else if (*arr_len >= *arr_cap) {
> + new_cap = (*arr_cap) + ((*arr_cap) >> 1);
> + }
> +
> + if (new_cap) {
> + tmp = reallocarray(*arr, new_cap, sizeof(void *));
> + if (!tmp)
> + goto no_mem;
> + *arr = tmp;
> + *arr_cap = new_cap;
> + }
> +
> + (*arr)[(*arr_len)++] = elem;
> + return true;
> +
> +no_mem:
> + fprintf(stderr, "%s: Not enough memory!\n", __func__);
> + return false;
> +}
> +
> +/*
> + * Utility: add uniq string to arr, which can auto extend its capacity.
> +*/
> +bool push_uniq_str(void ***arr, int *arr_len, int *arr_cap, char *str)
> +{
> + for (int i = 0; i < (*arr_len); i++) {
> + if (!strcmp((*arr)[i], str))
> + /* String already exists, skip it */
> + return true;
> + }
> + return add_to_arr(arr, arr_len, arr_cap, str);
> +}
> +
> +static bool add_ksym_modname(char *modname)
> +{
> + return push_uniq_str((void ***)&mods, &mods_len, &mods_cap, modname);
> +}
> +
> +bool check_ksyms_require_modname(char *modname, int *total)
> +{
> + if (total)
> + *total = mods_len;
> + for (int i = 0; i < mods_len; i++) {
> + if (!strcmp(modname, mods[i]))
> + return true;
> + }
> + return false;
> +}
> +
> +static void cleanup_ksyms_modname(void)
> +{
> + if (mods) {
> + free(mods);
> + mods = NULL;
> + }
> + mods_len = 0;
> + mods_cap = 0;
> +}
> +
> +/*
> + * Used by makedumpfile and extensions, to register their .init_ksyms section.
> + * so kallsyms can know which module/sym should be inited.
> +*/
> +REGISTER_SECTION(ksym)
> +
> +static void cleanup_ksyms_section_range(void)
> +{
> + for (int i = 0; i < sr_len; i++) {
> + free(sr[i]);
> + }
> + if (sr) {
> + free(sr);
> + sr = NULL;
> + }
> + sr_len = 0;
> + sr_cap = 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 uint64_t calc_addr_absolute_percpu(struct ksym_info *p)
> +{
> + return absolute_percpu(kallsyms_relative_base, p->value);
> +}
> +
> +static uint64_t calc_addr_relative_base(struct ksym_info *p)
> +{
> + return p->value + kallsyms_relative_base;
> +}
> +
> +static uint64_t calc_addr_place_relative(struct ksym_info *p)
> +{
> + return SYMBOL(kallsyms_offsets) + p->index * sizeof(uint32_t) +
> + (int32_t)kallsyms_offsets[p->index];
> +}
> +
> +#define BUFLEN 1024
> +static bool parse_kernel_kallsyms(void)
> +{
> + char buf[BUFLEN];
> + int index = 0, i, j;
> + uint8_t *compressd_data;
> + uint8_t *uncompressd_data;
> + uint8_t len, len_old;
> + struct ksym_info **p;
> + uint64_t (*calc_addr)(struct ksym_info *);
> + struct ksym_info *stext_p;
> +
> + 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]];
> + if (strlen(buf) + strlen((char *)uncompressd_data) >= BUFLEN) {
> + goto next_symbol;
> + }
> + strcat(buf, (char *)uncompressd_data);
> + compressd_data++;
> + }
> +
> + /* Now check if the symbol is we wanted */
> + for (j = 0; j < sr_len; j++) {
> + for (p = (struct ksym_info **)(sr[j]->start);
> + p < (struct ksym_info **)(sr[j]->stop);
> + p++) {
> + if (!strcmp((*p)->modname, "vmlinux") &&
> + !strcmp((*p)->symname, &buf[1])) {
> + (*p)->value = kallsyms_offsets[i];
> + (*p)->index = i;
> + }
> + }
> + }
> +next_symbol:
> + }
> +
> + /* Check the approach for calc absolute kallsyms address
> + *
> + * A complete comment of each approaches please refer to:
> + * https://github.com/osandov/drgn/commit/744f36ec3c3f64d7e1323a0037898158698585c4
> + */
> + if (!KERN_SYM_EXIST(_stext)) {
> + fprintf(stderr, "%s: symbol _stext not found!\n", __func__);
> + goto out;
> + }
> +
> + stext_p = GET_KERN_SYM_PTR(_stext);
> +
> + if (SYMBOL(_stext) == calc_addr_absolute_percpu(stext_p)) {
> + calc_addr = calc_addr_absolute_percpu;
> + } else if (SYMBOL(_stext) == calc_addr_relative_base(stext_p)) {
> + calc_addr = calc_addr_relative_base;
> + } else if (SYMBOL(_stext) == calc_addr_place_relative(stext_p)) {
> + calc_addr = calc_addr_place_relative;
> + } else {
> + fprintf(stderr, "%s: Wrong calculate kallsyms symbol value!\n", __func__);
> + goto out;
> + }
> +
> + /* Now do the calc */
> + for (j = 0; j < sr_len; j++) {
> + for (p = (struct ksym_info **)(sr[j]->start);
> + p < (struct ksym_info **)(sr[j]->stop);
> + p++) {
> + if (!strcmp((*p)->modname, "vmlinux") &&
> + SYM_EXIST(*p)) {
> + (*p)->value = calc_addr(*p);
> + }
> + }
> + }
> +
> + return true;
> +out:
> + return false;
> +}
> +
> +static bool vmcore_info_ready = false;
> +
> +bool 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;
> +}
> +
> +/*
> + * Makedumpfile's .init_ksyms section
> +*/
> +extern struct ksym_info *__start_init_ksyms[];
> +extern struct ksym_info *__stop_init_ksyms[];
> +
> +bool 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;
> + bool ret = false;
> +
> + if (vmcore_info_ready == false) {
> + fprintf(stderr, "%s: vmcoreinfo not ready for kallsyms!\n",
> + __func__);
> + return ret;
> + }
> +
> + if (!register_ksym_section((char *)__start_init_ksyms,
> + (char *)__stop_init_ksyms))
> + return ret;
> +
> + readmem(VADDR, SYMBOL(kallsyms_num_syms), &kallsyms_num_syms,
> + sizeof(kallsyms_num_syms));
> + if (SYMBOL(kallsyms_relative_base) != NOT_FOUND_SYMBOL)
> + 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);
> + /*
> + * The 2-byte representation was added in commit 73bbb94466fd3
> + * ("kallsyms: support "big" kernel symbols") in v6.1, thus for
> + * v6.1+, they indicate a long symbol, but for kernel versions
> + * prior to v6.1, they might be ambiguous.
> + */
> + 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);
> + kallsyms_offsets = NULL;
> + }
> + if (kallsyms_token_index) {
> + free(kallsyms_token_index);
> + kallsyms_token_index = NULL;
> + }
> + if (kallsyms_token_table) {
> + free(kallsyms_token_table);
> + kallsyms_token_table = NULL;
> + }
> + if (kallsyms_names) {
> + free(kallsyms_names);
> + kallsyms_names = NULL;
> + }
> + return ret;
> +}
> \ No newline at end of file
> diff --git a/kallsyms.h b/kallsyms.h
> new file mode 100644
> index 0000000..3791284
> --- /dev/null
> +++ b/kallsyms.h
> @@ -0,0 +1,91 @@
> +#ifndef _KALLSYMS_H
> +#define _KALLSYMS_H
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +
> +struct ksym_info {
> + /********in******/
> + char *modname;
> + char *symname;
> + bool sym_required;
> + /********out*****/
> + uint64_t value;
> + int index; // -1 if sym not found
> +};
> +
> +#define QUATE(x) #x
> +#define INIT_MOD_SYM_RQD(MOD, SYM, R) \
> + struct ksym_info _##MOD##_##SYM = { \
> + QUATE(MOD), QUATE(SYM), R, 0, -1 \
> + }; \
> + __attribute__((section(".init_ksyms"), used)) \
> + struct ksym_info * _ptr_##MOD##_##SYM = &_##MOD##_##SYM
> +
> +#define GET_MOD_SYM(MOD, SYM) (_##MOD##_##SYM.value)
> +#define GET_MOD_SYM_PTR(MOD, SYM) (&_##MOD##_##SYM)
> +#define MOD_SYM_EXIST(MOD, SYM) (_##MOD##_##SYM.index >= 0)
> +#define SYM_EXIST(p) ((p)->index >= 0)
> +
> +#define GET_KERN_SYM(SYM) GET_MOD_SYM(vmlinux, SYM)
> +#define GET_KERN_SYM_PTR(SYM) GET_MOD_SYM_PTR(vmlinux, SYM)
> +#define KERN_SYM_EXIST(SYM) MOD_SYM_EXIST(vmlinux, SYM)
> +
> +/*
> + * Required syms will be checked automatically before extension running.
> + * Optinal syms should be checked manually at extension runtime.
> + */
> +#define INIT_MOD_SYM(MOD, SYM) INIT_MOD_SYM_RQD(MOD, SYM, 1)
> +#define INIT_OPT_MOD_SYM(MOD, SYM) INIT_MOD_SYM_RQD(MOD, SYM, 0)
> +
> +#define INIT_KERN_SYM(SYM) INIT_MOD_SYM(vmlinux, SYM)
> +#define INIT_OPT_KERN_SYM(SYM) INIT_OPT_MOD_SYM(vmlinux, SYM)
> +
> +struct section_range {
> + char *start;
> + char *stop;
> +};
> +
> +#define REGISTER_SECTION(T) \
> +bool register_##T##_section(char *start, char *stop) \
> +{ \
> + struct section_range *new_sr; \
> + struct T##_info **p; \
> + bool ret = false; \
> + \
> + if (!start || !stop) { \
> + fprintf(stderr, "%s: Invalid section start/stop\n", \
> + __func__); \
> + goto out; \
> + } \
> + \
> + for (p = (struct T##_info **)start; \
> + p < (struct T##_info **)stop; \
> + p++) { \
> + if (!add_##T##_modname((*p)->modname)) \
> + goto out; \
> + } \
> + \
> + new_sr = malloc(sizeof(struct section_range)); \
> + if (!new_sr) { \
> + fprintf(stderr, "%s: Not enough memory!\n", __func__); \
> + goto out; \
> + } \
> + new_sr->start = start; \
> + new_sr->stop = stop; \
> + if (!add_to_arr((void ***)&sr, &sr_len, &sr_cap, new_sr)) { \
> + free(new_sr); \
> + goto out; \
> + } \
> + ret = true; \
> +out: \
> + return ret; \
> +}
> +
> +bool add_to_arr(void ***arr, int *arr_len, int *arr_cap, void *elem);
> +bool push_uniq_str(void ***arr, int *arr_len, int *arr_cap, char *str);
> +bool check_ksyms_require_modname(char *modname, int *total);
> +bool register_ksym_section(char *start, char *stop);
> +bool read_vmcoreinfo_kallsyms(void);
> +bool init_kernel_kallsyms(void);
> +#endif /* _KALLSYMS_H */
> \ No newline at end of file
> diff --git a/makedumpfile.c b/makedumpfile.c
> index 12fb0d8..dba3628 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;
> @@ -3105,6 +3106,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 134eb7a..0f13743 100644
> --- a/makedumpfile.h
> +++ b/makedumpfile.h
> @@ -259,6 +259,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))
>
>
> /*
> @@ -1919,6 +1920,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
next prev parent reply other threads:[~2026-04-02 23:32 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-17 15:07 [PATCH v4][makedumpfile 0/7] btf/kallsyms based makedumpfile extension for mm page filtering Tao Liu
2026-03-17 15:07 ` [PATCH v4][makedumpfile 1/7] Reserve sections for makedumpfile and extenions Tao Liu
2026-04-02 23:31 ` Stephen Brennan
2026-04-14 11:03 ` Tao Liu
2026-04-03 8:10 ` HAGIO KAZUHITO(萩尾 一仁)
2026-03-17 15:07 ` [PATCH v4][makedumpfile 2/7] Implement kernel kallsyms resolving Tao Liu
2026-04-02 23:32 ` Stephen Brennan [this message]
2026-04-03 8:12 ` HAGIO KAZUHITO(萩尾 一仁)
2026-04-14 11:10 ` Tao Liu
2026-03-17 15:07 ` [PATCH v4][makedumpfile 3/7] Implement kernel btf resolving Tao Liu
2026-04-02 23:41 ` Stephen Brennan
2026-04-03 8:13 ` HAGIO KAZUHITO(萩尾 一仁)
2026-04-14 11:17 ` Tao Liu
2026-03-17 15:07 ` [PATCH v4][makedumpfile 4/7] Implement kernel module's kallsyms resolving Tao Liu
2026-04-02 23:54 ` Stephen Brennan
2026-03-17 15:07 ` [PATCH v4][makedumpfile 5/7] Implement kernel module's btf resolving Tao Liu
2026-04-02 23:56 ` Stephen Brennan
2026-03-17 15:07 ` [PATCH v4][makedumpfile 6/7] Add makedumpfile extensions support Tao Liu
2026-04-03 0:11 ` Stephen Brennan
2026-04-14 11:27 ` Tao Liu
2026-04-03 8:14 ` HAGIO KAZUHITO(萩尾 一仁)
2026-04-14 11:50 ` Tao Liu
2026-03-17 15:07 ` [PATCH v4][makedumpfile 7/7] Filter amdgpu mm pages Tao Liu
2026-04-03 0:16 ` Stephen Brennan
2026-04-14 12:06 ` Tao Liu
2026-04-03 8:06 ` [PATCH v4][makedumpfile 0/7] btf/kallsyms based makedumpfile extension for mm page filtering HAGIO KAZUHITO(萩尾 一仁)
2026-04-14 10:37 ` Tao Liu
2026-04-03 18:26 ` Stephen Brennan
2026-04-14 10:47 ` Tao Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87341dey66.fsf@oracle.com \
--to=stephen.s.brennan@oracle.com \
--cc=aravinda@linux.vnet.ibm.com \
--cc=k-hagio-ab@nec.com \
--cc=kexec@lists.infradead.org \
--cc=ltao@redhat.com \
--cc=yamazaki-msmt@nec.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.