Skip to content

Commit

Permalink
Support module memory layout change on Linux 6.4
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
k-hagio committed Jun 26, 2023
1 parent 8b24b20 commit 7750e61
Show file tree
Hide file tree
Showing 5 changed files with 1,629 additions and 145 deletions.
46 changes: 40 additions & 6 deletions defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -675,13 +675,15 @@ 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)
#define PVOPS() (kt->flags & ARCH_PVOPS)
#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())
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
25 changes: 25 additions & 0 deletions gdb-10.2.patch
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
56 changes: 48 additions & 8 deletions kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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
Expand Down Expand Up @@ -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"));
}

Expand Down Expand Up @@ -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);
Expand Down
38 changes: 36 additions & 2 deletions memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 7750e61

Please sign in to comment.