2444 lines
69 KiB
Diff
2444 lines
69 KiB
Diff
|
From 7750e61fdb2a083f26156a5338aa2ebe26447f3f Mon Sep 17 00:00:00 2001
|
||
|
From: Kazuhito Hagio <k-hagio-ab@nec.com>
|
||
|
Date: Thu, 22 Jun 2023 16:09:07 +0900
|
||
|
Subject: [PATCH 13/13] Support module memory layout change on Linux 6.4
|
||
|
|
||
|
Support module memory layout change on Linux 6.4 by kernel commit
|
||
|
ac3b43283923 ("module: replace module_layout with module_memory") [1].
|
||
|
Without the patch, crash cannot even start a session with an error
|
||
|
message like this:
|
||
|
|
||
|
crash: invalid structure member offset: module_core_size
|
||
|
FILE: kernel.c LINE: 3787 FUNCTION: module_init()
|
||
|
|
||
|
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ac3b43283923
|
||
|
|
||
|
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
|
||
|
Signed-off-by: Lianbo Jiang <lijiang@redhat.com>
|
||
|
---
|
||
|
defs.h | 46 +-
|
||
|
gdb-10.2.patch | 25 +
|
||
|
kernel.c | 56 +-
|
||
|
memory.c | 38 +-
|
||
|
symbols.c | 1609 ++++++++++++++++++++++++++++++++++++++++++++----
|
||
|
5 files changed, 1629 insertions(+), 145 deletions(-)
|
||
|
|
||
|
diff --git a/defs.h b/defs.h
|
||
|
index 3e7d6cfbc6a8..414853660dc1 100644
|
||
|
--- a/defs.h
|
||
|
+++ b/defs.h
|
||
|
@@ -675,6 +675,7 @@ struct new_utsname {
|
||
|
#define IRQ_DESC_TREE_RADIX (0x40ULL)
|
||
|
#define IRQ_DESC_TREE_XARRAY (0x80ULL)
|
||
|
#define KMOD_PAX (0x100ULL)
|
||
|
+#define KMOD_MEMORY (0x200ULL)
|
||
|
|
||
|
#define XEN() (kt->flags & ARCH_XEN)
|
||
|
#define OPENVZ() (kt->flags & ARCH_OPENVZ)
|
||
|
@@ -682,6 +683,7 @@ struct new_utsname {
|
||
|
#define PVOPS_XEN() (kt->flags & ARCH_PVOPS_XEN)
|
||
|
|
||
|
#define PAX_MODULE_SPLIT() (kt->flags2 & KMOD_PAX)
|
||
|
+#define MODULE_MEMORY() (kt->flags2 & KMOD_MEMORY)
|
||
|
|
||
|
#define XEN_MACHINE_TO_MFN(m) ((ulonglong)(m) >> PAGESHIFT())
|
||
|
#define XEN_PFN_TO_PSEUDO(p) ((ulonglong)(p) << PAGESHIFT())
|
||
|
@@ -2217,6 +2219,9 @@ struct offset_table { /* stash of commonly-used offsets */
|
||
|
long kset_kobj;
|
||
|
long subsys_private_subsys;
|
||
|
long vmap_area_purge_list;
|
||
|
+ long module_mem;
|
||
|
+ long module_memory_base;
|
||
|
+ long module_memory_size;
|
||
|
};
|
||
|
|
||
|
struct size_table { /* stash of commonly-used sizes */
|
||
|
@@ -2390,6 +2395,7 @@ struct size_table { /* stash of commonly-used sizes */
|
||
|
long percpu_counter;
|
||
|
long maple_tree;
|
||
|
long maple_node;
|
||
|
+ long module_memory;
|
||
|
};
|
||
|
|
||
|
struct array_table {
|
||
|
@@ -2922,6 +2928,23 @@ struct mod_section_data {
|
||
|
ulong size;
|
||
|
int priority;
|
||
|
int flags;
|
||
|
+ ulong addr;
|
||
|
+};
|
||
|
+
|
||
|
+/* Emulate enum mod_mem_type in include/linux/module.h */
|
||
|
+#define MOD_TEXT (0)
|
||
|
+#define MOD_DATA (1)
|
||
|
+#define MOD_RODATA (2)
|
||
|
+#define MOD_RO_AFTER_INIT (3)
|
||
|
+#define MOD_INIT_TEXT (4)
|
||
|
+#define MOD_INIT_DATA (5)
|
||
|
+#define MOD_INIT_RODATA (6)
|
||
|
+#define MOD_MEM_NUM_TYPES (7)
|
||
|
+#define MOD_INVALID (-1)
|
||
|
+
|
||
|
+struct module_memory {
|
||
|
+ ulong base;
|
||
|
+ uint size;
|
||
|
};
|
||
|
|
||
|
struct load_module {
|
||
|
@@ -2957,19 +2980,29 @@ struct load_module {
|
||
|
ulong mod_percpu;
|
||
|
ulong mod_percpu_size;
|
||
|
struct objfile *loaded_objfile;
|
||
|
-};
|
||
|
|
||
|
-#define IN_MODULE(A,L) \
|
||
|
- (((ulong)(A) >= (L)->mod_base) && ((ulong)(A) < ((L)->mod_base+(L)->mod_size)))
|
||
|
-
|
||
|
-#define IN_MODULE_INIT(A,L) \
|
||
|
- (((ulong)(A) >= (L)->mod_init_module_ptr) && ((ulong)(A) < ((L)->mod_init_module_ptr+(L)->mod_init_size)))
|
||
|
+ /* For 6.4 module_memory */
|
||
|
+ struct module_memory mem[MOD_MEM_NUM_TYPES];
|
||
|
+ struct syment **symtable;
|
||
|
+ struct syment **symend;
|
||
|
+ struct syment *ext_symtable[MOD_MEM_NUM_TYPES];
|
||
|
+ struct syment *ext_symend[MOD_MEM_NUM_TYPES];
|
||
|
+ struct syment *load_symtable[MOD_MEM_NUM_TYPES];
|
||
|
+ struct syment *load_symend[MOD_MEM_NUM_TYPES];
|
||
|
+};
|
||
|
|
||
|
+#define IN_MODULE(A,L) (in_module_range(A, L, MOD_TEXT, MOD_RO_AFTER_INIT) != MOD_INVALID)
|
||
|
+#define IN_MODULE_INIT(A,L) (in_module_range(A, L, MOD_INIT_TEXT, MOD_INIT_RODATA) != MOD_INVALID)
|
||
|
+#define IN_MODULE_TEXT(A,L) (in_module_range(A, L, MOD_TEXT, MOD_TEXT) == MOD_TEXT || \
|
||
|
+ in_module_range(A, L, MOD_INIT_TEXT, MOD_INIT_TEXT) == MOD_INIT_TEXT)
|
||
|
#define IN_MODULE_PERCPU(A,L) \
|
||
|
(((ulong)(A) >= (L)->mod_percpu) && ((ulong)(A) < ((L)->mod_percpu+(L)->mod_percpu_size)))
|
||
|
|
||
|
#define MODULE_PERCPU_SYMS_LOADED(L) ((L)->mod_percpu && (L)->mod_percpu_size)
|
||
|
|
||
|
+#define for_each_mod_mem_type(type) \
|
||
|
+ for ((type) = MOD_TEXT; (type) < MOD_MEM_NUM_TYPES; (type)++)
|
||
|
+
|
||
|
#ifndef GDB_COMMON
|
||
|
|
||
|
#define KVADDR (0x1)
|
||
|
@@ -5588,6 +5621,7 @@ void dump_struct_member(char *, ulong, unsigned);
|
||
|
void dump_union(char *, ulong, unsigned);
|
||
|
void store_module_symbols_v1(ulong, int);
|
||
|
void store_module_symbols_v2(ulong, int);
|
||
|
+void store_module_symbols_6_4(ulong, int);
|
||
|
int is_datatype_command(void);
|
||
|
int is_typedef(char *);
|
||
|
int arg_to_datatype(char *, struct datatype_member *, ulong);
|
||
|
diff --git a/gdb-10.2.patch b/gdb-10.2.patch
|
||
|
index 835aae9859be..16228b1dbf73 100644
|
||
|
--- a/gdb-10.2.patch
|
||
|
+++ b/gdb-10.2.patch
|
||
|
@@ -3120,3 +3120,28 @@ exit 0
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
+--- gdb-10.2/gdb/symtab.c.orig
|
||
|
++++ gdb-10.2/gdb/symtab.c
|
||
|
+@@ -7476,7 +7476,7 @@ gdb_add_symbol_file(struct gnu_request *
|
||
|
+ int i;
|
||
|
+ int allsect = 0;
|
||
|
+ char *secname;
|
||
|
+- char buf[80];
|
||
|
++ char buf[96];
|
||
|
+
|
||
|
+ gdb_current_load_module = lm = (struct load_module *)req->addr;
|
||
|
+
|
||
|
+@@ -7515,8 +7515,11 @@ gdb_add_symbol_file(struct gnu_request *
|
||
|
+ secname = lm->mod_section_data[i].name;
|
||
|
+ if ((lm->mod_section_data[i].flags & SEC_FOUND) &&
|
||
|
+ !STREQ(secname, ".text")) {
|
||
|
+- sprintf(buf, " -s %s 0x%lx", secname,
|
||
|
+- lm->mod_section_data[i].offset + lm->mod_base);
|
||
|
++ if (lm->mod_section_data[i].addr)
|
||
|
++ sprintf(buf, " -s %s 0x%lx", secname, lm->mod_section_data[i].addr);
|
||
|
++ else
|
||
|
++ sprintf(buf, " -s %s 0x%lx", secname,
|
||
|
++ lm->mod_section_data[i].offset + lm->mod_base);
|
||
|
+ strcat(req->buf, buf);
|
||
|
+ }
|
||
|
+ }
|
||
|
diff --git a/kernel.c b/kernel.c
|
||
|
index 6e98f5f6f6b1..639ed64f306a 100644
|
||
|
--- a/kernel.c
|
||
|
+++ b/kernel.c
|
||
|
@@ -3571,7 +3571,21 @@ module_init(void)
|
||
|
MEMBER_OFFSET_INIT(module_num_gpl_syms, "module",
|
||
|
"num_gpl_syms");
|
||
|
|
||
|
- if (MEMBER_EXISTS("module", "module_core")) {
|
||
|
+ if (MEMBER_EXISTS("module", "mem")) { /* 6.4 and later */
|
||
|
+ kt->flags2 |= KMOD_MEMORY; /* MODULE_MEMORY() can be used. */
|
||
|
+
|
||
|
+ MEMBER_OFFSET_INIT(module_mem, "module", "mem");
|
||
|
+ MEMBER_OFFSET_INIT(module_memory_base, "module_memory", "base");
|
||
|
+ MEMBER_OFFSET_INIT(module_memory_size, "module_memory", "size");
|
||
|
+ STRUCT_SIZE_INIT(module_memory, "module_memory");
|
||
|
+
|
||
|
+ if (CRASHDEBUG(1))
|
||
|
+ error(INFO, "struct module_memory detected.\n");
|
||
|
+
|
||
|
+ if (get_array_length("module.mem", NULL, 0) != MOD_MEM_NUM_TYPES)
|
||
|
+ error(WARNING, "module memory types have changed!\n");
|
||
|
+
|
||
|
+ } else if (MEMBER_EXISTS("module", "module_core")) {
|
||
|
MEMBER_OFFSET_INIT(module_core_size, "module",
|
||
|
"core_size");
|
||
|
MEMBER_OFFSET_INIT(module_init_size, "module",
|
||
|
@@ -3757,6 +3771,8 @@ module_init(void)
|
||
|
total += nsyms;
|
||
|
total += 2; /* store the module's start/ending addresses */
|
||
|
total += 2; /* and the init start/ending addresses */
|
||
|
+ if (MODULE_MEMORY()) /* 7 regions at most -> 14, so needs +10 */
|
||
|
+ total += 10;
|
||
|
|
||
|
/*
|
||
|
* If the module has kallsyms, set up to grab them as well.
|
||
|
@@ -3784,7 +3800,11 @@ module_init(void)
|
||
|
case KALLSYMS_V2:
|
||
|
if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
|
||
|
numksyms = UINT(modbuf + OFFSET(module_num_symtab));
|
||
|
- size = UINT(modbuf + MODULE_OFFSET2(module_core_size, rx));
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ /* check mem[MOD_TEXT].size only */
|
||
|
+ size = UINT(modbuf + OFFSET(module_mem) + OFFSET(module_memory_size));
|
||
|
+ else
|
||
|
+ size = UINT(modbuf + MODULE_OFFSET2(module_core_size, rx));
|
||
|
} else {
|
||
|
numksyms = ULONG(modbuf + OFFSET(module_num_symtab));
|
||
|
size = ULONG(modbuf + MODULE_OFFSET2(module_core_size, rx));
|
||
|
@@ -3822,7 +3842,10 @@ module_init(void)
|
||
|
store_module_symbols_v1(total, kt->mods_installed);
|
||
|
break;
|
||
|
case KMOD_V2:
|
||
|
- store_module_symbols_v2(total, kt->mods_installed);
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ store_module_symbols_6_4(total, kt->mods_installed);
|
||
|
+ else
|
||
|
+ store_module_symbols_v2(total, kt->mods_installed);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
@@ -3836,7 +3859,7 @@ module_init(void)
|
||
|
static int
|
||
|
verify_modules(void)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
int found, irregularities;
|
||
|
ulong mod, mod_next, mod_base;
|
||
|
long mod_size;
|
||
|
@@ -3893,8 +3916,13 @@ verify_modules(void)
|
||
|
mod_base = mod;
|
||
|
break;
|
||
|
case KMOD_V2:
|
||
|
- mod_base = ULONG(modbuf +
|
||
|
- MODULE_OFFSET2(module_module_core, rx));
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ /* mem[MOD_TEXT].base */
|
||
|
+ mod_base = ULONG(modbuf + OFFSET(module_mem) +
|
||
|
+ OFFSET(module_memory_base));
|
||
|
+ else
|
||
|
+ mod_base = ULONG(modbuf +
|
||
|
+ MODULE_OFFSET2(module_module_core, rx));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
@@ -3916,7 +3944,17 @@ verify_modules(void)
|
||
|
case KMOD_V2:
|
||
|
module_name = modbuf +
|
||
|
OFFSET(module_name);
|
||
|
- if (THIS_KERNEL_VERSION >= LINUX(2,6,27))
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ mod_size = 0;
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (t == MOD_INIT_TEXT)
|
||
|
+ break;
|
||
|
+
|
||
|
+ mod_size += UINT(modbuf + OFFSET(module_mem) +
|
||
|
+ SIZE(module_memory) * t +
|
||
|
+ OFFSET(module_memory_size));
|
||
|
+ }
|
||
|
+ } else if (THIS_KERNEL_VERSION >= LINUX(2,6,27))
|
||
|
mod_size = UINT(modbuf +
|
||
|
MODULE_OFFSET2(module_core_size, rx));
|
||
|
else
|
||
|
@@ -4536,7 +4574,7 @@ do_module_cmd(ulong flag, char *modref, ulong address,
|
||
|
"MODULE"),
|
||
|
mkstring(buf2, maxnamelen, LJUST, "NAME"),
|
||
|
mkstring(buf4, VADDR_PRLEN, CENTER|LJUST,
|
||
|
- "BASE"),
|
||
|
+ MODULE_MEMORY() ? "TEXT_BASE" : "BASE"),
|
||
|
mkstring(buf3, maxsizelen, RJUST, "SIZE"));
|
||
|
}
|
||
|
|
||
|
@@ -6144,6 +6182,8 @@ dump_kernel_table(int verbose)
|
||
|
fprintf(fp, "%sIRQ_DESC_TREE_XARRAY", others++ ? "|" : "");
|
||
|
if (kt->flags2 & KMOD_PAX)
|
||
|
fprintf(fp, "%sKMOD_PAX", others++ ? "|" : "");
|
||
|
+ if (kt->flags2 & KMOD_MEMORY)
|
||
|
+ fprintf(fp, "%sKMOD_MEMORY", others++ ? "|" : "");
|
||
|
fprintf(fp, ")\n");
|
||
|
|
||
|
fprintf(fp, " stext: %lx\n", kt->stext);
|
||
|
diff --git a/memory.c b/memory.c
|
||
|
index ea3005a5c01f..acbee6389472 100644
|
||
|
--- a/memory.c
|
||
|
+++ b/memory.c
|
||
|
@@ -15712,10 +15712,44 @@ in_vmlist_segment(ulong vaddr)
|
||
|
static int
|
||
|
next_module_vaddr(ulong vaddr, ulong *nextvaddr)
|
||
|
{
|
||
|
- int i;
|
||
|
- ulong start, end;
|
||
|
+ int i, t;
|
||
|
+ ulong start, end, min = (ulong)-1;
|
||
|
struct load_module *lm;
|
||
|
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->mem[t].size)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ start = lm->mem[t].base;
|
||
|
+ end = start + lm->mem[t].size;
|
||
|
+
|
||
|
+ if (vaddr >= end)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (vaddr < start) {
|
||
|
+ if (start < min) /* replace candidate */
|
||
|
+ min = start;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ *nextvaddr = vaddr;
|
||
|
+ return TRUE;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (min != (ulong)-1) {
|
||
|
+ *nextvaddr = min;
|
||
|
+ return TRUE;
|
||
|
+ }
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+old_module:
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
lm = &st->load_modules[i];
|
||
|
start = lm->mod_base;
|
||
|
diff --git a/symbols.c b/symbols.c
|
||
|
index 7b1d59203b90..f161ee99e90a 100644
|
||
|
--- a/symbols.c
|
||
|
+++ b/symbols.c
|
||
|
@@ -48,8 +48,8 @@ static int load_module_index(struct syment *);
|
||
|
static void section_header_info(bfd *, asection *, void *);
|
||
|
static void store_section_data(struct load_module *, bfd *, asection *);
|
||
|
static void calculate_load_order_v1(struct load_module *, bfd *);
|
||
|
-static void calculate_load_order_v2(struct load_module *, bfd *, int,
|
||
|
- void *, long, unsigned int);
|
||
|
+static void calculate_load_order_v2(struct load_module *, bfd *, int, void *, long, unsigned int);
|
||
|
+static void calculate_load_order_6_4(struct load_module *, bfd *, int, void *, long, unsigned int);
|
||
|
static void check_insmod_builtin(struct load_module *, int, ulong *);
|
||
|
static int is_insmod_builtin(struct load_module *, struct syment *);
|
||
|
struct load_module;
|
||
|
@@ -104,6 +104,42 @@ static unsigned char is_right_brace(const char *);
|
||
|
static struct struct_elem *find_node(struct struct_elem *, char *);
|
||
|
static void dump_node(struct struct_elem *, char *, unsigned char, unsigned char);
|
||
|
|
||
|
+static int module_mem_type(ulong, struct load_module *);
|
||
|
+static ulong module_mem_end(ulong, struct load_module *);
|
||
|
+static int in_module_range(ulong, struct load_module *, int, int);
|
||
|
+static struct syment *value_search_module_6_4(ulong, ulong *);
|
||
|
+static struct syment *next_symbol_by_symname(char *);
|
||
|
+static struct syment *prev_symbol_by_symname(char *);
|
||
|
+static struct syment *next_module_symbol_by_value(ulong);
|
||
|
+static struct syment *prev_module_symbol_by_value(ulong);
|
||
|
+static struct syment *next_module_symbol_by_syment(struct syment *);
|
||
|
+static struct syment *prev_module_symbol_by_syment(struct syment *);
|
||
|
+
|
||
|
+struct module_tag {
|
||
|
+ char *start;
|
||
|
+ char *end;
|
||
|
+ char *start_str;
|
||
|
+ char *end_str;
|
||
|
+};
|
||
|
+
|
||
|
+#define MODULE_TAG(type, suffix) ("_MODULE_" #type "_" #suffix "_")
|
||
|
+#define MODULE_STR(type, suffix) ( "MODULE " #type " " #suffix)
|
||
|
+#define MODULE_TAGS(type) { \
|
||
|
+ .start = MODULE_TAG(type, START), \
|
||
|
+ .end = MODULE_TAG(type, END), \
|
||
|
+ .start_str = MODULE_STR(type, START), \
|
||
|
+ .end_str = MODULE_STR(type, END) \
|
||
|
+}
|
||
|
+
|
||
|
+static const struct module_tag module_tag[] = {
|
||
|
+ MODULE_TAGS(TEXT),
|
||
|
+ MODULE_TAGS(DATA),
|
||
|
+ MODULE_TAGS(RODATA),
|
||
|
+ MODULE_TAGS(RO_AFTER_INIT),
|
||
|
+ MODULE_TAGS(INIT_TEXT),
|
||
|
+ MODULE_TAGS(INIT_DATA),
|
||
|
+ MODULE_TAGS(INIT_RODATA),
|
||
|
+};
|
||
|
|
||
|
/*
|
||
|
* structure/union printing stuff
|
||
|
@@ -1268,10 +1304,7 @@ symname_hash_search(struct syment *table[], char *name)
|
||
|
* Output for sym -[lL] command.
|
||
|
*/
|
||
|
|
||
|
-#define MODULE_PSEUDO_SYMBOL(sp) \
|
||
|
- ((STRNEQ((sp)->name, "_MODULE_START_") || STRNEQ((sp)->name, "_MODULE_END_")) || \
|
||
|
- (STRNEQ((sp)->name, "_MODULE_INIT_START_") || STRNEQ((sp)->name, "_MODULE_INIT_END_")) || \
|
||
|
- (STRNEQ((sp)->name, "_MODULE_SECTION_")))
|
||
|
+#define MODULE_PSEUDO_SYMBOL(sp) (STRNEQ((sp)->name, "_MODULE_"))
|
||
|
|
||
|
#define MODULE_START(sp) (STRNEQ((sp)->name, "_MODULE_START_"))
|
||
|
#define MODULE_END(sp) (STRNEQ((sp)->name, "_MODULE_END_"))
|
||
|
@@ -1280,6 +1313,76 @@ symname_hash_search(struct syment *table[], char *name)
|
||
|
#define MODULE_SECTION_START(sp) (STRNEQ((sp)->name, "_MODULE_SECTION_START"))
|
||
|
#define MODULE_SECTION_END(sp) (STRNEQ((sp)->name, "_MODULE_SECTION_END"))
|
||
|
|
||
|
+#define MODULE_MEM_START(sp,t) (STRNEQ((sp)->name, module_tag[t].start))
|
||
|
+#define MODULE_MEM_END(sp,t) (STRNEQ((sp)->name, module_tag[t].end))
|
||
|
+
|
||
|
+/* For 6.4 and later */
|
||
|
+static void
|
||
|
+module_symbol_dump(char *module)
|
||
|
+{
|
||
|
+ int i, t;
|
||
|
+ struct syment *sp, *sp_end;
|
||
|
+ struct load_module *lm;
|
||
|
+ const char *p1, *p2;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+ if (module && !STREQ(module, lm->mod_name))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (received_SIGINT() || output_closed())
|
||
|
+ return;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * module percpu symbols are within the .data..percpu section,
|
||
|
+ * not in any module memory regions.
|
||
|
+ */
|
||
|
+ if (MODULE_PERCPU_SYMS_LOADED(lm)) {
|
||
|
+ p1 = "MODULE PERCPU START";
|
||
|
+ p2 = lm->mod_name;
|
||
|
+ fprintf(fp, "%lx %s: %s\n", lm->mod_percpu, p1, p2);
|
||
|
+
|
||
|
+ dump_percpu_symbols(lm);
|
||
|
+
|
||
|
+ p1 = "MODULE PERCPU END";
|
||
|
+ fprintf(fp, "%lx %s: %s\n", lm->mod_percpu + lm->mod_percpu_size, p1, p2);
|
||
|
+ }
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+
|
||
|
+ for ( ; sp <= sp_end; sp++) {
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp)) {
|
||
|
+ if (MODULE_MEM_START(sp, t)) {
|
||
|
+ p1 = module_tag[t].start_str;
|
||
|
+ p2 = sp->name + strlen(module_tag[t].start);
|
||
|
+ } else if (MODULE_MEM_END(sp, t)) {
|
||
|
+ p1 = module_tag[t].end_str;
|
||
|
+ p2 = sp->name + strlen(module_tag[t].end);
|
||
|
+ } else if (MODULE_SECTION_START(sp)) {
|
||
|
+ p1 = sp->name + strlen("_MODULE_SECTION_START ");
|
||
|
+ p2 = "section start";
|
||
|
+ } else if (MODULE_SECTION_END(sp)) {
|
||
|
+ p1 = sp->name + strlen("_MODULE_SECTION_END ");
|
||
|
+ p2 = "section end";
|
||
|
+ } else {
|
||
|
+ p1 = "unknown tag";
|
||
|
+ p2 = sp->name;
|
||
|
+ }
|
||
|
+
|
||
|
+ fprintf(fp, "%lx %s: %s\n", sp->value, p1, p2);
|
||
|
+ } else
|
||
|
+ show_symbol(sp, 0, SHOW_RADIX());
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
static void
|
||
|
symbol_dump(ulong flags, char *module)
|
||
|
{
|
||
|
@@ -1302,6 +1405,11 @@ symbol_dump(ulong flags, char *module)
|
||
|
if (!(flags & MODULE_SYMS))
|
||
|
return;
|
||
|
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ module_symbol_dump(module);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
|
||
|
lm = &st->load_modules[i];
|
||
|
@@ -1389,8 +1497,14 @@ dump_percpu_symbols(struct load_module *lm)
|
||
|
struct syment *sp, *sp_end;
|
||
|
|
||
|
if (MODULE_PERCPU_SYMS_LOADED(lm)) {
|
||
|
- sp = lm->mod_symtable;
|
||
|
- sp_end = lm->mod_symend;
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ /* The lm should have mod_load_symtable. */
|
||
|
+ sp = lm->mod_load_symtable;
|
||
|
+ sp_end = lm->mod_load_symend;
|
||
|
+ } else {
|
||
|
+ sp = lm->mod_symtable;
|
||
|
+ sp_end = lm->mod_symend;
|
||
|
+ }
|
||
|
for ( ; sp <= sp_end; sp++) {
|
||
|
if (IN_MODULE_PERCPU(sp->value, lm))
|
||
|
show_symbol(sp, 0, SHOW_RADIX());
|
||
|
@@ -1425,8 +1539,13 @@ check_for_dups(struct load_module *lm)
|
||
|
{
|
||
|
struct syment *sp, *sp_end;
|
||
|
|
||
|
- sp = lm->mod_symtable;
|
||
|
- sp_end = lm->mod_symend;
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ sp = lm->mod_load_symtable;
|
||
|
+ sp_end = lm->mod_load_symend;
|
||
|
+ } else {
|
||
|
+ sp = lm->mod_symtable;
|
||
|
+ sp_end = lm->mod_symend;
|
||
|
+ }
|
||
|
|
||
|
for ( ; sp <= sp_end; sp++) {
|
||
|
if (symbol_name_count(sp->name) > 1)
|
||
|
@@ -1788,6 +1907,362 @@ modsym_value(ulong syms, union kernel_symbol *modsym, int i)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * Linux 6.4 introduced module.mem memory layout
|
||
|
+ */
|
||
|
+void
|
||
|
+store_module_symbols_6_4(ulong total, int mods_installed)
|
||
|
+{
|
||
|
+ int i, m, t;
|
||
|
+ ulong mod, mod_next;
|
||
|
+ char *mod_name;
|
||
|
+ uint nsyms, ngplsyms;
|
||
|
+ ulong syms, gpl_syms;
|
||
|
+ ulong nksyms;
|
||
|
+ long strbuflen;
|
||
|
+ ulong size;
|
||
|
+ int mcnt, lm_mcnt;
|
||
|
+ union kernel_symbol *modsym;
|
||
|
+ size_t kernel_symbol_size;
|
||
|
+ struct load_module *lm;
|
||
|
+ char buf1[BUFSIZE];
|
||
|
+ char buf2[BUFSIZE];
|
||
|
+ char *strbuf = NULL, *modbuf, *modsymbuf;
|
||
|
+ struct syment *sp;
|
||
|
+ ulong first, last;
|
||
|
+
|
||
|
+ st->mods_installed = mods_installed;
|
||
|
+
|
||
|
+ if (!st->mods_installed) {
|
||
|
+ st->flags &= ~MODULE_SYMS;
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * If we've been here before, free up everything and start over.
|
||
|
+ */
|
||
|
+ if (st->flags & MODULE_SYMS)
|
||
|
+ error(FATAL, "re-initialization of module symbols not implemented yet!\n");
|
||
|
+
|
||
|
+ kernel_symbol_size = kernel_symbol_type_init();
|
||
|
+
|
||
|
+ if ((st->ext_module_symtable = (struct syment *)
|
||
|
+ calloc(total, sizeof(struct syment))) == NULL)
|
||
|
+ error(FATAL, "module syment space malloc (%ld symbols): %s\n",
|
||
|
+ total, strerror(errno));
|
||
|
+
|
||
|
+ if (!namespace_ctl(NAMESPACE_INIT, &st->ext_module_namespace,
|
||
|
+ (void *)total, NULL))
|
||
|
+ error(FATAL, "module namespace malloc: %s\n", strerror(errno));
|
||
|
+
|
||
|
+ if ((st->load_modules = (struct load_module *)calloc
|
||
|
+ (st->mods_installed, sizeof(struct load_module))) == NULL)
|
||
|
+ error(FATAL, "load_module array malloc: %s\n", strerror(errno));
|
||
|
+
|
||
|
+ modbuf = GETBUF(SIZE(module));
|
||
|
+ modsymbuf = NULL;
|
||
|
+ m = mcnt = mod_next = 0;
|
||
|
+
|
||
|
+ for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) {
|
||
|
+
|
||
|
+ readmem(mod, KVADDR, modbuf, SIZE(module),
|
||
|
+ "module buffer", FAULT_ON_ERROR);
|
||
|
+
|
||
|
+ syms = ULONG(modbuf + OFFSET(module_syms));
|
||
|
+ gpl_syms = ULONG(modbuf + OFFSET(module_gpl_syms));
|
||
|
+ nsyms = UINT(modbuf + OFFSET(module_num_syms));
|
||
|
+ ngplsyms = UINT(modbuf + OFFSET(module_num_gpl_syms));
|
||
|
+
|
||
|
+ nksyms = UINT(modbuf + OFFSET(module_num_symtab));
|
||
|
+
|
||
|
+ mod_name = modbuf + OFFSET(module_name);
|
||
|
+
|
||
|
+ lm = &st->load_modules[m++];
|
||
|
+ BZERO(lm, sizeof(struct load_module));
|
||
|
+
|
||
|
+ size = 0;
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ lm->mem[t].base = ULONG(modbuf + OFFSET(module_mem) +
|
||
|
+ SIZE(module_memory) * t + OFFSET(module_memory_base));
|
||
|
+ lm->mem[t].size = UINT(modbuf + OFFSET(module_mem) +
|
||
|
+ SIZE(module_memory) * t + OFFSET(module_memory_size));
|
||
|
+ if (t < MOD_INIT_TEXT)
|
||
|
+ size += lm->mem[t].size;
|
||
|
+ }
|
||
|
+ lm->mod_base = lm->mem[MOD_TEXT].base;
|
||
|
+ /* module core size, init not included */
|
||
|
+ lm->mod_size = size;
|
||
|
+ lm->module_struct = mod;
|
||
|
+
|
||
|
+ if (strlen(mod_name) < MAX_MOD_NAME)
|
||
|
+ strcpy(lm->mod_name, mod_name);
|
||
|
+ else {
|
||
|
+ error(INFO, "module name greater than MAX_MOD_NAME: %s\n", mod_name);
|
||
|
+ strncpy(lm->mod_name, mod_name, MAX_MOD_NAME-1);
|
||
|
+ }
|
||
|
+ if (CRASHDEBUG(3))
|
||
|
+ fprintf(fp, "%lx (%lx): %s syms: %d gplsyms: %d ksyms: %ld\n",
|
||
|
+ mod, lm->mod_base, lm->mod_name, nsyms, ngplsyms, nksyms);
|
||
|
+
|
||
|
+ lm->mod_flags = MOD_EXT_SYMS;
|
||
|
+ lm->mod_ext_symcnt = mcnt;
|
||
|
+ lm->mod_text_start = lm->mod_base;
|
||
|
+ lm->mod_init_module_ptr = lm->mem[MOD_INIT_TEXT].base;
|
||
|
+ lm->mod_init_size = lm->mem[MOD_INIT_TEXT].size;
|
||
|
+ lm->mod_init_text_size = lm->mem[MOD_INIT_TEXT].size;
|
||
|
+
|
||
|
+ if (VALID_MEMBER(module_percpu))
|
||
|
+ lm->mod_percpu = ULONG(modbuf + OFFSET(module_percpu));
|
||
|
+
|
||
|
+ lm_mcnt = mcnt;
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->mem[t].size)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ st->ext_module_symtable[mcnt].value = lm->mem[t].base;
|
||
|
+ st->ext_module_symtable[mcnt].type = 'm';
|
||
|
+ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
||
|
+ sprintf(buf2, "%s%s", module_tag[t].start, mod_name);
|
||
|
+ namespace_ctl(NAMESPACE_INSTALL, &st->ext_module_namespace,
|
||
|
+ &st->ext_module_symtable[mcnt], buf2);
|
||
|
+ lm_mcnt = mcnt;
|
||
|
+ mcnt++;
|
||
|
+
|
||
|
+ if (t >= MOD_INIT_TEXT)
|
||
|
+ lm->mod_flags |= MOD_INIT;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (nsyms && !IN_MODULE(syms, lm)) {
|
||
|
+ error(WARNING,
|
||
|
+ "[%s] module.syms outside of module " "address space (%lx)\n\n",
|
||
|
+ lm->mod_name, syms);
|
||
|
+ nsyms = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (nsyms) {
|
||
|
+ modsymbuf = GETBUF(kernel_symbol_size*nsyms);
|
||
|
+ readmem((ulong)syms, KVADDR, modsymbuf,
|
||
|
+ nsyms * kernel_symbol_size,
|
||
|
+ "module symbols", FAULT_ON_ERROR);
|
||
|
+ }
|
||
|
+
|
||
|
+ for (i = first = last = 0; i < nsyms; i++) {
|
||
|
+ modsym = (union kernel_symbol *)
|
||
|
+ (modsymbuf + (i * kernel_symbol_size));
|
||
|
+ if (!first
|
||
|
+ || first > modsym_name(syms, modsym, i))
|
||
|
+ first = modsym_name(syms, modsym, i);
|
||
|
+ if (modsym_name(syms, modsym, i) > last)
|
||
|
+ last = modsym_name(syms, modsym, i);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (last > first) {
|
||
|
+ /* The buffer should not go over the block. */
|
||
|
+ ulong end = module_mem_end(first, lm);
|
||
|
+
|
||
|
+ strbuflen = (last-first) + BUFSIZE;
|
||
|
+ if ((first + strbuflen) >= end) {
|
||
|
+ strbuflen = end - first;
|
||
|
+
|
||
|
+ }
|
||
|
+ strbuf = GETBUF(strbuflen);
|
||
|
+
|
||
|
+ if (!readmem(first, KVADDR, strbuf, strbuflen,
|
||
|
+ "module symbol strings", RETURN_ON_ERROR)) {
|
||
|
+ FREEBUF(strbuf);
|
||
|
+ strbuf = NULL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+ for (i = 0; i < nsyms; i++) {
|
||
|
+ modsym = (union kernel_symbol *)(modsymbuf + (i * kernel_symbol_size));
|
||
|
+
|
||
|
+ BZERO(buf1, BUFSIZE);
|
||
|
+
|
||
|
+ if (strbuf)
|
||
|
+ strcpy(buf1, &strbuf[modsym_name(syms, modsym, i) - first]);
|
||
|
+ else
|
||
|
+ read_string(modsym_name(syms, modsym, i), buf1, BUFSIZE-1);
|
||
|
+
|
||
|
+ if (strlen(buf1)) {
|
||
|
+ st->ext_module_symtable[mcnt].value =
|
||
|
+ modsym_value(syms, modsym, i);
|
||
|
+ st->ext_module_symtable[mcnt].type = '?';
|
||
|
+ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
||
|
+ strip_module_symbol_end(buf1);
|
||
|
+ strip_symbol_end(buf1, NULL);
|
||
|
+ namespace_ctl(NAMESPACE_INSTALL,
|
||
|
+ &st->ext_module_namespace,
|
||
|
+ &st->ext_module_symtable[mcnt], buf1);
|
||
|
+
|
||
|
+ mcnt++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (modsymbuf) {
|
||
|
+ FREEBUF(modsymbuf);
|
||
|
+ modsymbuf = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (strbuf)
|
||
|
+ FREEBUF(strbuf);
|
||
|
+
|
||
|
+ if (ngplsyms) {
|
||
|
+ modsymbuf = GETBUF(kernel_symbol_size * ngplsyms);
|
||
|
+ readmem((ulong)gpl_syms, KVADDR, modsymbuf,
|
||
|
+ ngplsyms * kernel_symbol_size,
|
||
|
+ "module gpl symbols", FAULT_ON_ERROR);
|
||
|
+ }
|
||
|
+
|
||
|
+ for (i = first = last = 0; i < ngplsyms; i++) {
|
||
|
+ modsym = (union kernel_symbol *)
|
||
|
+ (modsymbuf + (i * kernel_symbol_size));
|
||
|
+ if (!first
|
||
|
+ || first > modsym_name(gpl_syms, modsym, i))
|
||
|
+ first = modsym_name(gpl_syms, modsym, i);
|
||
|
+ if (modsym_name(gpl_syms, modsym, i) > last)
|
||
|
+ last = modsym_name(gpl_syms, modsym, i);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (last > first) {
|
||
|
+ ulong end = module_mem_end(first, lm);
|
||
|
+
|
||
|
+ strbuflen = (last-first) + BUFSIZE;
|
||
|
+ if ((first + strbuflen) >= end) {
|
||
|
+ strbuflen = end - first;
|
||
|
+
|
||
|
+ }
|
||
|
+ strbuf = GETBUF(strbuflen);
|
||
|
+
|
||
|
+ if (!readmem(first, KVADDR, strbuf, strbuflen,
|
||
|
+ "module gpl symbol strings", RETURN_ON_ERROR)) {
|
||
|
+ FREEBUF(strbuf);
|
||
|
+ strbuf = NULL;
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ strbuf = NULL;
|
||
|
+
|
||
|
+ for (i = 0; i < ngplsyms; i++) {
|
||
|
+ modsym = (union kernel_symbol *) (modsymbuf + (i * kernel_symbol_size));
|
||
|
+
|
||
|
+ BZERO(buf1, BUFSIZE);
|
||
|
+
|
||
|
+ if (strbuf)
|
||
|
+ strcpy(buf1, &strbuf[modsym_name(gpl_syms, modsym, i) - first]);
|
||
|
+ else
|
||
|
+ read_string(modsym_name(gpl_syms, modsym, i), buf1, BUFSIZE-1);
|
||
|
+
|
||
|
+ if (strlen(buf1)) {
|
||
|
+ st->ext_module_symtable[mcnt].value =
|
||
|
+ modsym_value(gpl_syms, modsym, i);
|
||
|
+ st->ext_module_symtable[mcnt].type = '?';
|
||
|
+ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
||
|
+ strip_module_symbol_end(buf1);
|
||
|
+ strip_symbol_end(buf1, NULL);
|
||
|
+ namespace_ctl(NAMESPACE_INSTALL,
|
||
|
+ &st->ext_module_namespace,
|
||
|
+ &st->ext_module_symtable[mcnt], buf1);
|
||
|
+
|
||
|
+ mcnt++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (modsymbuf) {
|
||
|
+ FREEBUF(modsymbuf);
|
||
|
+ modsymbuf = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (strbuf)
|
||
|
+ FREEBUF(strbuf);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * If the module was compiled with kallsyms, add them in.
|
||
|
+ */
|
||
|
+ switch (kt->flags & (KALLSYMS_V1|KALLSYMS_V2))
|
||
|
+ {
|
||
|
+ case KALLSYMS_V1: /* impossible, I hope... */
|
||
|
+ mcnt += store_module_kallsyms_v1(lm, lm_mcnt, mcnt, modbuf);
|
||
|
+ break;
|
||
|
+ case KALLSYMS_V2:
|
||
|
+ mcnt += store_module_kallsyms_v2(lm, lm_mcnt, mcnt, modbuf);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->mem[t].size)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ st->ext_module_symtable[mcnt].value = lm->mem[t].base + lm->mem[t].size;
|
||
|
+ st->ext_module_symtable[mcnt].type = 'm';
|
||
|
+ st->ext_module_symtable[mcnt].flags |= MODULE_SYMBOL;
|
||
|
+ sprintf(buf2, "%s%s", module_tag[t].end, mod_name);
|
||
|
+ namespace_ctl(NAMESPACE_INSTALL,
|
||
|
+ &st->ext_module_namespace,
|
||
|
+ &st->ext_module_symtable[mcnt], buf2);
|
||
|
+ mcnt++;
|
||
|
+ }
|
||
|
+
|
||
|
+ lm->mod_ext_symcnt = mcnt - lm->mod_ext_symcnt;
|
||
|
+
|
||
|
+ NEXT_MODULE(mod_next, modbuf);
|
||
|
+ }
|
||
|
+
|
||
|
+ FREEBUF(modbuf);
|
||
|
+
|
||
|
+ st->ext_module_symcnt = mcnt;
|
||
|
+ st->ext_module_symend = &st->ext_module_symtable[mcnt];
|
||
|
+
|
||
|
+ namespace_ctl(NAMESPACE_COMPLETE, &st->ext_module_namespace,
|
||
|
+ st->ext_module_symtable, st->ext_module_symend);
|
||
|
+
|
||
|
+ qsort(st->ext_module_symtable, mcnt, sizeof(struct syment),
|
||
|
+ compare_syms);
|
||
|
+
|
||
|
+ /* sort by text base address */
|
||
|
+ qsort(st->load_modules, m, sizeof(struct load_module), compare_mods);
|
||
|
+
|
||
|
+ for (m = 0; m < st->mods_installed; m++) {
|
||
|
+ lm = &st->load_modules[m];
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->mem[t].size)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ sprintf(buf1, "%s%s", module_tag[t].start, lm->mod_name);
|
||
|
+ sprintf(buf2, "%s%s", module_tag[t].end, lm->mod_name);
|
||
|
+
|
||
|
+ for (sp = st->ext_module_symtable; sp < st->ext_module_symend; sp++) {
|
||
|
+ if (STREQ(sp->name, buf1)) {
|
||
|
+ lm->ext_symtable[t] = sp;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ for ( ; sp < st->ext_module_symend; sp++) {
|
||
|
+ if (STREQ(sp->name, buf2)) {
|
||
|
+ lm->ext_symend[t] = sp;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (lm->ext_symtable[t] && lm->ext_symend[t])
|
||
|
+ mod_symtable_hash_install_range(lm->ext_symtable[t], lm->ext_symend[t]);
|
||
|
+ }
|
||
|
+ lm->symtable = lm->ext_symtable;
|
||
|
+ lm->symend = lm->ext_symend;
|
||
|
+ }
|
||
|
+
|
||
|
+ st->flags |= MODULE_SYMS;
|
||
|
+
|
||
|
+ if (CRASHDEBUG(2)) {
|
||
|
+ for (sp = st->ext_module_symtable; sp < st->ext_module_symend; sp++)
|
||
|
+ fprintf(fp, "%16lx %s\n", sp->value, sp->name);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (mcnt > total)
|
||
|
+ error(FATAL, "store_module_symbols_6_4: total: %ld mcnt: %d\n", total, mcnt);
|
||
|
+}
|
||
|
+
|
||
|
void
|
||
|
store_module_symbols_v2(ulong total, int mods_installed)
|
||
|
{
|
||
|
@@ -2384,6 +2859,7 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
|
||
|
int mcnt;
|
||
|
int mcnt_idx;
|
||
|
char *module_buf_init = NULL;
|
||
|
+ ulong base, base_init, size, size_init;
|
||
|
|
||
|
if (!(kt->flags & KALLSYMS_V2))
|
||
|
return 0;
|
||
|
@@ -2394,9 +2870,22 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
|
||
|
ns = &st->ext_module_namespace;
|
||
|
ec = &elf_common;
|
||
|
|
||
|
- module_buf = GETBUF(lm->mod_size);
|
||
|
+ /* kallsyms data looks to be in MOD_DATA region. */
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ base = lm->mem[MOD_DATA].base;
|
||
|
+ size = lm->mem[MOD_DATA].size;
|
||
|
+ base_init = lm->mem[MOD_INIT_DATA].base;
|
||
|
+ size_init = lm->mem[MOD_INIT_DATA].size;
|
||
|
+ } else {
|
||
|
+ base = lm->mod_base;
|
||
|
+ size = lm->mod_size;
|
||
|
+ base_init = lm->mod_init_module_ptr;
|
||
|
+ size_init = lm->mod_init_size;
|
||
|
+ }
|
||
|
|
||
|
- if (!readmem(lm->mod_base, KVADDR, module_buf, lm->mod_size,
|
||
|
+ module_buf = GETBUF(size);
|
||
|
+
|
||
|
+ if (!readmem(base, KVADDR, module_buf, size,
|
||
|
"module (kallsyms)", RETURN_ON_ERROR|QUIET)) {
|
||
|
error(WARNING,"cannot access module kallsyms\n");
|
||
|
FREEBUF(module_buf);
|
||
|
@@ -2404,10 +2893,10 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
|
||
|
}
|
||
|
|
||
|
if (lm->mod_init_size > 0) {
|
||
|
- module_buf_init = GETBUF(lm->mod_init_size);
|
||
|
+ module_buf_init = GETBUF(size_init);
|
||
|
|
||
|
- if (!readmem(lm->mod_init_module_ptr, KVADDR, module_buf_init, lm->mod_init_size,
|
||
|
- "module init (kallsyms)", RETURN_ON_ERROR|QUIET)) {
|
||
|
+ if (!readmem(base_init, KVADDR, module_buf_init, size_init,
|
||
|
+ "module init (kallsyms)", RETURN_ON_ERROR|QUIET)) {
|
||
|
error(WARNING,"cannot access module init kallsyms\n");
|
||
|
FREEBUF(module_buf_init);
|
||
|
}
|
||
|
@@ -2429,9 +2918,9 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
|
||
|
return 0;
|
||
|
}
|
||
|
if (IN_MODULE(ksymtab, lm))
|
||
|
- locsymtab = module_buf + (ksymtab - lm->mod_base);
|
||
|
+ locsymtab = module_buf + (ksymtab - base);
|
||
|
else
|
||
|
- locsymtab = module_buf_init + (ksymtab - lm->mod_init_module_ptr);
|
||
|
+ locsymtab = module_buf_init + (ksymtab - base_init);
|
||
|
|
||
|
kstrtab = ULONG(modbuf + OFFSET(module_strtab));
|
||
|
if (!IN_MODULE(kstrtab, lm) && !IN_MODULE_INIT(kstrtab, lm)) {
|
||
|
@@ -2444,9 +2933,9 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
|
||
|
return 0;
|
||
|
}
|
||
|
if (IN_MODULE(kstrtab, lm))
|
||
|
- locstrtab = module_buf + (kstrtab - lm->mod_base);
|
||
|
+ locstrtab = module_buf + (kstrtab - base);
|
||
|
else
|
||
|
- locstrtab = module_buf_init + (kstrtab - lm->mod_init_module_ptr);
|
||
|
+ locstrtab = module_buf_init + (kstrtab - base_init);
|
||
|
|
||
|
for (i = 1; i < nksyms; i++) { /* ELF starts real symbols at 1 */
|
||
|
switch (BITS())
|
||
|
@@ -2461,11 +2950,8 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
- if (((ec->st_value < lm->mod_base) ||
|
||
|
- (ec->st_value > (lm->mod_base + lm->mod_size))) &&
|
||
|
- ((ec->st_value < lm->mod_init_module_ptr) ||
|
||
|
- (ec->st_value > (lm->mod_init_module_ptr + lm->mod_init_size))))
|
||
|
- continue;
|
||
|
+ if (!IN_MODULE(ec->st_value, lm) && !IN_MODULE_INIT(ec->st_value, lm))
|
||
|
+ continue;
|
||
|
|
||
|
if (ec->st_shndx == SHN_UNDEF)
|
||
|
continue;
|
||
|
@@ -2572,7 +3058,7 @@ strip_module_symbol_end(char *buf)
|
||
|
ulong
|
||
|
lowest_module_address(void)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
struct load_module *lm;
|
||
|
ulong low, lowest;
|
||
|
|
||
|
@@ -2582,9 +3068,20 @@ lowest_module_address(void)
|
||
|
lowest = (ulong)(-1);
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
lm = &st->load_modules[i];
|
||
|
- low = lm->mod_base;
|
||
|
- if (low < lowest)
|
||
|
- lowest = low;
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->mem[t].size)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ low = lm->mem[t].base;
|
||
|
+ if (low < lowest)
|
||
|
+ lowest = low;
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ low = lm->mod_base;
|
||
|
+ if (low < lowest)
|
||
|
+ lowest = low;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
return lowest;
|
||
|
@@ -2593,16 +3090,27 @@ lowest_module_address(void)
|
||
|
ulong
|
||
|
highest_module_address(void)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
struct load_module *lm;
|
||
|
ulong high, highest;
|
||
|
|
||
|
highest = 0;
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
lm = &st->load_modules[i];
|
||
|
- high = lm->mod_base + lm->mod_size;
|
||
|
- if (high > highest)
|
||
|
- highest = high;
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->mem[t].size)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ high = lm->mem[t].base + lm->mem[t].size;
|
||
|
+ if (high > highest)
|
||
|
+ highest = high;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ high = lm->mod_base + lm->mod_size;
|
||
|
+ if (high > highest)
|
||
|
+ highest = high;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
return highest;
|
||
|
@@ -2853,7 +3361,8 @@ compare_syms(const void *v1, const void *v2)
|
||
|
return -1;
|
||
|
if (STRNEQ(s2->name, "__insmod"))
|
||
|
return 1;
|
||
|
- if (STRNEQ(s2->name, "_MODULE_START_"))
|
||
|
+ if (MODULE_MEM_START(s2, MOD_TEXT) ||
|
||
|
+ STRNEQ(s2->name, "_MODULE_START_"))
|
||
|
return 1;
|
||
|
/* Get pseudo section name. */
|
||
|
if (MODULE_SECTION_START(s1))
|
||
|
@@ -2986,13 +3495,19 @@ is_kernel_text(ulong value)
|
||
|
if (!(lm->mod_section_data[s].flags & SEC_CODE))
|
||
|
continue;
|
||
|
|
||
|
- start = lm->mod_base +
|
||
|
- lm->mod_section_data[s].offset;
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ start = lm->mod_section_data[s].addr;
|
||
|
+ else
|
||
|
+ start = lm->mod_base + lm->mod_section_data[s].offset;
|
||
|
+
|
||
|
end = start + lm->mod_section_data[s].size;
|
||
|
|
||
|
if ((value >= start) && (value < end))
|
||
|
return TRUE;
|
||
|
}
|
||
|
+ } else if (MODULE_MEMORY()) {
|
||
|
+ if (IN_MODULE_TEXT(value, lm))
|
||
|
+ return TRUE;
|
||
|
} else {
|
||
|
switch (kt->flags & (KMOD_V1|KMOD_V2))
|
||
|
{
|
||
|
@@ -3531,22 +4046,42 @@ dump_symbol_table(void)
|
||
|
(ulong)lm->mod_section_data,
|
||
|
lm->mod_section_data ? "" : "(not allocated)");
|
||
|
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ int t;
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ fprintf(fp, " mem[%d]: %lx (%x)\n",
|
||
|
+ t, lm->mem[t].base, lm->mem[t].size);
|
||
|
+ }
|
||
|
+ fprintf(fp, " symtable: %lx\n", (ulong)lm->symtable);
|
||
|
+ fprintf(fp, " ext_symtable: %lx\n", (ulong)lm->ext_symtable);
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ fprintf(fp, " ext_symtable[%d]: %lx - %lx\n",
|
||
|
+ t, (ulong)lm->ext_symtable[t], (ulong)lm->ext_symend[t]);
|
||
|
+ }
|
||
|
+ fprintf(fp, " load_symtable: %lx\n", (ulong)lm->load_symtable);
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ fprintf(fp, " load_symtable[%d]: %lx - %lx\n",
|
||
|
+ t, (ulong)lm->load_symtable[t], (ulong)lm->load_symend[t]);
|
||
|
+ }
|
||
|
+ }
|
||
|
|
||
|
for (s = 0; s < lm->mod_sections; s++) {
|
||
|
fprintf(fp,
|
||
|
- " %12s prio: %x flags: %05x offset: %-8lx size: %lx\n",
|
||
|
+ " %20s prio: %x flags: %08x %s: %-16lx size: %lx\n",
|
||
|
lm->mod_section_data[s].name,
|
||
|
lm->mod_section_data[s].priority,
|
||
|
lm->mod_section_data[s].flags,
|
||
|
- lm->mod_section_data[s].offset,
|
||
|
+ MODULE_MEMORY() ? "addr" : "offset",
|
||
|
+ MODULE_MEMORY() ? lm->mod_section_data[s].addr :
|
||
|
+ lm->mod_section_data[s].offset,
|
||
|
lm->mod_section_data[s].size);
|
||
|
}
|
||
|
|
||
|
fprintf(fp, " loaded_objfile: %lx\n", (ulong)lm->loaded_objfile);
|
||
|
|
||
|
- if (CRASHDEBUG(1)) {
|
||
|
+ if (CRASHDEBUG(1) && lm->mod_load_symtable) {
|
||
|
for (sp = lm->mod_load_symtable;
|
||
|
- sp < lm->mod_load_symend; sp++) {
|
||
|
+ sp <= lm->mod_load_symend; sp++) {
|
||
|
fprintf(fp, " %lx %s\n",
|
||
|
sp->value, sp->name);
|
||
|
}
|
||
|
@@ -4458,8 +4993,11 @@ get_section(ulong vaddr, char *buf)
|
||
|
if (module_symbol(vaddr, NULL, &lm, NULL, *gdb_output_radix)) {
|
||
|
if (lm->mod_flags & MOD_LOAD_SYMS) {
|
||
|
for (i = (lm->mod_sections-1); i >= 0; i--) {
|
||
|
- start = lm->mod_base +
|
||
|
- lm->mod_section_data[i].offset;
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ start = lm->mod_section_data[i].addr;
|
||
|
+ else
|
||
|
+ start = lm->mod_base +
|
||
|
+ lm->mod_section_data[i].offset;
|
||
|
end = start + lm->mod_section_data[i].size;
|
||
|
|
||
|
if ((vaddr >= start) && (vaddr < end)) {
|
||
|
@@ -4514,7 +5052,7 @@ get_build_directory(char *buf)
|
||
|
int
|
||
|
symbol_query(char *s, char *print_pad, struct syment **spp)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
struct syment *sp, *sp_end;
|
||
|
struct load_module *lm;
|
||
|
int cnt, search_init;
|
||
|
@@ -4534,6 +5072,60 @@ symbol_query(char *s, char *print_pad, struct syment **spp)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ if (lm->mod_flags & MOD_LOAD_SYMS) {
|
||
|
+ sp = lm->mod_load_symtable;
|
||
|
+ sp_end = lm->mod_load_symend;
|
||
|
+
|
||
|
+ for (; sp < sp_end; sp++) {
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (strstr(sp->name, s)) {
|
||
|
+ if (print_pad) {
|
||
|
+ if (strlen(print_pad))
|
||
|
+ fprintf(fp, "%s", print_pad);
|
||
|
+ show_symbol(sp, 0, SHOW_RADIX()|SHOW_MODULE);
|
||
|
+ }
|
||
|
+ if (spp)
|
||
|
+ *spp = sp;
|
||
|
+ cnt++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+
|
||
|
+ for (; sp < sp_end; sp++) {
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (strstr(sp->name, s)) {
|
||
|
+ if (print_pad) {
|
||
|
+ if (strlen(print_pad))
|
||
|
+ fprintf(fp, "%s", print_pad);
|
||
|
+ show_symbol(sp, 0, SHOW_RADIX()|SHOW_MODULE);
|
||
|
+ }
|
||
|
+ if (spp)
|
||
|
+ *spp = sp;
|
||
|
+ cnt++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return cnt;
|
||
|
+
|
||
|
+old_module:
|
||
|
search_init = FALSE;
|
||
|
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
@@ -4639,7 +5231,7 @@ symbol_search(char *s)
|
||
|
int
|
||
|
symbol_name_count(char *s)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
struct syment *sp, *sp_end;
|
||
|
struct load_module *lm;
|
||
|
int count, pseudos, search_init;
|
||
|
@@ -4653,6 +5245,37 @@ symbol_name_count(char *s)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ if (lm->mod_flags & MOD_LOAD_SYMS) {
|
||
|
+ sp = lm->mod_load_symtable;
|
||
|
+ sp_end = lm->mod_load_symend;
|
||
|
+
|
||
|
+ for (; sp < sp_end; sp++) {
|
||
|
+ if (STREQ(s, sp->name))
|
||
|
+ count++;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+ for (; sp < sp_end; sp++) {
|
||
|
+ if (STREQ(s, sp->name))
|
||
|
+ count++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return count++;
|
||
|
+
|
||
|
+old_module:
|
||
|
pseudos = (strstr(s, "_MODULE_START_") || strstr(s, "_MODULE_END_"));
|
||
|
search_init = FALSE;
|
||
|
|
||
|
@@ -4702,7 +5325,7 @@ symbol_name_count(char *s)
|
||
|
struct syment *
|
||
|
symbol_search_next(char *s, struct syment *spstart)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
struct syment *sp, *sp_end;
|
||
|
struct load_module *lm;
|
||
|
int found_start;
|
||
|
@@ -4722,6 +5345,38 @@ symbol_search_next(char *s, struct syment *spstart)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+
|
||
|
+ if (!found_start && (spstart < sp || spstart > sp_end))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ for ( ; sp < sp_end; sp++) {
|
||
|
+ if (sp == spstart) {
|
||
|
+ found_start = TRUE;
|
||
|
+ continue;
|
||
|
+ } else if (!found_start)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (STREQ(s, sp->name))
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+old_module:
|
||
|
pseudos = (strstr(s, "_MODULE_START_") || strstr(s, "_MODULE_END_"));
|
||
|
search_init = FALSE;
|
||
|
|
||
|
@@ -4831,17 +5486,29 @@ module_symbol(ulong value,
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
lm = &st->load_modules[i];
|
||
|
|
||
|
- if (IN_MODULE(value, lm)) {
|
||
|
- base = lm->mod_base;
|
||
|
- end = lm->mod_base + lm->mod_size;
|
||
|
- } else if (IN_MODULE_INIT(value, lm)) {
|
||
|
- base = lm->mod_init_module_ptr;
|
||
|
- end = lm->mod_init_module_ptr + lm->mod_init_size;
|
||
|
- } else if (IN_MODULE_PERCPU(value, lm)) {
|
||
|
- base = lm->mod_percpu;
|
||
|
- end = lm->mod_percpu + lm->mod_percpu_size;
|
||
|
- } else
|
||
|
- continue;
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ if (IN_MODULE(value, lm) || IN_MODULE_INIT(value, lm)) {
|
||
|
+ int type = module_mem_type(value, lm);
|
||
|
+ base = lm->mem[type].base;
|
||
|
+ end = base + lm->mem[type].size;
|
||
|
+ } else if (IN_MODULE_PERCPU(value, lm)) {
|
||
|
+ base = lm->mod_percpu;
|
||
|
+ end = lm->mod_percpu + lm->mod_percpu_size;
|
||
|
+ } else
|
||
|
+ continue;
|
||
|
+ } else {
|
||
|
+ if (IN_MODULE(value, lm)) {
|
||
|
+ base = lm->mod_base;
|
||
|
+ end = lm->mod_base + lm->mod_size;
|
||
|
+ } else if (IN_MODULE_INIT(value, lm)) {
|
||
|
+ base = lm->mod_init_module_ptr;
|
||
|
+ end = lm->mod_init_module_ptr + lm->mod_init_size;
|
||
|
+ } else if (IN_MODULE_PERCPU(value, lm)) {
|
||
|
+ base = lm->mod_percpu;
|
||
|
+ end = lm->mod_percpu + lm->mod_percpu_size;
|
||
|
+ } else
|
||
|
+ continue;
|
||
|
+ }
|
||
|
|
||
|
if ((value >= base) && (value < end)) {
|
||
|
if (lmp)
|
||
|
@@ -4877,6 +5544,71 @@ module_symbol(ulong value,
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
+static struct syment *
|
||
|
+value_search_module_6_4(ulong value, ulong *offset)
|
||
|
+{
|
||
|
+ int i, t;
|
||
|
+ struct syment *sp, *sp_end, *spnext, *splast;
|
||
|
+ struct load_module *lm;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ if (!IN_MODULE(value, lm) && !IN_MODULE_INIT(value, lm))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+
|
||
|
+ if (value < sp->value)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ splast = NULL;
|
||
|
+ for ( ; sp <= sp_end; sp++) {
|
||
|
+ if (machine_type("ARM64") &&
|
||
|
+ IN_MODULE_PERCPU(sp->value, lm) &&
|
||
|
+ !IN_MODULE_PERCPU(value, lm))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (value == sp->value) {
|
||
|
+ if (MODULE_MEM_END(sp, t))
|
||
|
+ break;
|
||
|
+
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp)) {
|
||
|
+ spnext = sp + 1;
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(spnext))
|
||
|
+ continue;
|
||
|
+ if (spnext->value == value)
|
||
|
+ sp = spnext;
|
||
|
+ }
|
||
|
+ if (sp->name[0] == '.') {
|
||
|
+ spnext = sp+1;
|
||
|
+ if (spnext->value == value)
|
||
|
+ sp = spnext;
|
||
|
+ }
|
||
|
+ if (offset)
|
||
|
+ *offset = 0;
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (sp->value > value) {
|
||
|
+ sp = splast ? splast : sp - 1;
|
||
|
+ if (offset)
|
||
|
+ *offset = value - sp->value;
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!MODULE_PSEUDO_SYMBOL(sp)) {
|
||
|
+ splast = sp;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
struct syment *
|
||
|
value_search_module(ulong value, ulong *offset)
|
||
|
{
|
||
|
@@ -4885,6 +5617,9 @@ value_search_module(ulong value, ulong *offset)
|
||
|
struct load_module *lm;
|
||
|
int search_init_sections, search_init;
|
||
|
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ return value_search_module_6_4(value, offset);
|
||
|
+
|
||
|
search_init = FALSE;
|
||
|
search_init_sections = 0;
|
||
|
|
||
|
@@ -5203,6 +5938,99 @@ closest_symbol_value(ulong value)
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
+/* Only for 6.4 and later */
|
||
|
+static struct syment *
|
||
|
+next_symbol_by_symname(char *symbol)
|
||
|
+{
|
||
|
+ struct syment *sp;
|
||
|
+
|
||
|
+ if ((sp = symbol_search(symbol))) {
|
||
|
+ sp++;
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp) && strstr(sp->name, "_END"))
|
||
|
+ return next_module_symbol_by_value(sp->value);
|
||
|
+
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+/* val_in should be a pseudo module end symbol. */
|
||
|
+static struct syment *
|
||
|
+next_module_symbol_by_value(ulong val_in)
|
||
|
+{
|
||
|
+ struct load_module *lm;
|
||
|
+ struct syment *sp, *sp_end;
|
||
|
+ ulong start, min;
|
||
|
+ int i, t;
|
||
|
+
|
||
|
+retry:
|
||
|
+ sp = sp_end = NULL;
|
||
|
+ min = (ulong)-1;
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ /* Search for the next lowest symtable. */
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ start = lm->symtable[t]->value;
|
||
|
+ if (start > val_in && start < min) {
|
||
|
+ min = start;
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!sp)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ for ( ; sp < sp_end; sp++) {
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp))
|
||
|
+ continue;
|
||
|
+ if (sp->value > val_in)
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Found a table that has only pseudo symbols. */
|
||
|
+ val_in = sp_end->value;
|
||
|
+ goto retry;
|
||
|
+}
|
||
|
+
|
||
|
+/* Only for 6.4 and later */
|
||
|
+static struct syment *
|
||
|
+next_module_symbol_by_syment(struct syment *sp_in)
|
||
|
+{
|
||
|
+ struct load_module *lm;
|
||
|
+ struct syment *sp;
|
||
|
+ int i, t;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (sp_in < lm->symtable[t] || sp_in > lm->symend[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (sp_in == lm->symend[t])
|
||
|
+ return next_module_symbol_by_value(sp_in->value);
|
||
|
+
|
||
|
+ sp = sp_in + 1;
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp))
|
||
|
+ return next_module_symbol_by_value(sp->value);
|
||
|
+
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
/*
|
||
|
* For a given symbol, return a pointer to the next (higher) symbol's syment.
|
||
|
* Either a symbol name or syment pointer may be passed as an argument.
|
||
|
@@ -5230,6 +6058,9 @@ next_symbol(char *symbol, struct syment *sp_in)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ return next_module_symbol_by_syment(sp_in);
|
||
|
+
|
||
|
search_init = FALSE;
|
||
|
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
@@ -5270,46 +6101,148 @@ next_symbol(char *symbol, struct syment *sp_in)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- return NULL;
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ return next_symbol_by_symname(symbol);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Deal with a few special cases...
|
||
|
+ */
|
||
|
+ if (strstr(symbol, " module)")) {
|
||
|
+ sprintf(buf, "_MODULE_START_");
|
||
|
+ strcat(buf, &symbol[1]);
|
||
|
+ p1 = strstr(buf, " module)");
|
||
|
+ *p1 = NULLCHAR;
|
||
|
+ symbol = buf;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (STREQ(symbol, "_end")) {
|
||
|
+ if (!st->mods_installed)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ lm = &st->load_modules[0];
|
||
|
+
|
||
|
+ return lm->mod_symtable;
|
||
|
+ }
|
||
|
+
|
||
|
+ if ((sp = symbol_search(symbol))) {
|
||
|
+ sp++;
|
||
|
+ if (MODULE_END(sp)) {
|
||
|
+ sp--;
|
||
|
+ i = load_module_index(sp);
|
||
|
+ if ((i+1) == st->mods_installed)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ lm = &st->load_modules[i+1];
|
||
|
+
|
||
|
+ sp = lm->mod_symtable;
|
||
|
+ }
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+/* Only for 6.4 and later */
|
||
|
+static struct syment *
|
||
|
+prev_symbol_by_symname(char *symbol)
|
||
|
+{
|
||
|
+ struct syment *sp;
|
||
|
+
|
||
|
+ if ((sp = symbol_search(symbol))) {
|
||
|
+ if (sp == st->symtable)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ if (module_symbol(sp->value, NULL, NULL, NULL, 0)) {
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp) && strstr(sp->name, "_START"))
|
||
|
+ return prev_module_symbol_by_value(sp->value);
|
||
|
+ else
|
||
|
+ sp--;
|
||
|
+ } else
|
||
|
+ sp--;
|
||
|
+
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+/* val_in should be a pseudo module start symbol. */
|
||
|
+static struct syment *
|
||
|
+prev_module_symbol_by_value(ulong val_in)
|
||
|
+{
|
||
|
+ struct load_module *lm;
|
||
|
+ struct syment *sp, *sp_end;
|
||
|
+ ulong end, max;
|
||
|
+ int i, t;
|
||
|
+
|
||
|
+retry:
|
||
|
+ sp = sp_end = NULL;
|
||
|
+ max = 0;
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ /* Search for the previous highest table. */
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ end = lm->symend[t]->value;
|
||
|
+ if (end < val_in && end > max) {
|
||
|
+ max = end;
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+ }
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
+ if (!sp)
|
||
|
+ return NULL;
|
||
|
|
||
|
- /*
|
||
|
- * Deal with a few special cases...
|
||
|
- */
|
||
|
- if (strstr(symbol, " module)")) {
|
||
|
- sprintf(buf, "_MODULE_START_");
|
||
|
- strcat(buf, &symbol[1]);
|
||
|
- p1 = strstr(buf, " module)");
|
||
|
- *p1 = NULLCHAR;
|
||
|
- symbol = buf;
|
||
|
+ for ( ; sp_end > sp; sp_end--) {
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp_end))
|
||
|
+ continue;
|
||
|
+ if (sp_end->value < val_in)
|
||
|
+ return sp_end;
|
||
|
}
|
||
|
|
||
|
- if (STREQ(symbol, "_end")) {
|
||
|
- if (!st->mods_installed)
|
||
|
- return NULL;
|
||
|
+ /* Found a table that has only pseudo symbols. */
|
||
|
+ val_in = sp->value;
|
||
|
+ goto retry;
|
||
|
+}
|
||
|
|
||
|
- lm = &st->load_modules[0];
|
||
|
+/* Only for 6.4 and later */
|
||
|
+static struct syment *
|
||
|
+prev_module_symbol_by_syment(struct syment *sp_in)
|
||
|
+{
|
||
|
+ struct load_module *lm;
|
||
|
+ struct syment *sp;
|
||
|
+ int i, t;
|
||
|
|
||
|
- return lm->mod_symtable;
|
||
|
- }
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
|
||
|
- if ((sp = symbol_search(symbol))) {
|
||
|
- sp++;
|
||
|
- if (MODULE_END(sp)) {
|
||
|
- sp--;
|
||
|
- i = load_module_index(sp);
|
||
|
- if ((i+1) == st->mods_installed)
|
||
|
- return NULL;
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
|
||
|
- lm = &st->load_modules[i+1];
|
||
|
+ if (sp_in < lm->symtable[t] || sp_in > lm->symend[t])
|
||
|
+ continue;
|
||
|
|
||
|
- sp = lm->mod_symtable;
|
||
|
+ if (sp_in == lm->symtable[t])
|
||
|
+ return prev_module_symbol_by_value(sp_in->value);
|
||
|
+
|
||
|
+ sp = sp_in - 1;
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp))
|
||
|
+ return prev_module_symbol_by_value(sp->value);
|
||
|
+
|
||
|
+ return sp;
|
||
|
}
|
||
|
- return sp;
|
||
|
}
|
||
|
|
||
|
- return NULL;
|
||
|
+ return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
@@ -5335,6 +6268,9 @@ prev_symbol(char *symbol, struct syment *sp_in)
|
||
|
sp_prev = sp;
|
||
|
}
|
||
|
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ return prev_module_symbol_by_syment(sp_in);
|
||
|
+
|
||
|
search_init = FALSE;
|
||
|
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
@@ -5378,6 +6314,9 @@ prev_symbol(char *symbol, struct syment *sp_in)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ return prev_symbol_by_symname(symbol);
|
||
|
+
|
||
|
if (strstr(symbol, " module)")) {
|
||
|
sprintf(buf, "_MODULE_START_");
|
||
|
strcat(buf, &symbol[1]);
|
||
|
@@ -5600,7 +6539,7 @@ kernel_symbol_search(char *symbol)
|
||
|
int
|
||
|
get_syment_array(char *symbol, struct syment **sp_array, int max)
|
||
|
{
|
||
|
- int i, cnt;
|
||
|
+ int i, cnt, t;
|
||
|
struct syment *sp, *sp_end;
|
||
|
struct load_module *lm;
|
||
|
|
||
|
@@ -5625,6 +6564,31 @@ get_syment_array(char *symbol, struct syment **sp_array, int max)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+
|
||
|
+ sp = lm->symtable[t];
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+ for (; sp < sp_end; sp++) {
|
||
|
+ if (STREQ(symbol, sp->name)) {
|
||
|
+ if (max && (cnt < max))
|
||
|
+ sp_array[cnt] = sp;
|
||
|
+ cnt++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return cnt;
|
||
|
+
|
||
|
+old_module:
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
lm = &st->load_modules[i];
|
||
|
sp = lm->mod_symtable;
|
||
|
@@ -9229,6 +10193,9 @@ dump_offset_table(char *spec, ulong makestruct)
|
||
|
OFFSET(module_strtab));
|
||
|
fprintf(fp, " module_percpu: %ld\n",
|
||
|
OFFSET(module_percpu));
|
||
|
+ fprintf(fp, " module_mem: %ld\n", OFFSET(module_mem));
|
||
|
+ fprintf(fp, " module_memory_base: %ld\n", OFFSET(module_memory_base));
|
||
|
+ fprintf(fp, " module_memory_size: %ld\n", OFFSET(module_memory_size));
|
||
|
|
||
|
fprintf(fp, " module_sect_attrs: %ld\n",
|
||
|
OFFSET(module_sect_attrs));
|
||
|
@@ -10852,6 +11819,7 @@ dump_offset_table(char *spec, ulong makestruct)
|
||
|
SIZE(super_block));
|
||
|
fprintf(fp, " irqdesc: %ld\n", SIZE(irqdesc));
|
||
|
fprintf(fp, " module: %ld\n", SIZE(module));
|
||
|
+ fprintf(fp, " module_memory: %ld\n", SIZE(module_memory));
|
||
|
fprintf(fp, " module_sect_attr: %ld\n", SIZE(module_sect_attr));
|
||
|
fprintf(fp, " list_head: %ld\n", SIZE(list_head));
|
||
|
fprintf(fp, " hlist_head: %ld\n", SIZE(hlist_head));
|
||
|
@@ -11467,6 +12435,13 @@ store_section_data(struct load_module *lm, bfd *bfd, asection *section)
|
||
|
lm->mod_section_data[i].section = section;
|
||
|
lm->mod_section_data[i].priority = prio;
|
||
|
lm->mod_section_data[i].flags = section->flags & ~SEC_FOUND;
|
||
|
+ lm->mod_section_data[i].size = bfd_section_size(section);
|
||
|
+ lm->mod_section_data[i].offset = 0;
|
||
|
+ lm->mod_section_data[i].addr = 0;
|
||
|
+ if (strlen(name) < MAX_MOD_SEC_NAME)
|
||
|
+ strcpy(lm->mod_section_data[i].name, name);
|
||
|
+ else
|
||
|
+ strncpy(lm->mod_section_data[i].name, name, MAX_MOD_SEC_NAME-1);
|
||
|
/*
|
||
|
* The percpu section isn't included in kallsyms or module_core area.
|
||
|
*/
|
||
|
@@ -11474,13 +12449,8 @@ store_section_data(struct load_module *lm, bfd *bfd, asection *section)
|
||
|
(STREQ(name,".data.percpu") || STREQ(name, ".data..percpu"))) {
|
||
|
lm->mod_percpu_size = bfd_section_size(section);
|
||
|
lm->mod_section_data[i].flags |= SEC_FOUND;
|
||
|
+ lm->mod_section_data[i].addr = lm->mod_percpu;
|
||
|
}
|
||
|
- lm->mod_section_data[i].size = bfd_section_size(section);
|
||
|
- lm->mod_section_data[i].offset = 0;
|
||
|
- if (strlen(name) < MAX_MOD_SEC_NAME)
|
||
|
- strcpy(lm->mod_section_data[i].name, name);
|
||
|
- else
|
||
|
- strncpy(lm->mod_section_data[i].name, name, MAX_MOD_SEC_NAME-1);
|
||
|
lm->mod_sections += 1;
|
||
|
}
|
||
|
|
||
|
@@ -11722,6 +12692,124 @@ calculate_load_order_v2(struct load_module *lm, bfd *bfd, int dynamic,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+/* Linux 6.4 and later */
|
||
|
+static void
|
||
|
+calculate_load_order_6_4(struct load_module *lm, bfd *bfd, int dynamic,
|
||
|
+ void *minisyms, long symcount, unsigned int size)
|
||
|
+{
|
||
|
+ struct syment *s1, *s2;
|
||
|
+ ulong sec_start;
|
||
|
+ bfd_byte *from, *fromend;
|
||
|
+ asymbol *store;
|
||
|
+ asymbol *sym;
|
||
|
+ symbol_info syminfo;
|
||
|
+ char *secname;
|
||
|
+ int i, t;
|
||
|
+
|
||
|
+ if ((store = bfd_make_empty_symbol(bfd)) == NULL)
|
||
|
+ error(FATAL, "bfd_make_empty_symbol() failed\n");
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ s1 = lm->symtable[t];
|
||
|
+ s2 = lm->symend[t];
|
||
|
+ while (s1 < s2) {
|
||
|
+
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(s1)) {
|
||
|
+ s1++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Skip over symbols whose sections have been identified. */
|
||
|
+ for (i = 0; i < lm->mod_sections; i++) {
|
||
|
+ if ((lm->mod_section_data[i].flags & SEC_FOUND) == 0)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (s1->value >= lm->mod_section_data[i].addr &&
|
||
|
+ s1->value < lm->mod_section_data[i].addr
|
||
|
+ + lm->mod_section_data[i].size)
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Matched one of the sections. Skip symbol. */
|
||
|
+ if (i < lm->mod_sections) {
|
||
|
+ if (CRASHDEBUG(2))
|
||
|
+ fprintf(fp, "skip %lx %s %s\n", s1->value, s1->name,
|
||
|
+ lm->mod_section_data[i].name);
|
||
|
+ s1++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Find the symbol in the object file. */
|
||
|
+ from = (bfd_byte *) minisyms;
|
||
|
+ fromend = from + symcount * size;
|
||
|
+ secname = NULL;
|
||
|
+ for (; from < fromend; from += size) {
|
||
|
+ if (!(sym = bfd_minisymbol_to_symbol(bfd, dynamic, from, store)))
|
||
|
+ error(FATAL, "bfd_minisymbol_to_symbol() failed\n");
|
||
|
+
|
||
|
+ bfd_get_symbol_info(bfd, sym, &syminfo);
|
||
|
+ if (CRASHDEBUG(3)) {
|
||
|
+ fprintf(fp,"matching sym %s %lx against bfd %s %lx\n",
|
||
|
+ s1->name, (long) s1->value, syminfo.name,
|
||
|
+ (long) syminfo.value);
|
||
|
+ }
|
||
|
+ if (strcmp(syminfo.name, s1->name) == 0) {
|
||
|
+ secname = (char *)bfd_section_name(sym->section);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ }
|
||
|
+ if (secname == NULL) {
|
||
|
+ if (CRASHDEBUG(1))
|
||
|
+ fprintf(fp, "symbol %s not found in module\n", s1->name);
|
||
|
+ s1++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Match the section it came in. */
|
||
|
+ for (i = 0; i < lm->mod_sections; i++) {
|
||
|
+ if (STREQ(lm->mod_section_data[i].name, secname))
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (i == lm->mod_sections) {
|
||
|
+ fprintf(fp, "?? Section %s not found for symbol %s\n",
|
||
|
+ secname, s1->name);
|
||
|
+ s1++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (lm->mod_section_data[i].flags & SEC_FOUND) {
|
||
|
+ s1++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Update the offset information for the section */
|
||
|
+ sec_start = s1->value - syminfo.value;
|
||
|
+ /* keep the address instead of offset */
|
||
|
+ lm->mod_section_data[i].addr = sec_start;
|
||
|
+ lm->mod_section_data[i].flags |= SEC_FOUND;
|
||
|
+
|
||
|
+ if (CRASHDEBUG(2))
|
||
|
+ fprintf(fp, "update sec offset sym %s @ %lx val %lx section %s\n",
|
||
|
+ s1->name, s1->value, (ulong)syminfo.value, secname);
|
||
|
+
|
||
|
+ if (strcmp(secname, ".text") == 0)
|
||
|
+ lm->mod_text_start = sec_start;
|
||
|
+
|
||
|
+ if (strcmp(secname, ".bss") == 0)
|
||
|
+ lm->mod_bss_start = sec_start;
|
||
|
+
|
||
|
+ if (strcmp(secname, ".data") == 0)
|
||
|
+ lm->mod_data_start = sec_start;
|
||
|
+
|
||
|
+ if (strcmp(secname, ".rodata") == 0)
|
||
|
+ lm->mod_rodata_start = sec_start;
|
||
|
+ s1++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
/*
|
||
|
* Later versons of insmod store basic address information of each
|
||
|
* module in a format that looks like the following example of the
|
||
|
@@ -11927,8 +13015,11 @@ add_symbol_file(struct load_module *lm)
|
||
|
(!STREQ(secname, ".text") &&
|
||
|
!STREQ(secname, ".data.percpu") &&
|
||
|
!STREQ(secname, ".data..percpu"))) {
|
||
|
- sprintf(buf, " -s %s 0x%lx", secname,
|
||
|
- lm->mod_section_data[i].offset + lm->mod_base);
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ sprintf(buf, " -s %s 0x%lx", secname, lm->mod_section_data[i].addr);
|
||
|
+ else
|
||
|
+ sprintf(buf, " -s %s 0x%lx", secname,
|
||
|
+ lm->mod_section_data[i].offset + lm->mod_base);
|
||
|
len += strlen(buf);
|
||
|
}
|
||
|
}
|
||
|
@@ -12269,24 +13360,43 @@ static struct syment *
|
||
|
kallsyms_module_symbol(struct load_module *lm, symbol_info *syminfo)
|
||
|
{
|
||
|
struct syment *sp, *spx;
|
||
|
- int cnt;
|
||
|
+ int cnt, t;
|
||
|
|
||
|
if (!(lm->mod_flags & MOD_KALLSYMS))
|
||
|
return NULL;
|
||
|
|
||
|
sp = NULL;
|
||
|
cnt = 0;
|
||
|
- for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) {
|
||
|
- if (!STREQ(spx->name, syminfo->name))
|
||
|
- continue;
|
||
|
- if (spx->cnt) {
|
||
|
- cnt++;
|
||
|
- continue;
|
||
|
- }
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->ext_symtable[t])
|
||
|
+ continue;
|
||
|
+ for (spx = lm->ext_symtable[t]; spx <= lm->ext_symend[t]; spx++) {
|
||
|
+ if (!STREQ(spx->name, syminfo->name))
|
||
|
+ continue;
|
||
|
+ if (spx->cnt) {
|
||
|
+ cnt++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
|
||
|
- spx->cnt++;
|
||
|
- sp = spx;
|
||
|
- break;
|
||
|
+ spx->cnt++;
|
||
|
+ sp = spx;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) {
|
||
|
+ if (!STREQ(spx->name, syminfo->name))
|
||
|
+ continue;
|
||
|
+ if (spx->cnt) {
|
||
|
+ cnt++;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ spx->cnt++;
|
||
|
+ sp = spx;
|
||
|
+ break;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
if (CRASHDEBUG(2)) {
|
||
|
@@ -12312,7 +13422,7 @@ static void
|
||
|
store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
long symcount, unsigned int size, ulong base_addr, char *namelist)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
asymbol *store;
|
||
|
asymbol *sym;
|
||
|
bfd_byte *from, *fromend;
|
||
|
@@ -12323,7 +13433,7 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
char *nameptr, *secname;
|
||
|
long index;
|
||
|
long symalloc;
|
||
|
- int found;
|
||
|
+ int found = FALSE;
|
||
|
|
||
|
if ((store = bfd_make_empty_symbol(bfd)) == NULL)
|
||
|
error(FATAL, "bfd_make_empty_symbol() failed\n");
|
||
|
@@ -12380,8 +13490,17 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
lm->mod_rodata_start = lm->mod_bss_start = 0;
|
||
|
lm->mod_load_symcnt = 0;
|
||
|
lm->mod_sections = 0;
|
||
|
- for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++)
|
||
|
- spx->cnt = 0;
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->ext_symtable[t])
|
||
|
+ continue;
|
||
|
+ for (spx = lm->ext_symtable[t]; spx <= lm->ext_symend[t]; spx++)
|
||
|
+ spx->cnt = 0;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++)
|
||
|
+ spx->cnt = 0;
|
||
|
+ }
|
||
|
sp = lm->mod_load_symtable;
|
||
|
|
||
|
if (!(lm->mod_section_data = (struct mod_section_data *)
|
||
|
@@ -12392,13 +13511,14 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
|
||
|
bfd_map_over_sections(bfd, section_header_info, MODULE_SECTIONS);
|
||
|
|
||
|
- if (kt->flags & KMOD_V1)
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ calculate_load_order_6_4(lm, bfd, dynamic, minisyms, symcount, size);
|
||
|
+ else if (kt->flags & KMOD_V1)
|
||
|
calculate_load_order_v1(lm, bfd);
|
||
|
else
|
||
|
calculate_load_order_v2(lm, bfd, dynamic, minisyms,
|
||
|
symcount, size);
|
||
|
|
||
|
-
|
||
|
from = (bfd_byte *) minisyms;
|
||
|
fromend = from + symcount * size;
|
||
|
for (; from < fromend; from += size)
|
||
|
@@ -12501,7 +13621,10 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
syminfo.value += lm->mod_percpu;
|
||
|
found = TRUE;
|
||
|
} else {
|
||
|
- syminfo.value += lm->mod_section_data[i].offset + lm->mod_base;
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ syminfo.value += lm->mod_section_data[i].addr;
|
||
|
+ else
|
||
|
+ syminfo.value += lm->mod_section_data[i].offset + lm->mod_base;
|
||
|
found = TRUE;
|
||
|
}
|
||
|
}
|
||
|
@@ -12536,6 +13659,53 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
* syminfo data types accepted above, plus the two pseudo symbols.
|
||
|
* Note that the new syment name pointers haven't been resolved yet.
|
||
|
*/
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->ext_symtable[t])
|
||
|
+ continue;
|
||
|
+ for (spx = lm->ext_symtable[t]; spx <= lm->ext_symend[t]; spx++) {
|
||
|
+ found = FALSE;
|
||
|
+ for (sp = lm->mod_load_symtable; sp < lm->mod_load_symend; sp++) {
|
||
|
+ index = (long)sp->name;
|
||
|
+ nameptr = &lm->mod_load_namespace.address[index];
|
||
|
+ if (STREQ(spx->name, nameptr)) {
|
||
|
+ found = TRUE;
|
||
|
+ if (spx->value == sp->value) {
|
||
|
+ if (CRASHDEBUG(2))
|
||
|
+ fprintf(fp, "%s: %s matches!\n",
|
||
|
+ lm->mod_name, nameptr);
|
||
|
+ } else {
|
||
|
+ if (CRASHDEBUG(2))
|
||
|
+ fprintf(fp,
|
||
|
+ "[%s] %s: %lx != extern'd value: %lx\n",
|
||
|
+ lm->mod_name,
|
||
|
+ nameptr, sp->value,
|
||
|
+ spx->value);
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (!found) {
|
||
|
+ if (CRASHDEBUG(2))
|
||
|
+ fprintf(fp, "append ext %s (%lx)\n", spx->name, spx->value);
|
||
|
+ /* append it here... */
|
||
|
+ namespace_ctl(NAMESPACE_INSTALL,
|
||
|
+ &lm->mod_load_namespace,
|
||
|
+ lm->mod_load_symend, spx->name);
|
||
|
+
|
||
|
+ lm->mod_load_symend->value = spx->value;
|
||
|
+ lm->mod_load_symend->type = spx->type;
|
||
|
+ lm->mod_load_symend->flags |= MODULE_SYMBOL;
|
||
|
+ lm->mod_load_symend++;
|
||
|
+ lm->mod_load_symcnt++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ goto append_section_symbols;
|
||
|
+
|
||
|
+old_module:
|
||
|
for (spx = lm->mod_ext_symtable; spx <= lm->mod_ext_symend; spx++) {
|
||
|
found = FALSE;
|
||
|
for (sp = lm->mod_load_symtable;
|
||
|
@@ -12578,6 +13748,7 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+append_section_symbols:
|
||
|
/*
|
||
|
* Append helpful pseudo symbols about found out sections.
|
||
|
* Use 'S' as its type which is never seen in existing symbols.
|
||
|
@@ -12587,8 +13758,11 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
if (!(lm->mod_section_data[i].flags & SEC_FOUND))
|
||
|
continue;
|
||
|
/* Section start */
|
||
|
- lm->mod_load_symend->value = lm->mod_base +
|
||
|
- lm->mod_section_data[i].offset;
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ lm->mod_load_symend->value = lm->mod_section_data[i].addr;
|
||
|
+ else
|
||
|
+ lm->mod_load_symend->value = lm->mod_base +
|
||
|
+ lm->mod_section_data[i].offset;
|
||
|
lm->mod_load_symend->type = 'S';
|
||
|
lm->mod_load_symend->flags |= MODULE_SYMBOL;
|
||
|
sprintf(name, "_MODULE_SECTION_START [%s]",
|
||
|
@@ -12599,9 +13773,12 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
lm->mod_load_symcnt++;
|
||
|
|
||
|
/* Section end */
|
||
|
- lm->mod_load_symend->value = lm->mod_base +
|
||
|
- lm->mod_section_data[i].offset +
|
||
|
- lm->mod_section_data[i].size;
|
||
|
+ if (MODULE_MEMORY())
|
||
|
+ lm->mod_load_symend->value = lm->mod_section_data[i].addr;
|
||
|
+ else
|
||
|
+ lm->mod_load_symend->value = lm->mod_base +
|
||
|
+ lm->mod_section_data[i].offset;
|
||
|
+ lm->mod_load_symend->value += lm->mod_section_data[i].size;
|
||
|
lm->mod_load_symend->type = 'S';
|
||
|
lm->mod_load_symend->flags |= MODULE_SYMBOL;
|
||
|
sprintf(name, "_MODULE_SECTION_END [%s]",
|
||
|
@@ -12618,16 +13795,57 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
qsort(lm->mod_load_symtable, lm->mod_load_symcnt, sizeof(struct syment),
|
||
|
compare_syms);
|
||
|
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ /* keep load symtable addresses to lm->load_symtable[] */
|
||
|
+ /* TODO: make more efficient */
|
||
|
+ for (sp = lm->mod_load_symtable; sp < lm->mod_load_symend; sp++) {
|
||
|
+ char buf1[BUFSIZE], buf2[BUFSIZE];
|
||
|
+
|
||
|
+ if (CRASHDEBUG(2))
|
||
|
+ fprintf(fp, "DEBUG: value %16lx name %s\n", sp->value, sp->name);
|
||
|
+
|
||
|
+ if (!MODULE_PSEUDO_SYMBOL(sp))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->mem[t].size)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ sprintf(buf1, "%s%s", module_tag[t].start, lm->mod_name);
|
||
|
+ sprintf(buf2, "%s%s", module_tag[t].end, lm->mod_name);
|
||
|
+
|
||
|
+ if (STREQ(sp->name, buf1)) {
|
||
|
+ lm->load_symtable[t] = sp;
|
||
|
+ break;
|
||
|
+ } else if (STREQ(sp->name, buf2)) {
|
||
|
+ lm->load_symend[t] = sp;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
lm->mod_load_symend--;
|
||
|
- if (!MODULE_END(lm->mod_load_symend) &&
|
||
|
+ if (!MODULE_MEMORY() && !MODULE_END(lm->mod_load_symend) &&
|
||
|
!IN_MODULE_PERCPU(lm->mod_load_symend->value, lm))
|
||
|
error(INFO, "%s: last symbol: %s is not _MODULE_END_%s?\n",
|
||
|
lm->mod_name, lm->mod_load_symend->name, lm->mod_name);
|
||
|
|
||
|
- mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend);
|
||
|
- lm->mod_symtable = lm->mod_load_symtable;
|
||
|
- lm->mod_symend = lm->mod_load_symend;
|
||
|
- mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+ mod_symtable_hash_remove_range(lm->symtable[t], lm->symend[t]);
|
||
|
+ }
|
||
|
+ lm->symtable = lm->load_symtable;
|
||
|
+ lm->symend = lm->load_symend;
|
||
|
+ mod_symtable_hash_install_range(lm->mod_load_symtable, lm->mod_load_symend);
|
||
|
+ } else {
|
||
|
+ mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+ lm->mod_symtable = lm->mod_load_symtable;
|
||
|
+ lm->mod_symend = lm->mod_load_symend;
|
||
|
+ mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+ }
|
||
|
|
||
|
lm->mod_flags &= ~MOD_EXT_SYMS;
|
||
|
lm->mod_flags |= MOD_LOAD_SYMS;
|
||
|
@@ -12642,7 +13860,7 @@ store_load_module_symbols(bfd *bfd, int dynamic, void *minisyms,
|
||
|
void
|
||
|
delete_load_module(ulong base_addr)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
struct load_module *lm;
|
||
|
struct gnu_request request, *req;
|
||
|
|
||
|
@@ -12657,7 +13875,18 @@ delete_load_module(ulong base_addr)
|
||
|
req->name = lm->mod_namelist;
|
||
|
gdb_interface(req);
|
||
|
}
|
||
|
- mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ if (lm->mod_load_symtable) {
|
||
|
+ mod_symtable_hash_remove_range(lm->mod_load_symtable,
|
||
|
+ lm->mod_load_symend);
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ lm->load_symtable[t] = NULL;
|
||
|
+ lm->load_symend[t] = NULL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+
|
||
|
if (lm->mod_load_symtable) {
|
||
|
free(lm->mod_load_symtable);
|
||
|
namespace_ctl(NAMESPACE_FREE,
|
||
|
@@ -12665,9 +13894,23 @@ delete_load_module(ulong base_addr)
|
||
|
}
|
||
|
if (lm->mod_flags & MOD_REMOTE)
|
||
|
unlink_module(lm);
|
||
|
- lm->mod_symtable = lm->mod_ext_symtable;
|
||
|
- lm->mod_symend = lm->mod_ext_symend;
|
||
|
- mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ if (lm->mod_load_symtable) { /* still non-NULL */
|
||
|
+ lm->symtable = lm->ext_symtable;
|
||
|
+ lm->symend = lm->ext_symend;
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+ mod_symtable_hash_install_range(lm->symtable[t],
|
||
|
+ lm->symend[t]);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ lm->mod_symtable = lm->mod_ext_symtable;
|
||
|
+ lm->mod_symend = lm->mod_ext_symend;
|
||
|
+ mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+ }
|
||
|
lm->mod_flags &= ~(MOD_LOAD_SYMS|MOD_REMOTE|MOD_NOPATCH);
|
||
|
lm->mod_flags |= MOD_EXT_SYMS;
|
||
|
lm->mod_load_symtable = NULL;
|
||
|
@@ -12696,7 +13939,18 @@ delete_load_module(ulong base_addr)
|
||
|
req->name = lm->mod_namelist;
|
||
|
gdb_interface(req);
|
||
|
}
|
||
|
- mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ if (lm->mod_load_symtable) {
|
||
|
+ mod_symtable_hash_remove_range(lm->mod_load_symtable,
|
||
|
+ lm->mod_load_symend);
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ lm->load_symtable[t] = NULL;
|
||
|
+ lm->load_symend[t] = NULL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else
|
||
|
+ mod_symtable_hash_remove_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+
|
||
|
if (lm->mod_load_symtable) {
|
||
|
free(lm->mod_load_symtable);
|
||
|
namespace_ctl(NAMESPACE_FREE,
|
||
|
@@ -12704,9 +13958,23 @@ delete_load_module(ulong base_addr)
|
||
|
}
|
||
|
if (lm->mod_flags & MOD_REMOTE)
|
||
|
unlink_module(lm);
|
||
|
- lm->mod_symtable = lm->mod_ext_symtable;
|
||
|
- lm->mod_symend = lm->mod_ext_symend;
|
||
|
- mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+
|
||
|
+ if (MODULE_MEMORY()) {
|
||
|
+ if (lm->mod_load_symtable) {
|
||
|
+ lm->symtable = lm->ext_symtable;
|
||
|
+ lm->symend = lm->ext_symend;
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ if (!lm->symtable[t])
|
||
|
+ continue;
|
||
|
+ mod_symtable_hash_install_range(lm->symtable[t],
|
||
|
+ lm->symend[t]);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ lm->mod_symtable = lm->mod_ext_symtable;
|
||
|
+ lm->mod_symend = lm->mod_ext_symend;
|
||
|
+ mod_symtable_hash_install_range(lm->mod_symtable, lm->mod_symend);
|
||
|
+ }
|
||
|
lm->mod_flags &= ~(MOD_LOAD_SYMS|MOD_REMOTE|MOD_NOPATCH);
|
||
|
lm->mod_flags |= MOD_EXT_SYMS;
|
||
|
lm->mod_load_symtable = NULL;
|
||
|
@@ -13455,7 +14723,7 @@ is_downsized(char *name)
|
||
|
struct syment *
|
||
|
symbol_complete_match(const char *match, struct syment *sp_last)
|
||
|
{
|
||
|
- int i;
|
||
|
+ int i, t;
|
||
|
struct syment *sp, *sp_end, *sp_start;
|
||
|
struct load_module *lm;
|
||
|
int search_init;
|
||
|
@@ -13475,6 +14743,34 @@ symbol_complete_match(const char *match, struct syment *sp_last)
|
||
|
sp_start = NULL;
|
||
|
}
|
||
|
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for (i = 0; i < st->mods_installed; i++) {
|
||
|
+ lm = &st->load_modules[i];
|
||
|
+
|
||
|
+ for_each_mod_mem_type(t) {
|
||
|
+ sp_end = lm->symend[t];
|
||
|
+ if (!sp_start)
|
||
|
+ sp_start = lm->symtable[t];
|
||
|
+
|
||
|
+ if (sp_start < lm->symtable[t] || sp_start > sp_end)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ for (sp = sp_start; sp < sp_end; sp++) {
|
||
|
+ if (MODULE_PSEUDO_SYMBOL(sp))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (STRNEQ(sp->name, match))
|
||
|
+ return sp;
|
||
|
+ }
|
||
|
+ sp_start = NULL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+old_module:
|
||
|
search_init = FALSE;
|
||
|
|
||
|
for (i = 0; i < st->mods_installed; i++) {
|
||
|
@@ -13521,3 +14817,58 @@ symbol_complete_match(const char *match, struct syment *sp_last)
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
+
|
||
|
+/* Returns module memory type if addr is in range, otherwise MOD_INVALID(-1) */
|
||
|
+static int
|
||
|
+in_module_range(ulong addr, struct load_module *lm, int start, int end)
|
||
|
+{
|
||
|
+ ulong base = 0, size = 0;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (!MODULE_MEMORY())
|
||
|
+ goto old_module;
|
||
|
+
|
||
|
+ for (i = start ; i <= end; i++) {
|
||
|
+ base = lm->mem[i].base;
|
||
|
+ size = lm->mem[i].size;
|
||
|
+ if (!size)
|
||
|
+ continue;
|
||
|
+ if ((addr >= base) && (addr < (base + size)))
|
||
|
+ return i;
|
||
|
+ }
|
||
|
+ return MOD_INVALID;
|
||
|
+
|
||
|
+old_module:
|
||
|
+ if (start == MOD_TEXT) {
|
||
|
+ base = lm->mod_base;
|
||
|
+ size = lm->mod_size;
|
||
|
+ } else if (start == MOD_INIT_TEXT) {
|
||
|
+ base = lm->mod_init_module_ptr;
|
||
|
+ size = lm->mod_init_size;
|
||
|
+ } else
|
||
|
+ error(FATAL, "invalid module memory type!");
|
||
|
+
|
||
|
+ if ((addr >= base) && (addr < (base + size)))
|
||
|
+ return start;
|
||
|
+
|
||
|
+ return MOD_INVALID;
|
||
|
+}
|
||
|
+
|
||
|
+/* Returns module memory type, otherwise MOD_INVALID(-1) */
|
||
|
+static int
|
||
|
+module_mem_type(ulong addr, struct load_module *lm)
|
||
|
+{
|
||
|
+ return in_module_range(addr, lm, MOD_TEXT, MOD_INIT_RODATA);
|
||
|
+}
|
||
|
+
|
||
|
+/* Returns the end address of the module memory region. */
|
||
|
+static ulong
|
||
|
+module_mem_end(ulong addr, struct load_module *lm)
|
||
|
+{
|
||
|
+ int type = module_mem_type(addr, lm);
|
||
|
+
|
||
|
+ if (type == MOD_INVALID)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ return lm->mem[type].base + lm->mem[type].size;
|
||
|
+}
|
||
|
--
|
||
|
2.37.1
|
||
|
|