btf data contains symbols' type and size, which is essential to determine a
structure/member size & offset. kallsyms contains symbols' address. All the
data can provide complete info for symbol resolving.

To initialize, we will:
1st) read vmcore info and parse kernel's kallsyms data;
2nd) read kernel data range of "__start/stop_BTF" and parse kernel's btf data,
     now we have complete type/address info for kernel symbols;
3rd) iterate kernel's module and parse all kernel modules' kallsyms data;
4th) iterate btf_modules and parse all kernel modules' btf data, now we have
     complete type/address info for kernel and modules symbols.

Also to speed up the converting of symbol name to address/type, the parsed
btf/kallsyms data are stored in hash tables.

Signed-off-by: Tao Liu <l...@redhat.com>
---
 Makefile       |   2 +-
 btf.c          | 919 +++++++++++++++++++++++++++++++++++++++++++++++++
 btf.h          | 176 ++++++++++
 kallsyms.c     | 371 ++++++++++++++++++++
 kallsyms.h     |  42 +++
 makedumpfile.c |   3 +
 makedumpfile.h |  11 +
 7 files changed, 1523 insertions(+), 1 deletion(-)
 create mode 100644 btf.c
 create mode 100644 btf.h
 create mode 100644 kallsyms.c
 create mode 100644 kallsyms.h

diff --git a/Makefile b/Makefile
index 18d3a17..fbc9f5b 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 btf.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/btf.c b/btf.c
new file mode 100644
index 0000000..c91c841
--- /dev/null
+++ b/btf.c
@@ -0,0 +1,919 @@
+#include "btf.h"
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <dirent.h>
+#include "kallsyms.h"
+#include "makedumpfile.h"
+
+// btf_files_array[0] must be kernel itself
+// btf_files_array[1..] are kernel modules
+static struct btf_file **btf_files_array = NULL;
+static int btf_files_array_index = 0;
+
+static const char *kind_names[NR_BTF_KINDS] = {
+       [BTF_KIND_UNKN] = "UNKN",
+       [BTF_KIND_INT] = "INT",
+       [BTF_KIND_PTR] = "PTR",
+       [BTF_KIND_ARRAY] = "ARRAY",
+       [BTF_KIND_STRUCT] = "STRUCT",
+       [BTF_KIND_UNION] = "UNION",
+       [BTF_KIND_ENUM] = "ENUM",
+       [BTF_KIND_FWD] = "FWD",
+       [BTF_KIND_TYPEDEF] = "TYPEDEF",
+       [BTF_KIND_VOLATILE] = "VOLATILE",
+       [BTF_KIND_CONST] = "CONST",
+       [BTF_KIND_RESTRICT] = "RESTRICT",
+       [BTF_KIND_FUNC] = "FUNC",
+       [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO",
+       [BTF_KIND_VAR] = "VAR",
+       [BTF_KIND_DATASEC] = "DATASEC",
+       [BTF_KIND_FLOAT] = "FLOAT",
+       [BTF_KIND_DECL_TAG] = "DECL_TAG",
+       [BTF_KIND_TYPE_TAG] = "TYPE_TAG",
+       [BTF_KIND_ENUM64] = "ENUM64",
+};
+
+static inline uint16_t btf_vlen(uint32_t info)
+{
+       return info & 0xFFFF;
+}
+
+static inline uint32_t btf_kind_flag(uint32_t info)
+{
+       return info & (1 << 31);
+}
+
+static inline uint32_t btf_member_bit_offset(uint32_t value)
+{
+       return value & 0xffffff;
+}
+
+static inline uint32_t btf_member_bitfield_size(uint32_t value)
+{
+       return value >> 24;
+}
+
+static struct btf_type *btf_next(struct btf_type *tp)
+{
+       uintptr_t next = (uintptr_t)&tp[1];
+
+       switch (btf_kind(tp->info)) {
+       case BTF_KIND_INT:
+               next += sizeof(uint32_t);
+               break;
+
+       case BTF_KIND_ARRAY:
+               next += sizeof(struct btf_array);
+               break;
+
+       case BTF_KIND_STRUCT:
+       case BTF_KIND_UNION:
+               next += btf_vlen(tp->info) * sizeof(struct btf_member);
+               break;
+
+       case BTF_KIND_ENUM:
+               next += btf_vlen(tp->info) * sizeof(struct btf_enum);
+               break;
+
+       case BTF_KIND_FUNC_PROTO:
+               next += btf_vlen(tp->info) * sizeof(struct btf_param);
+               break;
+
+       case BTF_KIND_VAR:
+               next += sizeof(struct btf_var);
+               break;
+
+       case BTF_KIND_DATASEC:
+               next += btf_vlen(tp->info) * sizeof(struct btf_var_secinfo);
+               break;
+
+       case BTF_KIND_DECL_TAG:
+               next += sizeof(struct btf_decl_tag);
+               break;
+
+       case BTF_KIND_ENUM64:
+               next += btf_vlen(tp->info) * sizeof(struct btf_enum64);
+               break;
+
+       case BTF_KIND_PTR:
+       case BTF_KIND_FWD:
+       case BTF_KIND_TYPEDEF:
+       case BTF_KIND_VOLATILE:
+       case BTF_KIND_CONST:
+       case BTF_KIND_RESTRICT:
+       case BTF_KIND_FUNC:
+       case BTF_KIND_FLOAT:
+       case BTF_KIND_TYPE_TAG:
+               break; // no extra data
+
+       default:
+               __builtin_unreachable();
+       }
+       return (struct btf_type *)next;
+}
+
+#define NAME_HASH 512
+static struct name_entry *name_hash_table[NAME_HASH] = {0};
+
+static void *get_name_entry_next(void *entry)
+{
+       return ((struct name_entry *)entry)->name_hash_next;
+}
+
+static void set_name_entry_next(void *entry, void *next)
+{
+       ((struct name_entry *)entry)->name_hash_next = next;
+}
+
+static void name_hash_install(struct name_entry *en)
+{
+       hash_install((void **)name_hash_table, NAME_HASH, en, en->name,
+                       get_name_entry_next, set_name_entry_next);
+}
+
+static unsigned int name_hash_index(char *name)
+{
+       return hash_index(name, NAME_HASH);
+}
+
+static int read_file_at_offset(char *f, int f_off, int r_size, void *buf)
+{
+       int got = 0;
+       int r, fd, ret = -1;
+
+       fd = open(f, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "%s: Failed to open file %s\n", __func__, f);
+               goto out;
+       }
+
+       while (got < r_size) {
+               r = pread(fd, buf + got, r_size - got, f_off + got);
+               if (r < 0) {
+                       fprintf(stderr, "%s: Failed to read file %s\n", 
__func__, f);
+                       goto clean_fd;
+               }
+               got += r;
+       }
+       ret = got;
+
+clean_fd:
+       close(fd);
+out:
+       return ret;
+}
+
+static char *get_str_by_name_off(struct btf_file *bf, uint32_t name_off)
+{
+       struct btf_file *bf_vmlinux = btf_files_array[0];
+
+       if (bf != bf_vmlinux && name_off >= bf_vmlinux->str_cache_len)
+               return bf->str_cache + name_off - bf_vmlinux->str_cache_len;
+       else
+               return bf_vmlinux->str_cache + name_off;
+}
+
+int get_btf_type_by_type_id(struct btf_file *bf_in, uint32_t id_in,
+                           struct btf_type *bt_out, struct name_entry **en_out)
+{
+       struct name_entry *en;
+       struct btf_file *bf_vmlinux = btf_files_array[0];
+
+       if (bf_in != bf_vmlinux && id_in > bf_vmlinux->array_len) {
+               en = bf_in->array[id_in - bf_vmlinux->array_len - 1];
+       } else {
+               en = bf_vmlinux->array[id_in - 1];
+       }
+       if (en_out)
+               *en_out = en;
+       return read_file_at_offset(en->bf->file_name,
+               en->btf_type_offset + en->bf->types_data_offset,
+               sizeof(*bt_out), bt_out);
+}
+
+/*
+* Parse the btf data and install elements into hashtable and array for quick
+* reference.
+*/
+static int parse_btf_data(char *file_path, char *data_start, uint32_t data_len)
+{
+       struct btf_header *hdr = (struct btf_header *)data_start;
+       struct btf_file *bf = NULL;
+       void *type_start;
+       char *str_start;
+       struct btf_type *tp;
+       struct name_entry *en;
+       int i, j, scale;
+
+       /* We do some check first */
+       if (hdr->magic != BTF_MAGIC) {
+               fprintf(stderr, "%s: Invalid BTF magic in %s\n",
+                       __func__, file_path);
+               goto out;
+       }
+       if (hdr->hdr_len != sizeof(*hdr)) {
+               fprintf(stderr, "%s: Invalid BTF header length in %s\n",
+                       __func__, file_path);
+               goto out;
+       }
+       if (hdr->hdr_len + hdr->str_off + hdr->str_len > data_len) {
+               fprintf(stderr, "%s: String section exceeds data length in 
%s\n",
+                       __func__, file_path);
+               goto out;
+       }
+       if (hdr->hdr_len + hdr->type_off + hdr->type_len > data_len) {
+               fprintf(stderr, "%s: Type section exceeds data length in %s\n",
+                       __func__, file_path);
+               goto out;
+       }
+
+       /* Let's start parsing */
+       bf = (struct btf_file *)malloc(sizeof(*bf));
+       if (!bf)
+               goto no_mem;
+       memset(bf, 0, sizeof(*bf));
+
+       /* Enlarge array when reach power of 2 */
+       btf_files_array[btf_files_array_index++] = bf;
+       if ((btf_files_array_index & (btf_files_array_index - 1)) == 0) {
+               struct btf_file **tmp = (struct btf_file 
**)reallocarray(btf_files_array,
+                                               btf_files_array_index << 1,
+                                               sizeof(struct btf_file *));
+               if (!tmp)
+                       goto no_mem;
+               btf_files_array = tmp;
+       }
+
+       type_start = data_start + hdr->hdr_len + hdr->type_off;
+       str_start = data_start + hdr->hdr_len + hdr->str_off;
+
+       bf->str_cache = malloc(hdr->str_len);
+       if (!bf->str_cache)
+               goto no_mem;
+       memcpy(bf->str_cache, str_start, hdr->str_len);
+       bf->str_cache_len = hdr->str_len;
+
+       bf->array_len = 64;
+       bf->array = (struct name_entry **)calloc(bf->array_len,
+                       sizeof(struct name_entry *));
+       if (!bf->array)
+               goto no_mem;
+
+       bf->file_name = strdup(file_path);
+       if (!bf->file_name)
+               goto no_mem;
+       bf->types_data_offset = (char *)type_start - data_start;
+
+       for (tp = (struct btf_type *)type_start, i = 0;
+            (void *)tp < type_start + hdr->type_len;
+            tp = btf_next(tp), i++) {
+               en = (struct name_entry *)malloc(sizeof(struct name_entry));
+               if (!en) {
+                       bf->array_len = i;
+                       goto no_mem;
+               }
+               memset(en, 0, sizeof(struct name_entry));
+
+               en->btf_type_offset = (void *)tp - type_start;
+               en->id = i + 1;
+               en->bf = bf;
+               if (tp->name_off) {
+                       en->name = get_str_by_name_off(bf, tp->name_off);
+                       if (en->name[0])
+                               name_hash_install(en);
+               }
+               bf->array[i] = en;
+
+               /* Now deal with sub elements which also have a name */
+               if (btf_kind(tp->info) == BTF_KIND_ENUM ||
+                   btf_kind(tp->info) == BTF_KIND_ENUM64) {
+                       scale = btf_kind(tp->info) == BTF_KIND_ENUM ?
+                               sizeof(struct btf_enum) : sizeof(struct 
btf_enum64);
+                       for (j = 0; j < btf_vlen(tp->info); j++) {
+                               en = (struct name_entry *)malloc(sizeof(struct 
name_entry));
+                               if (!en) {
+                                       bf->array_len = i + 1;
+                                       goto no_mem;
+                               }
+                               memset(en, 0, sizeof(struct name_entry));
+                               en->id = 0;
+                               en->p_id = i + 1;
+                               en->p_id += btf_files_array_index == 1 ?
+                                       0 : btf_files_array[0]->array_len;
+                               en->bf = bf;
+                               en->name = get_str_by_name_off(bf,
+                                       *(uint32_t *)((char *)&tp[1] + j * 
scale));
+                               // printf("%s\n", en->name);
+                               name_hash_install(en);
+                       }
+               }
+
+               /* Enlarge array when over 3/4 */
+               if (i > (bf->array_len >> 2) * 3) {
+                       struct name_entry **tmp = (struct name_entry 
**)reallocarray(bf->array,
+                               bf->array_len << 1, sizeof(struct name_entry 
*));
+                       if (!tmp)
+                               goto no_mem;
+                       bf->array = tmp;
+                       bf->array_len <<= 1;
+               }
+       }
+       bf->array_len = i;
+       return 0;
+no_mem:
+       /* All the memory free will be dealed later. */
+       fprintf(stderr, "%s: Not enough memory!\n", __func__);
+out:
+       return -1;
+}
+
+/* 
+ * Used by search_name_by_cond() for searching specific type:
+ * such as struct/union/typedef
+ */
+static bool is_type(struct name_entry *sp, void *data_in, void *data_out)
+{
+       struct btf_type *bt = (struct btf_type *)data_out;
+       int type = *(int *)data_in;
+       if (sp->id) {
+               read_file_at_offset(sp->bf->file_name,
+                       sp->btf_type_offset + sp->bf->types_data_offset,
+                       sizeof(*bt), bt);
+               return btf_kind(bt->info) == type;
+       }
+       return false;
+}
+
+static struct name_entry *search_name_by_cond(char *name,
+                       bool (*fn)(struct name_entry *, void *, void *),
+                       void *data_in, void *data_out)
+{
+       unsigned int index;
+       struct name_entry *sp;
+
+       index = name_hash_index(name);
+       for (sp = name_hash_table[index]; sp; sp = sp->name_hash_next) {
+               if (!strcmp(name, sp->name) && fn(sp, data_in, data_out)) {
+                       return sp;
+               }
+       }
+       return sp;
+}
+
+// caller should prepare bt
+void resolve_typedef(struct name_entry *en_in, struct name_entry **en_out,
+                    struct btf_type *bt)
+{
+       uint32_t id;
+       struct name_entry *en;
+
+       read_file_at_offset(en_in->bf->file_name,
+               en_in->btf_type_offset + en_in->bf->types_data_offset,
+               sizeof(*bt), bt);
+       if (btf_kind(bt->info) == BTF_KIND_TYPEDEF) {
+               id = bt->type;
+               get_btf_type_by_type_id(en_in->bf, id, bt, &en);
+               return resolve_typedef(en, en_out, bt);
+       } else {
+               *en_out = en_in;
+       }
+}
+
+struct cond_args {
+       int index;
+       char *name;
+};
+
+static bool cond(struct cond_args *c1, struct cond_args *c2)
+{
+       if (c1->name && c2->name)
+               /* Check if the member name is what we want */
+               return !strcmp(c1->name, c2->name);
+       else
+               /* Check if the member index is what we want */
+               return c1->index == c2->index;
+}
+
+static bool get_internal_member_info(struct name_entry *en, int base_position,
+                                    int *global_index, struct cond_args 
*tar_arg,
+                                    struct member_info *mi, bool initial_dive)
+{
+       struct btf_type bt, sub_bt;
+       int member_num;
+       char *member_array_buf = NULL;
+       int i;
+       struct btf_member *bm;
+       struct btf_array *ba;
+       int bm_position;
+       struct name_entry *sub_en;
+       bool res;
+       struct cond_args cur_arg = {0};
+
+       read_file_at_offset(en->bf->file_name,
+               en->btf_type_offset + en->bf->types_data_offset,
+               sizeof(bt), &bt); // this struct 
+       if (initial_dive ||
+           ((!en->name || en->name[0] == '\0') &&
+            ((btf_kind(bt.info) == BTF_KIND_STRUCT) ||
+             (btf_kind(bt.info) == BTF_KIND_UNION)))) {
+               /* anonymous struct/union, dive into */
+               member_num = btf_vlen(bt.info);
+               member_array_buf = calloc(member_num, sizeof(struct 
btf_member));
+               if (!member_array_buf) {
+                       fprintf(stderr, "%s: Not enough memory!\n", __func__);
+                       return false;
+               }
+               read_file_at_offset(en->bf->file_name,
+                       en->btf_type_offset + en->bf->types_data_offset + 
sizeof(bt),
+                       member_num *sizeof(struct btf_member), 
member_array_buf);
+               for (i = 0, bm = (struct btf_member *)member_array_buf;
+               i < member_num; i++) {
+                       if (btf_kind_flag(bt.info)) {
+                               bm_position = base_position +
+                                       btf_member_bit_offset(bm[i].offset);
+                               mi->bits = 
btf_member_bitfield_size(bm[i].offset);
+                       } else {
+                               bm_position = base_position +
+                                       bm[i].offset;
+                       }
+                       mi->sname = get_str_by_name_off(en->bf, bm[i].name_off);
+                       get_btf_type_by_type_id(en->bf, bm[i].type,
+                                               &sub_bt, &sub_en);
+                       // Dive into this member
+                       res = get_internal_member_info(sub_en, bm_position, 
global_index,
+                                               tar_arg, mi, false);
+                       if (res) {
+                               free(member_array_buf);
+                               return res;
+                       }                       
+               }
+               free(member_array_buf);
+               return false;
+       }
+
+       cur_arg.index = *global_index;
+       cur_arg.name = mi->sname;
+       if (cond(&cur_arg, tar_arg)) {
+               mi->bit_pos = base_position;
+               resolve_typedef(en, &sub_en, &bt);
+               mi->uniq_id = id_to_uniq_id(sub_en->id, sub_en->bf);
+               if (btf_kind(bt.info) == BTF_KIND_PTR) {
+                       /* 
+                        * BUG? No pointer size in btf,
+                        * 32bit btf target on 64bit machine
+                        */
+                       mi->size = sizeof(void *);
+                       mi->type = "char *";
+               } else if (btf_kind(bt.info) == BTF_KIND_ARRAY) {
+                       en = sub_en;
+                       ba = (struct btf_array *)malloc(sizeof(struct 
btf_array));
+                       if (!ba) {
+                               fprintf(stderr, "%s: Not enough memory!\n", 
__func__);
+                               return false;
+                       }
+                       read_file_at_offset(en->bf->file_name,
+                               en->btf_type_offset + en->bf->types_data_offset 
+ sizeof(bt),
+                               sizeof(struct btf_array), ba);
+                       mi->size = ba->nelems; // array elements
+                       get_btf_type_by_type_id(en->bf, ba->type, &sub_bt, 
&sub_en);
+                       resolve_typedef(sub_en, &en, &bt);
+                       free(ba);
+                       mi->size *= bt.size; // element size
+                       mi->type = NULL; // "char [64]? Leave it NULL for now"
+               } else {
+                       mi->size = bt.size;
+                       mi->type = get_str_by_name_off(sub_en->bf, bt.name_off);
+               }
+               return true;
+       } else {
+               (*global_index)++;
+               return false;
+       }
+}
+
+static bool get_member_info_by_index(struct name_entry *en, int target_index,
+                                    struct member_info *mi)
+{
+       int g_index = 1;
+       struct cond_args args = {0};
+       args.index = target_index;
+       memset(mi, 0, sizeof(*mi));
+       return get_internal_member_info(en, 0, &g_index, &args, mi, true);
+}
+
+static bool get_member_info_by_member_name(struct name_entry *en, char 
*member_name,
+                                          struct member_info *mi)
+{
+       int g_index = 1;
+       struct cond_args args = {0};
+       args.name = member_name;
+       memset(mi, 0, sizeof(*mi));
+       return get_internal_member_info(en, 0, &g_index, &args, mi, true);
+}
+
+/* 
+ * Entry for query struct members
+ * Return: struct size, mi_out: member details
+ */
+uint32_t get_struct_member_by_name(char *struct_name, char *member_name,
+                                  struct member_info *mi_out)
+{
+       struct name_entry *en;
+       struct btf_type bt = {0};
+       int type = BTF_KIND_STRUCT;
+
+       en = search_name_by_cond(struct_name, is_type, (void *)&type, (void 
*)&bt);
+       if (!en)
+               goto out;       
+       if (get_member_info_by_member_name(en, member_name, mi_out))
+               return bt.size;
+out:
+       return 0;
+}
+
+/* 
+ * Entry for query type members, similar as above
+ * Return: found-the-type, mi_out: member details
+ */
+static uint32_t uniq_id_to_id(uint32_t, struct btf_file **);
+bool get_type_member_by_index(uint64_t type_uniq_id, int member_index,
+                             struct member_info *mi_out)
+{
+       uint32_t id;
+       struct btf_file *bf;
+
+       id = uniq_id_to_id((uint32_t)type_uniq_id, &bf);
+       return get_member_info_by_index(bf->array[id - 1], member_index, 
mi_out);
+}
+
+uint32_t id_to_uniq_id(uint32_t id, struct btf_file *bf)
+{
+       int i;
+       uint32_t uniq_id = 0;
+       for (i = 0; i < btf_files_array_index; i++) {
+               if (btf_files_array[i] != bf)
+                       uniq_id += btf_files_array[i]->array_len;
+               else
+                       return id + uniq_id;
+       }
+       __builtin_unreachable();
+       assert(false);
+}
+
+static uint32_t uniq_id_to_id(uint32_t uid, struct btf_file **bf)
+{
+       int i;
+       
+       for (i = 0; i < btf_files_array_index; i++) {
+               if (uid > btf_files_array[i]->array_len) {
+                       uid -= btf_files_array[i]->array_len;
+               } else {
+                       if (bf)
+                               *bf = btf_files_array[i];
+                       return uid;
+               }
+
+       }
+       __builtin_unreachable();
+       assert(false);
+}
+
+// api for eppic
+uint32_t get_type_size_by_name(char *type_name, int type, uint32_t *uniq_id)
+{
+       struct name_entry *en;
+       struct btf_type bt = {0};
+
+       en = search_name_by_cond(type_name, is_type, (void *)&type, (void 
*)&bt);
+       if (!en)
+               goto out;
+       if (uniq_id)
+               *uniq_id = id_to_uniq_id(en->id, en->bf);
+       return bt.size;
+out:
+       return 0;       
+}
+
+// Caller should prepare bt_out
+struct name_entry *get_en_by_uniq_id(uint32_t uniq_id, struct btf_type *bt_out)
+{
+       uint32_t id;
+       struct btf_file *bf;
+       struct name_entry *en;
+       struct name_entry *en_sub;
+
+       id = uniq_id_to_id((uint32_t)uniq_id, &bf);
+       en = bf->array[id - 1];
+       resolve_typedef(en, &en_sub, bt_out);
+       return en_sub;
+}
+
+/* Deal with btf file */
+static int btf_file_init(int fd)
+{
+       struct stat f_stat;
+       char *buf = NULL;
+       char proc_path[32];
+       char real_path[512];
+       int ret = -1;
+
+       memset(real_path, 0, sizeof(real_path));
+       if (fstat(fd, &f_stat) < 0) {
+               fprintf(stderr, "%s: fstat fail!\n", __func__);
+               goto out;
+       }
+       buf = malloc(f_stat.st_size);
+       if (!buf) {
+               fprintf(stderr, "%s: Not enough memory!\n", __func__);
+               goto out;
+       }
+
+       snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd);
+       readlink(proc_path, real_path, sizeof(real_path) - 1);
+       if (read_file_at_offset(real_path, 0, f_stat.st_size, buf) < 0)
+               goto out;
+
+       ret = parse_btf_data(real_path, buf, f_stat.st_size);
+out:
+       if (buf)
+               free(buf);
+       return ret;
+}
+
+/* Deal with elf file which contains .BTF section */
+static int elf_file_init(int fd)
+{
+       char proc_path[32];
+       char real_path[512];
+       Elf *elf = NULL;
+       Elf_Scn *scn = NULL;
+       size_t shstrndx;
+       GElf_Shdr shdr;
+       Elf_Data *data = NULL;
+       char *str, *databuf = NULL;
+       int ret = -1;
+
+       snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd);
+       readlink(proc_path, real_path, sizeof(real_path) - 1);
+
+       if (elf_version(EV_CURRENT) == EV_NONE)
+               goto elf_err;
+       elf = elf_begin(fd, ELF_C_READ, NULL);
+       if (!elf || elf_getshdrstrndx(elf, &shstrndx))
+               goto elf_err;
+       while ((scn = elf_nextscn(elf, scn)) != NULL) {
+               if (!gelf_getshdr(scn, &shdr))
+                       continue;
+               str = elf_strptr(elf, shstrndx, shdr.sh_name);
+               if (!strcmp(str, ".BTF"))
+                       break;
+       }
+       if (!scn) {
+               fprintf(stderr, "%s: No .BTF found in %s!\n", __func__, 
real_path);
+               goto out;
+       }
+
+       data = elf_rawdata(scn, data);
+       databuf = malloc(data->d_size);
+       if (!databuf) {
+               fprintf(stderr, "%s: Not enough memory!\n", __func__);
+               goto out;
+       }
+       memcpy(databuf, data->d_buf, data->d_size);
+       elf_end(elf);
+       elf = NULL;
+       ret = parse_btf_data(real_path, databuf, data->d_size);
+       goto out;
+
+elf_err:
+       fprintf(stderr, "%s: elf error in %s!\n", __func__, real_path);
+out:
+       if (databuf)
+               free(databuf);
+       if (elf)
+               elf_end(elf);
+       return ret;
+}
+
+/*
+ * Entry for parse btf file and elf file.
+ */
+static int file_init(char *name)
+{
+       int fd = 0, ret = -1;
+       char buf[4] = {0};
+
+       /* Will be enlarged automatically */
+       if (!btf_files_array) {
+               btf_files_array = calloc(1, sizeof(struct btf_file *));
+               if (!btf_files_array) {
+                       fprintf(stderr, "%s: Not enough memory!\n", __func__);
+                       goto out;
+               }
+       }
+       
+       fd = open(name, O_RDONLY);
+       if (read_file_at_offset(name, 0, sizeof(buf), buf) < 0)
+               goto out;
+       if (*(u_int16_t *)&buf == BTF_MAGIC) {
+               ret = btf_file_init(fd);
+       } else if (*(u_int32_t *)&buf == (((u_int32_t)ELFMAG0 << 24) | 
+                                         ((u_int32_t)ELFMAG1 << 16) |
+                                         ((u_int32_t)ELFMAG2 << 8)  |
+                                         ((u_int32_t)ELFMAG3 << 0))) {
+               ret = elf_file_init(fd);
+       } else {
+               __builtin_unreachable();
+       }
+
+out:
+       if (fd)
+               close(fd);
+       return ret;
+}
+
+/* For pure buffer data, wrap it as a file, and handover to file_init() */
+char temp[] = "/tmp/btf_XXXXXX";
+static int init_btf_from_buf(char *mod_name, char *buf, int size)
+{
+       int fd;
+       char name_buf[64];
+       int w, got = 0;
+       static char *temp_dir = NULL;
+
+       if (!temp_dir)
+               temp_dir = mkdtemp(temp);
+       snprintf(name_buf, sizeof(name_buf), "%s/%s", temp_dir, mod_name);
+       fd = open(name_buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+       if (fd < 0) {
+               fprintf(stderr, "%s: open fail in %s!\n", __func__, name_buf);
+               return -1;
+       }
+
+       while (got < size) {
+               w = pwrite(fd, buf + got, size - got, got);
+               if (w < 0) {
+                       fprintf(stderr, "%s: pwrite fail in %s!\n", __func__, 
name_buf);
+                       return -1;
+               }
+               got += w;
+       }
+       close(fd);
+       return file_init(name_buf);
+}
+
+int init_kernel_btf(void)
+{
+       uint64_t size;
+       char *buf;
+       int ret;
+
+       uint64_t start_btf = get_kallsyms_value_by_name("__start_BTF");
+       uint64_t stop_btf = get_kallsyms_value_by_name("__stop_BTF");
+       if (!start_btf || !stop_btf) {
+               fprintf(stderr, "%s: symbol __start/stop_BTF not found!\n", 
__func__);
+               return -1;
+       }
+
+       size = stop_btf - start_btf;
+       buf = (char *)malloc(size);
+       if (!buf) {
+               fprintf(stderr, "%s: Not enough memory!\n", __func__);
+               return -1;
+       }
+       readmem(VADDR, start_btf, buf, size);
+       ret = init_btf_from_buf("vmlinux", buf, size);
+       free(buf);
+
+       return ret;
+}
+
+int init_module_btf(void)
+{
+       uint64_t btf_modules, list;
+       struct member_info mi;
+       uint64_t btf = 0, data = 0, module = 0;
+       int data_size = 0;
+       char *btf_buf = NULL;
+       char *modname = NULL;
+
+       btf_modules = get_kallsyms_value_by_name("btf_modules");
+       if (!btf_modules)
+               /* Maybe module is not enabled, this is not an error */
+               return 0;
+
+       INIT_MEMBER_OFF_SIZE(btf_module, list);
+       INIT_MEMBER_OFF_SIZE(btf_module, btf);
+       INIT_MEMBER_OFF_SIZE(btf_module, module);
+       INIT_MEMBER_OFF_SIZE(module, name);
+       INIT_MEMBER_OFF_SIZE(btf, data);
+       INIT_MEMBER_OFF_SIZE(btf, data_size);
+       modname = (char *)malloc(GET_MEMBER_SIZE(module, name));
+       if (!modname) {
+               fprintf(stderr, "%s: Not enough memory!\n", __func__);
+               goto out;
+       }
+
+       for (list = next_list(btf_modules); list != btf_modules; list = 
next_list(list)) {
+               readmem(VADDR, list - GET_MEMBER_OFF(btf_module, list) +
+                               GET_MEMBER_OFF(btf_module, btf),
+                       &btf, GET_MEMBER_SIZE(btf_module, btf));
+               readmem(VADDR, list - GET_MEMBER_OFF(btf_module, list) +
+                               GET_MEMBER_OFF(btf_module, module),
+                       &module, GET_MEMBER_SIZE(btf_module, module));
+               readmem(VADDR, module + GET_MEMBER_OFF(module, name),
+                       modname, GET_MEMBER_SIZE(module, name));
+               readmem(VADDR, btf + GET_MEMBER_OFF(btf, data),
+                       &data, GET_MEMBER_SIZE(btf, data));             
+               readmem(VADDR, btf + GET_MEMBER_OFF(btf, data_size),
+                       &data_size, GET_MEMBER_SIZE(btf, data_size));
+               btf_buf = (char *)malloc(data_size);
+               if (!btf_buf) {
+                       fprintf(stderr, "%s: Not enough memory!\n", __func__);
+                       goto out;
+               }
+               readmem(VADDR, data, btf_buf, data_size);
+               if (init_btf_from_buf(modname, btf_buf, data_size))
+                       goto out;
+               free(btf_buf);
+       }
+       return 0;
+out:
+       if (modname)
+               free(modname);
+       if (btf_buf)
+               free(btf_buf);
+       return -1;
+}
+
+void cleanup_btf(void)
+{
+       int bf_index, i;
+       struct btf_file *bf;
+       struct name_entry *en, *en_tmp;
+       DIR *temp_dir;
+       struct dirent *entry;
+       char path[512];
+
+       /* Free no-hashtable-installed elements */
+       for (bf_index = 0; bf_index < btf_files_array_index; bf_index++) {
+               bf = btf_files_array[bf_index];
+               if (bf && bf->array) {
+                       for (i = 0; i < bf->array_len; i++) {
+                               if (!bf->array[i]->name || 
!bf->array[i]->name[0])
+                                       free(bf->array[i]);
+                       }
+               }
+       }
+
+       /* Free the hashtable-installed elements */
+       for (i = 0; i < NAME_HASH; i++) {
+               for (en = name_hash_table[i]; en;) {
+                       en_tmp = en;
+                       en = en->name_hash_next;
+                       free(en_tmp);
+               }
+       }
+
+       /* Cleanup anything else */
+       for (bf_index = 0; bf_index < btf_files_array_index; bf_index++) {
+               bf = btf_files_array[bf_index];
+               if (bf && bf->array) {
+                       free(bf->array);
+               }
+               if (bf && bf->file_name)
+                       free(bf->file_name);
+               if (bf && bf->str_cache)
+                       free(bf->str_cache);
+               if (bf)
+                       free(bf);
+       }
+
+       if (btf_files_array)
+               free(btf_files_array);
+       
+       /* Cleanup the dir /tmp/btf_XXXXXX */
+       temp_dir = opendir(temp);
+       if (!temp_dir)
+               return;
+       while ((entry = readdir(temp_dir)) != NULL) {
+               if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+                       continue;
+               snprintf(path, sizeof(path), "%s/%s", temp, entry->d_name);
+               if (remove(path)) {
+                       fprintf(stderr, "%s: fail del %s!\n", __func__, path);
+                       /* In case too many fail messages of files in the dir */
+                       return;
+               }
+       }
+       closedir(temp_dir);
+       rmdir(temp);
+}
diff --git a/btf.h b/btf.h
new file mode 100644
index 0000000..a0d13e6
--- /dev/null
+++ b/btf.h
@@ -0,0 +1,176 @@
+#ifndef _BTF_H
+#define _BTF_H
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef uint8_t  __u8;
+typedef uint16_t __u16;
+typedef uint32_t __u32;
+typedef int32_t __s32;
+
+#define BTF_MAGIC      0xeb9f
+
+enum {
+       BTF_KIND_UNKN           = 0,    /* Unknown      */
+       BTF_KIND_INT            = 1,    /* Integer      */
+       BTF_KIND_PTR            = 2,    /* Pointer      */
+       BTF_KIND_ARRAY          = 3,    /* Array        */
+       BTF_KIND_STRUCT         = 4,    /* Struct       */
+       BTF_KIND_UNION          = 5,    /* Union        */
+       BTF_KIND_ENUM           = 6,    /* Enumeration  */
+       BTF_KIND_FWD            = 7,    /* Forward      */
+       BTF_KIND_TYPEDEF        = 8,    /* Typedef      */
+       BTF_KIND_VOLATILE       = 9,    /* Volatile     */
+       BTF_KIND_CONST          = 10,   /* Const        */
+       BTF_KIND_RESTRICT       = 11,   /* Restrict     */
+       BTF_KIND_FUNC           = 12,   /* Function     */
+       BTF_KIND_FUNC_PROTO     = 13,   /* Function Proto       */
+       BTF_KIND_VAR            = 14,   /* Variable     */
+       BTF_KIND_DATASEC        = 15,   /* Section      */
+       BTF_KIND_FLOAT          = 16,   /* Floating point       */
+       BTF_KIND_DECL_TAG       = 17,   /* Decl Tag */
+       BTF_KIND_TYPE_TAG       = 18,   /* Type Tag */
+       BTF_KIND_ENUM64 = 19,   /* Enumeration up to 64-bit values */
+
+       NR_BTF_KINDS,
+       BTF_KIND_MAX            = NR_BTF_KINDS - 1,
+};
+
+struct btf_type {
+       __u32 name_off;
+
+       /* "info" bits arrangement
+        * bits  0-15: vlen (e.g. # of struct's members)
+        * bits 16-23: unused
+        * bits 24-27: kind (e.g. int, ptr, array...etc)
+        * bits 28-30: unused
+        * bit     31: kind_flag, currently used by
+        *             struct, union and fwd
+        */
+       __u32 info;
+
+       /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
+        * "size" tells the size of the type it is describing.
+        *
+        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
+        * FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
+        * "type" is a type_id referring to another type.
+        */
+       union {
+               __u32 size;
+               __u32 type;
+       };
+};
+
+struct btf_header {
+       __u16   magic;
+       __u8    version;
+       __u8    flags;
+       __u32   hdr_len;
+
+       /* All offsets are in bytes relative to the end of this header */
+       __u32   type_off;       /* offset of type section       */
+       __u32   type_len;       /* length of type section       */
+       __u32   str_off;        /* offset of string section     */
+       __u32   str_len;        /* length of string section     */
+};
+
+struct btf_array {
+       __u32   type;
+       __u32   index_type;
+       __u32   nelems;
+};
+
+struct btf_member {
+       __u32   name_off;
+       __u32   type;
+       /* If the type info kind_flag is set, the btf_member offset
+        * contains both member bitfield size and bit offset. The
+        * bitfield size is set for bitfield members. If the type
+        * info kind_flag is not set, the offset contains only bit
+        * offset.
+        */
+       __u32   offset;
+};
+
+struct btf_enum {
+       __u32   name_off;
+       __s32   val;
+};
+
+struct btf_enum64 {
+       __u32   name_off;
+       __u32   val_lo32;
+       __u32   val_hi32;
+};
+
+struct btf_param {
+       __u32   name_off;
+       __u32   type;
+};
+
+struct btf_var {
+       __u32   linkage;
+};
+
+struct btf_var_secinfo {
+       __u32   type;
+       __u32   offset;
+       __u32   size;
+};
+
+struct btf_decl_tag {
+       __s32   component_idx;
+};
+
+/*************************************/
+struct btf_file {
+       char *file_name;
+       char *str_cache;
+       uint32_t str_cache_len;
+       uint32_t types_data_offset;
+       uint32_t array_len;
+       struct name_entry **array;
+};
+
+struct name_entry {
+       union {
+               uint32_t btf_type_offset;
+               uint32_t p_id;
+       };
+
+       uint32_t id;
+       char *name;
+       struct btf_file *bf;
+       struct name_entry *name_hash_next;
+};
+
+struct member_info {
+       char *sname;    // member name
+       char *type;     // member type: int, long etc
+       uint32_t bit_pos;       // member position in bits
+       uint32_t bits;  // member width in bits
+       uint32_t size;  // member size in bytes
+       int uniq_id;    // uniq_id of btf
+};
+
+/*************************************/
+
+int init_kernel_btf(void);
+uint32_t get_struct_member_by_name(char *, char *, struct member_info *);
+bool get_type_member_by_index(uint64_t, int, struct member_info *);
+uint32_t get_type_size_by_name(char *, int, uint32_t *);
+struct name_entry *get_en_by_uniq_id(uint32_t, struct btf_type *);
+void resolve_typedef(struct name_entry *, struct name_entry **, struct 
btf_type *);
+int get_btf_type_by_type_id(struct btf_file *, uint32_t,
+                           struct btf_type *, struct name_entry **);
+uint32_t id_to_uniq_id(uint32_t, struct btf_file *);
+int init_module_btf(void);
+void cleanup_btf(void);
+
+static inline uint32_t btf_kind(uint32_t info)
+{
+       return (info & 0x1F000000) >> 24;
+}
+
+#endif /* _BTF_H */
diff --git a/kallsyms.c b/kallsyms.c
new file mode 100644
index 0000000..89f6bfa
--- /dev/null
+++ b/kallsyms.c
@@ -0,0 +1,371 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#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;
+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 512
+int parse_kernel_kallsyms(void)
+{
+       char buf[BUFLEN];
+       int len = 0;
+       int index = 0, i;
+       uint8_t *compressd_data;
+       uint8_t *uncompressd_data;
+       uint64_t stext;
+
+       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++];
+               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;
+       char data;
+       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) {
+                       printf("BUG! long sym name");
+                       goto out;
+               }
+               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;
+}
+
+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)
+{
+       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..96ea970
--- /dev/null
+++ b/kallsyms.h
@@ -0,0 +1,42 @@
+#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);
+int parse_kernel_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 *,
+               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
--
Crash-utility mailing list -- devel@lists.crash-utility.osci.io
To unsubscribe send an email to devel-le...@lists.crash-utility.osci.io
https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/
Contribution Guidelines: https://github.com/crash-utility/crash/wiki

Reply via email to