From: wya <wya(a)qti.qualcomm.com>
Recover readable modules from module_kset when a dumpfile cannot walk
the modules list. Use recovered addresses for both module memory
layouts.
Signed-off-by: wya <wya(a)qti.qualcomm.com>
Signed-off-by: Chao Huang <chahuan(a)qti.qualcomm.com>
---
defs.h | 7 ++-
kernel.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++----
symbols.c | 33 ++++++++--
3 files changed, 195 insertions(+), 20 deletions(-)
diff --git a/defs.h b/defs.h
index c8708d1..c27c98c 100644
--- a/defs.h
+++ b/defs.h
@@ -2241,6 +2241,9 @@ struct offset_table { /* stash of commonly-used
offsets */
long module_mem;
long module_memory_base;
long module_memory_size;
+ long module_mkobj;
+ long module_kobject_kobj;
+ long module_kobject_mod;
long irq_data_irq;
long zspage_huge;
long zram_comp_algs;
@@ -5840,8 +5843,8 @@ void dump_struct(char *, ulong, unsigned);
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);
+void store_module_symbols_v2(ulong, int, ulong *);
+void store_module_symbols_6_4(ulong, int, ulong *);
int is_datatype_command(void);
int is_typedef(char *);
int arg_to_datatype(char *, struct datatype_member *, ulong);
diff --git a/kernel.c b/kernel.c
index 2d9ac03..132449e 100644
--- a/kernel.c
+++ b/kernel.c
@@ -95,6 +95,7 @@ static void hypervisor_init(void);
static void dump_log_legacy(void);
static void dump_variable_length_record(void);
static int is_livepatch(void);
+static int recover_modules_from_module_kset(void *, ulong **);
static void show_kernel_taints(char *, int);
static void dump_dmi_info(void);
static void list_source_code(struct gnu_request *, int);
@@ -3616,6 +3617,99 @@ get_dumpfile_regs(struct bt_info *bt, ulong *eip, ulong *esp)
}
+static int
+recover_modules_from_module_kset(void *module_buf, ulong **module_list)
+{
+ ulong module_kset, head, next, module_kobj, module;
+ ulong *module_addr_list;
+ struct list_data ld;
+ int complete, count, i, recovered;
+
+ *module_list = NULL;
+
+ if (!kernel_symbol_exists("module_kset") ||
+ !STRUCT_EXISTS("module_kobject") ||
+ !VALID_MEMBER(module_mkobj) ||
+ !VALID_MEMBER(module_kobject_kobj) ||
+ !VALID_MEMBER(module_kobject_mod) ||
+ !VALID_MEMBER(kset_list) ||
+ !VALID_MEMBER(kobject_entry))
+ return 0;
+
+ if (!try_get_symbol_data("module_kset", sizeof(ulong), &module_kset) ||
+ !module_kset)
+ return 0;
+
+ head = module_kset + OFFSET(kset_list);
+ if (!readmem(head + OFFSET(list_head_next), KVADDR, &next,
+ sizeof(ulong), "module_kset list_head", RETURN_ON_ERROR|QUIET))
+ return 0;
+ if (next == head)
+ return 0;
+
+ if (!hq_open())
+ return 0;
+
+ BZERO(&ld, sizeof(struct list_data));
+ ld.flags = RETURN_ON_DUPLICATE|RETURN_ON_LIST_ERROR;
+ ld.start = next;
+ ld.end = head;
+ ld.list_head_offset = OFFSET(kobject_entry);
+ do_list(&ld);
+ count = hq_close();
+ if (!count)
+ return 0;
+
+ ld.list_ptr = (ulong *)GETBUF(count * sizeof(ulong));
+ count = retrieve_list(ld.list_ptr, count);
+ if (count <= 0) {
+ FREEBUF(ld.list_ptr);
+ return 0;
+ }
+
+ complete = readmem(ld.list_ptr[count-1] + OFFSET(kobject_entry),
+ KVADDR, &next, sizeof(ulong), "module_kset entry",
+ RETURN_ON_ERROR|QUIET) && next == head;
+ module_addr_list = malloc(count * sizeof(ulong));
+ if (!module_addr_list)
+ error(FATAL, "module address list malloc: %s\n",
+ strerror(errno));
+
+ recovered = 0;
+ for (i = 0; i < count; i++) {
+ module_kobj = ld.list_ptr[i] - OFFSET(module_kobject_kobj);
+ if (!readmem(module_kobj + OFFSET(module_kobject_mod), KVADDR,
+ &module, sizeof(ulong), "module_kobject.mod",
+ RETURN_ON_ERROR|QUIET))
+ continue;
+
+ if (!module)
+ continue;
+ if (module + OFFSET(module_mkobj) != module_kobj)
+ continue;
+
+ if (!readmem(module, KVADDR, module_buf, SIZE(module),
+ "module struct", RETURN_ON_ERROR|QUIET))
+ continue;
+ module_addr_list[recovered++] = module;
+ }
+ FREEBUF(ld.list_ptr);
+
+ if (!recovered) {
+ free(module_addr_list);
+ return 0;
+ }
+
+ *module_list = module_addr_list;
+ error(WARNING,
+ "recovered %d loaded module%s from module_kset%s\n",
+ recovered, recovered == 1 ? "" : "s",
+ complete ? "" : " (partial traversal)");
+
+ return recovered;
+}
+
+
/*
* Store the head of the kernel module list for future use.
* Count the number of symbols defined by all modules in the system,
@@ -3625,9 +3719,10 @@ void
module_init(void)
{
int i, c;
- ulong size, mod, mod_next;
+ ulong size, mod, mod_next, module_total;
uint nsyms;
ulong total, numksyms;
+ ulong *module_addr_list;
char *modbuf, *kallsymsbuf;
ulong kallsyms_header;
struct syment *sp, *sp_array[10];
@@ -3785,6 +3880,21 @@ module_init(void)
}
MEMBER_OFFSET_INIT(module_percpu, "module", "percpu");
+ if (MEMBER_EXISTS("module", "mkobj"))
+ MEMBER_OFFSET_INIT(module_mkobj, "module", "mkobj");
+ if (STRUCT_EXISTS("module_kobject")) {
+ if (MEMBER_EXISTS("module_kobject", "kobj"))
+ MEMBER_OFFSET_INIT(module_kobject_kobj,
+ "module_kobject", "kobj");
+ if (MEMBER_EXISTS("module_kobject", "mod"))
+ MEMBER_OFFSET_INIT(module_kobject_mod,
+ "module_kobject", "mod");
+ }
+ if (STRUCT_EXISTS("kset") && MEMBER_EXISTS("kset",
"list"))
+ MEMBER_OFFSET_INIT(kset_list, "kset", "list");
+ if (STRUCT_EXISTS("kobject") &&
+ MEMBER_EXISTS("kobject", "entry"))
+ MEMBER_OFFSET_INIT(kobject_entry, "kobject", "entry");
/*
* Make sure to pick the kernel "modules" list_head symbol,
@@ -3839,7 +3949,8 @@ module_init(void)
break;
}
- total = kt->mods_installed = 0;
+ total = kt->mods_installed = c = 0;
+ module_addr_list = NULL;
modbuf = GETBUF(SIZE(module));
kallsymsbuf = kt->flags & KALLSYMS_V1 ?
@@ -3847,20 +3958,39 @@ module_init(void)
please_wait("gathering module symbol data");
- for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) {
+ mod = kt->module_list;
+ i = 0;
+ while (mod != kt->kernel_module) {
if (CRASHDEBUG(3))
fprintf(fp, "module: %lx\n", mod);
if (!readmem(mod, KVADDR, modbuf, SIZE(module),
"module struct", RETURN_ON_ERROR|QUIET)) {
- error(WARNING,
- "%scannot access vmalloc'd module memory\n\n",
- DUMPFILE() ? "\n" : "");
- kt->mods_installed = 0;
- kt->flags |= NO_MODULE_ACCESS;
- FREEBUF(modbuf);
+ if (!module_addr_list && DUMPFILE() &&
+ (kt->flags & KMOD_V2)) {
+ error(WARNING,
+ "\ncannot access vmalloc'd module memory at %lx, "
+ "trying module_kset fallback\n\n", mod);
+ if ((c =
+ recover_modules_from_module_kset(modbuf,
+ &module_addr_list))) {
+ total = kt->mods_installed = 0;
+ i = 0;
+ mod = module_addr_list[0];
+ continue;
+ }
+ }
+
+ error(WARNING,
+ "%scannot access vmalloc'd module memory\n\n",
+ DUMPFILE() ? "\n" : "");
+ kt->mods_installed = 0;
+ kt->flags |= NO_MODULE_ACCESS;
+ free(module_addr_list);
+ FREEBUF(modbuf);
return;
}
+ module_total = total;
switch (kt->flags & (KMOD_V1|KMOD_V2))
{
@@ -3929,6 +4059,14 @@ module_init(void)
}
if (!size) {
+ if (module_addr_list) {
+ error(WARNING,
+ "invalid kernel module size: 0 at %lx, skipping module\n",
+ mod);
+ total = module_total;
+ goto next_module;
+ }
+
/*
* Bail out here instead of a crashing with a
* getbuf(0) failure during storage later on.
@@ -3945,9 +4083,19 @@ module_init(void)
break;
}
+ if (module_addr_list)
+ module_addr_list[kt->mods_installed] = mod;
kt->mods_installed++;
- NEXT_MODULE(mod_next, modbuf);
+ next_module:
+ if (module_addr_list) {
+ if (++i >= c)
+ break;
+ mod = module_addr_list[i];
+ } else {
+ NEXT_MODULE(mod_next, modbuf);
+ mod = mod_next;
+ }
}
FREEBUF(modbuf);
@@ -3961,11 +4109,14 @@ module_init(void)
break;
case KMOD_V2:
if (MODULE_MEMORY())
- store_module_symbols_6_4(total, kt->mods_installed);
+ store_module_symbols_6_4(total, kt->mods_installed,
+ module_addr_list);
else
- store_module_symbols_v2(total, kt->mods_installed);
+ store_module_symbols_v2(total, kt->mods_installed,
+ module_addr_list);
break;
}
+ free(module_addr_list);
please_wait_done();
}
diff --git a/symbols.c b/symbols.c
index 270e14e..e5c9881 100644
--- a/symbols.c
+++ b/symbols.c
@@ -1924,7 +1924,8 @@ modsym_value(ulong syms, union kernel_symbol *modsym, int i)
* Linux 6.4 introduced module.mem memory layout
*/
void
-store_module_symbols_6_4(ulong total, int mods_installed)
+store_module_symbols_6_4(ulong total, int mods_installed,
+ ulong *module_addr_list)
{
int i, m, t;
ulong mod, mod_next;
@@ -1976,7 +1977,13 @@ store_module_symbols_6_4(ulong total, int mods_installed)
modsymbuf = NULL;
m = mcnt = mod_next = 0;
- for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) {
+ for (mod = kt->module_list; ; ) {
+ if (module_addr_list) {
+ if (m >= mods_installed)
+ break;
+ mod = module_addr_list[m];
+ } else if (mod == kt->kernel_module)
+ break;
readmem(mod, KVADDR, modbuf, SIZE(module),
"module buffer", FAULT_ON_ERROR);
@@ -2225,7 +2232,9 @@ store_module_symbols_6_4(ulong total, int mods_installed)
lm->mod_ext_symcnt = mcnt - lm->mod_ext_symcnt;
- NEXT_MODULE(mod_next, modbuf);
+ if (!module_addr_list)
+ NEXT_MODULE(mod_next, modbuf);
+ mod = mod_next;
}
FREEBUF(modbuf);
@@ -2284,7 +2293,8 @@ store_module_symbols_6_4(ulong total, int mods_installed)
}
void
-store_module_symbols_v2(ulong total, int mods_installed)
+store_module_symbols_v2(ulong total, int mods_installed,
+ ulong *module_addr_list)
{
int i, m;
ulong mod, mod_next;
@@ -2341,7 +2351,13 @@ store_module_symbols_v2(ulong total, int mods_installed)
modsymbuf = NULL;
m = mcnt = mod_next = 0;
- for (mod = kt->module_list; mod != kt->kernel_module; mod = mod_next) {
+ for (mod = kt->module_list; ; ) {
+ if (module_addr_list) {
+ if (m >= mods_installed)
+ break;
+ mod = module_addr_list[m];
+ } else if (mod == kt->kernel_module)
+ break;
readmem(mod, KVADDR, modbuf, SIZE(module),
"module buffer", FAULT_ON_ERROR);
@@ -2619,7 +2635,9 @@ store_module_symbols_v2(ulong total, int mods_installed)
if (!lm->mod_etext_guess)
find_mod_etext(lm);
- NEXT_MODULE(mod_next, modbuf);
+ if (!module_addr_list)
+ NEXT_MODULE(mod_next, modbuf);
+ mod = mod_next;
}
FREEBUF(modbuf);
@@ -10400,6 +10418,9 @@ dump_offset_table(char *spec, ulong makestruct)
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_mkobj: %ld\n", OFFSET(module_mkobj));
+ fprintf(fp, " module_kobject_kobj: %ld\n",
OFFSET(module_kobject_kobj));
+ fprintf(fp, " module_kobject_mod: %ld\n",
OFFSET(module_kobject_mod));
fprintf(fp, " module_sect_attrs: %ld\n",
OFFSET(module_sect_attrs));
--
2.43.0