Skip to content

Commit

Permalink
module: replace module_layout with module_memory
Browse files Browse the repository at this point in the history
module_layout manages different types of memory (text, data, rodata, etc.)
in one allocation, which is problematic for some reasons:

1. It is hard to enable CONFIG_STRICT_MODULE_RWX.
2. It is hard to use huge pages in modules (and not break strict rwx).
3. Many archs uses module_layout for arch-specific data, but it is not
   obvious how these data are used (are they RO, RX, or RW?)

Improve the scenario by replacing 2 (or 3) module_layout per module with
up to 7 module_memory per module:

        MOD_TEXT,
        MOD_DATA,
        MOD_RODATA,
        MOD_RO_AFTER_INIT,
        MOD_INIT_TEXT,
        MOD_INIT_DATA,
        MOD_INIT_RODATA,

and allocating them separately. This adds slightly more entries to
mod_tree (from up to 3 entries per module, to up to 7 entries per
module). However, this at most adds a small constant overhead to
__module_address(), which is expected to be fast.

Various archs use module_layout for different data. These data are put
into different module_memory based on their location in module_layout.
IOW, data that used to go with text is allocated with MOD_MEM_TYPE_TEXT;
data that used to go with data is allocated with MOD_MEM_TYPE_DATA, etc.

module_memory simplifies quite some of the module code. For example,
ARCH_WANTS_MODULES_DATA_IN_VMALLOC is a lot cleaner, as it just uses a
different allocator for the data. kernel/module/strict_rwx.c is also
much cleaner with module_memory.

Signed-off-by: Song Liu <song@kernel.org>
Cc: Luis Chamberlain <mcgrof@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
  • Loading branch information
liu-song-6 authored and mcgrof committed Mar 9, 2023
1 parent fe15c26 commit ac3b432
Show file tree
Hide file tree
Showing 18 changed files with 427 additions and 467 deletions.
12 changes: 7 additions & 5 deletions arch/arc/kernel/unwind.c
Expand Up @@ -369,6 +369,8 @@ void *unwind_add_table(struct module *module, const void *table_start,
unsigned long table_size)
{
struct unwind_table *table;
struct module_memory *core_text;
struct module_memory *init_text;

if (table_size <= 0)
return NULL;
Expand All @@ -377,11 +379,11 @@ void *unwind_add_table(struct module *module, const void *table_start,
if (!table)
return NULL;

init_unwind_table(table, module->name,
module->core_layout.base, module->core_layout.size,
module->init_layout.base, module->init_layout.size,
table_start, table_size,
NULL, 0);
core_text = &module->mem[MOD_TEXT];
init_text = &module->mem[MOD_INIT_TEXT];

init_unwind_table(table, module->name, core_text->base, core_text->size,
init_text->base, init_text->size, table_start, table_size, NULL, 0);

init_unwind_hdr(table, unw_hdr_alloc);

Expand Down
9 changes: 2 additions & 7 deletions arch/arm/kernel/module-plts.c
Expand Up @@ -28,11 +28,6 @@ static const u32 fixed_plts[] = {
#endif
};

static bool in_init(const struct module *mod, unsigned long loc)
{
return loc - (u32)mod->init_layout.base < mod->init_layout.size;
}

static void prealloc_fixed(struct mod_plt_sec *pltsec, struct plt_entries *plt)
{
int i;
Expand All @@ -50,8 +45,8 @@ static void prealloc_fixed(struct mod_plt_sec *pltsec, struct plt_entries *plt)

u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
{
struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
&mod->arch.init;
struct mod_plt_sec *pltsec = !within_module_init(loc, mod) ?
&mod->arch.core : &mod->arch.init;
struct plt_entries *plt;
int idx;

Expand Down
13 changes: 4 additions & 9 deletions arch/arm64/kernel/module-plts.c
Expand Up @@ -65,17 +65,12 @@ static bool plt_entries_equal(const struct plt_entry *a,
(q + aarch64_insn_adrp_get_offset(le32_to_cpu(b->adrp)));
}

static bool in_init(const struct module *mod, void *loc)
{
return (u64)loc - (u64)mod->init_layout.base < mod->init_layout.size;
}

u64 module_emit_plt_entry(struct module *mod, Elf64_Shdr *sechdrs,
void *loc, const Elf64_Rela *rela,
Elf64_Sym *sym)
{
struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
&mod->arch.init;
struct mod_plt_sec *pltsec = !within_module_init((unsigned long)loc, mod) ?
&mod->arch.core : &mod->arch.init;
struct plt_entry *plt = (struct plt_entry *)sechdrs[pltsec->plt_shndx].sh_addr;
int i = pltsec->plt_num_entries;
int j = i - 1;
Expand Down Expand Up @@ -105,8 +100,8 @@ u64 module_emit_plt_entry(struct module *mod, Elf64_Shdr *sechdrs,
u64 module_emit_veneer_for_adrp(struct module *mod, Elf64_Shdr *sechdrs,
void *loc, u64 val)
{
struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
&mod->arch.init;
struct mod_plt_sec *pltsec = !within_module_init((unsigned long)loc, mod) ?
&mod->arch.core : &mod->arch.init;
struct plt_entry *plt = (struct plt_entry *)sechdrs[pltsec->plt_shndx].sh_addr;
int i = pltsec->plt_num_entries++;
u32 br;
Expand Down
24 changes: 14 additions & 10 deletions arch/ia64/kernel/module.c
Expand Up @@ -485,19 +485,19 @@ module_frob_arch_sections (Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings,
return 0;
}

static inline int
static inline bool
in_init (const struct module *mod, uint64_t addr)
{
return addr - (uint64_t) mod->init_layout.base < mod->init_layout.size;
return within_module_init(addr, mod);
}

static inline int
static inline bool
in_core (const struct module *mod, uint64_t addr)
{
return addr - (uint64_t) mod->core_layout.base < mod->core_layout.size;
return within_module_core(addr, mod);
}

static inline int
static inline bool
is_internal (const struct module *mod, uint64_t value)
{
return in_init(mod, value) || in_core(mod, value);
Expand Down Expand Up @@ -677,7 +677,8 @@ do_reloc (struct module *mod, uint8_t r_type, Elf64_Sym *sym, uint64_t addend,
break;

case RV_BDREL:
val -= (uint64_t) (in_init(mod, val) ? mod->init_layout.base : mod->core_layout.base);
val -= (uint64_t) (in_init(mod, val) ? mod->mem[MOD_INIT_TEXT].base :
mod->mem[MOD_TEXT].base);
break;

case RV_LTV:
Expand Down Expand Up @@ -812,15 +813,18 @@ apply_relocate_add (Elf64_Shdr *sechdrs, const char *strtab, unsigned int symind
* addresses have been selected...
*/
uint64_t gp;
if (mod->core_layout.size > MAX_LTOFF)
struct module_memory *mod_mem;

mod_mem = &mod->mem[MOD_DATA];
if (mod_mem->size > MAX_LTOFF)
/*
* This takes advantage of fact that SHF_ARCH_SMALL gets allocated
* at the end of the module.
*/
gp = mod->core_layout.size - MAX_LTOFF / 2;
gp = mod_mem->size - MAX_LTOFF / 2;
else
gp = mod->core_layout.size / 2;
gp = (uint64_t) mod->core_layout.base + ((gp + 7) & -8);
gp = mod_mem->size / 2;
gp = (uint64_t) mod_mem->base + ((gp + 7) & -8);
mod->arch.gp = gp;
DEBUGP("%s: placing gp at 0x%lx\n", __func__, gp);
}
Expand Down
11 changes: 5 additions & 6 deletions arch/mips/kernel/vpe.c
Expand Up @@ -199,18 +199,17 @@ static void layout_sections(struct module *mod, const Elf_Ehdr *hdr,
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
for (i = 0; i < hdr->e_shnum; ++i) {
Elf_Shdr *s = &sechdrs[i];
struct module_memory *mod_mem;

mod_mem = &mod->mem[MOD_TEXT];

if ((s->sh_flags & masks[m][0]) != masks[m][0]
|| (s->sh_flags & masks[m][1])
|| s->sh_entsize != ~0UL)
continue;
s->sh_entsize =
get_offset((unsigned long *)&mod->core_layout.size, s);
get_offset((unsigned long *)&mod_mem->size, s);
}

if (m == 0)
mod->core_layout.text_size = mod->core_layout.size;

}
}

Expand Down Expand Up @@ -641,7 +640,7 @@ static int vpe_elfload(struct vpe *v)
layout_sections(&mod, hdr, sechdrs, secstrings);
}

v->load_addr = alloc_progmem(mod.core_layout.size);
v->load_addr = alloc_progmem(mod.mod_mem[MOD_TEXT].size);
if (!v->load_addr)
return -ENOMEM;

Expand Down
51 changes: 17 additions & 34 deletions arch/parisc/kernel/module.c
Expand Up @@ -27,9 +27,9 @@
* We are not doing SEGREL32 handling correctly. According to the ABI, we
* should do a value offset, like this:
* if (in_init(me, (void *)val))
* val -= (uint32_t)me->init_layout.base;
* val -= (uint32_t)me->mem[MOD_INIT_TEXT].base;
* else
* val -= (uint32_t)me->core_layout.base;
* val -= (uint32_t)me->mem[MOD_TEXT].base;
* However, SEGREL32 is used only for PARISC unwind entries, and we want
* those entries to have an absolute address, and not just an offset.
*
Expand Down Expand Up @@ -76,25 +76,6 @@
* allows us to allocate up to 4095 GOT entries. */
#define MAX_GOTS 4095

/* three functions to determine where in the module core
* or init pieces the location is */
static inline int in_init(struct module *me, void *loc)
{
return (loc >= me->init_layout.base &&
loc <= (me->init_layout.base + me->init_layout.size));
}

static inline int in_core(struct module *me, void *loc)
{
return (loc >= me->core_layout.base &&
loc <= (me->core_layout.base + me->core_layout.size));
}

static inline int in_local(struct module *me, void *loc)
{
return in_init(me, loc) || in_core(me, loc);
}

#ifndef CONFIG_64BIT
struct got_entry {
Elf32_Addr addr;
Expand Down Expand Up @@ -302,6 +283,7 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
{
unsigned long gots = 0, fdescs = 0, len;
unsigned int i;
struct module_memory *mod_mem;

len = hdr->e_shnum * sizeof(me->arch.section[0]);
me->arch.section = kzalloc(len, GFP_KERNEL);
Expand Down Expand Up @@ -346,14 +328,15 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
me->arch.section[s].stub_entries += count;
}

mod_mem = &me->mem[MOD_TEXT];
/* align things a bit */
me->core_layout.size = ALIGN(me->core_layout.size, 16);
me->arch.got_offset = me->core_layout.size;
me->core_layout.size += gots * sizeof(struct got_entry);
mod_mem->size = ALIGN(mod_mem->size, 16);
me->arch.got_offset = mod_mem->size;
mod_mem->size += gots * sizeof(struct got_entry);

me->core_layout.size = ALIGN(me->core_layout.size, 16);
me->arch.fdesc_offset = me->core_layout.size;
me->core_layout.size += fdescs * sizeof(Elf_Fdesc);
mod_mem->size = ALIGN(mod_mem->size, 16);
me->arch.fdesc_offset = mod_mem->size;
mod_mem->size += fdescs * sizeof(Elf_Fdesc);

me->arch.got_max = gots;
me->arch.fdesc_max = fdescs;
Expand All @@ -371,7 +354,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)

BUG_ON(value == 0);

got = me->core_layout.base + me->arch.got_offset;
got = me->mem[MOD_TEXT].base + me->arch.got_offset;
for (i = 0; got[i].addr; i++)
if (got[i].addr == value)
goto out;
Expand All @@ -389,7 +372,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
#ifdef CONFIG_64BIT
static Elf_Addr get_fdesc(struct module *me, unsigned long value)
{
Elf_Fdesc *fdesc = me->core_layout.base + me->arch.fdesc_offset;
Elf_Fdesc *fdesc = me->mem[MOD_TEXT].base + me->arch.fdesc_offset;

if (!value) {
printk(KERN_ERR "%s: zero OPD requested!\n", me->name);
Expand All @@ -407,7 +390,7 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value)

/* Create new one */
fdesc->addr = value;
fdesc->gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
fdesc->gp = (Elf_Addr)me->mem[MOD_TEXT].base + me->arch.got_offset;
return (Elf_Addr)fdesc;
}
#endif /* CONFIG_64BIT */
Expand Down Expand Up @@ -742,7 +725,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
loc, val);
val += addend;
/* can we reach it locally? */
if (in_local(me, (void *)val)) {
if (within_module(val, me)) {
/* this is the case where the symbol is local
* to the module, but in a different section,
* so stub the jump in case it's more than 22
Expand Down Expand Up @@ -801,7 +784,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
break;
case R_PARISC_FPTR64:
/* 64-bit function address */
if(in_local(me, (void *)(val + addend))) {
if (within_module(val + addend, me)) {
*loc64 = get_fdesc(me, val+addend);
pr_debug("FDESC for %s at %llx points to %llx\n",
strtab + sym->st_name, *loc64,
Expand Down Expand Up @@ -839,7 +822,7 @@ register_unwind_table(struct module *me,

table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr;
end = table + sechdrs[me->arch.unwind_section].sh_size;
gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
gp = (Elf_Addr)me->mem[MOD_TEXT].base + me->arch.got_offset;

pr_debug("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n",
me->arch.unwind_section, table, end, gp);
Expand Down Expand Up @@ -977,7 +960,7 @@ void module_arch_cleanup(struct module *mod)
#ifdef CONFIG_64BIT
void *dereference_module_function_descriptor(struct module *mod, void *ptr)
{
unsigned long start_opd = (Elf64_Addr)mod->core_layout.base +
unsigned long start_opd = (Elf64_Addr)mod->mem[MOD_TEXT].base +
mod->arch.fdesc_offset;
unsigned long end_opd = start_opd +
mod->arch.fdesc_count * sizeof(Elf64_Fdesc);
Expand Down
7 changes: 3 additions & 4 deletions arch/powerpc/kernel/module_32.c
Expand Up @@ -163,8 +163,7 @@ static uint32_t do_plt_call(void *location,

pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
/* Init, or core PLT? */
if (location >= mod->core_layout.base
&& location < mod->core_layout.base + mod->core_layout.size)
if (within_module_core((unsigned long)location, mod))
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
else
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
Expand Down Expand Up @@ -322,14 +321,14 @@ notrace int module_trampoline_target(struct module *mod, unsigned long addr,

int module_finalize_ftrace(struct module *module, const Elf_Shdr *sechdrs)
{
module->arch.tramp = do_plt_call(module->core_layout.base,
module->arch.tramp = do_plt_call(module->mem[MOD_TEXT].base,
(unsigned long)ftrace_caller,
sechdrs, module);
if (!module->arch.tramp)
return -ENOENT;

#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
module->arch.tramp_regs = do_plt_call(module->core_layout.base,
module->arch.tramp_regs = do_plt_call(module->mem[MOD_TEXT].base,
(unsigned long)ftrace_regs_caller,
sechdrs, module);
if (!module->arch.tramp_regs)
Expand Down

0 comments on commit ac3b432

Please sign in to comment.