Commit 5b2f8076 authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[PATCH] Modules 3/3: Sort sections

RTH's final complaint (so far 8) was that we should sort the module
sections: archs might require some sections to be adjacent, so they can
all be reached by a relative pointer (ie.  GOT pointer).  This
implements that reordering, and simplfies the module interface for
architectures as well.

Previously an arch could specify it wanted extra space, but not where
that space would be.  The new method (used only by PPC so far) is to
allocate an empty section (in asm/module.h or by setting LDFLAGS_MODULE
to use an arch specific linker script), and expand that to the desired
size in "module_frob_arch_sections()".
parent 18f7a1ae
...@@ -67,18 +67,12 @@ void module_free(struct module *module, void *region) ...@@ -67,18 +67,12 @@ void module_free(struct module *module, void *region)
vfree(region); vfree(region);
} }
long int module_frob_arch_sections(const Elf_Ehdr *hdr,
module_core_size(const Elf32_Ehdr *hdr, const Elf32_Shdr *sechdrs, const Elf_Shdr *sechdrs,
const char *secstrings, struct module *module) const char *secstrings,
struct module *mod)
{ {
return module->core_size; return 0;
}
long
module_init_size(const Elf32_Ehdr *hdr, const Elf32_Shdr *sechdrs,
const char *secstrings, struct module *module)
{
return module->init_size;
} }
int int
......
...@@ -45,20 +45,12 @@ void module_free(struct module *mod, void *module_region) ...@@ -45,20 +45,12 @@ void module_free(struct module *mod, void *module_region)
} }
/* We don't need anything special. */ /* We don't need anything special. */
long module_core_size(const Elf32_Ehdr *hdr, int module_frob_arch_sections(const Elf_Ehdr *hdr,
const Elf32_Shdr *sechdrs, const Elf_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->core_size;
}
long module_init_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *module) struct module *mod)
{ {
return module->init_size; return 0;
} }
int apply_relocate(Elf32_Shdr *sechdrs, int apply_relocate(Elf32_Shdr *sechdrs,
......
...@@ -101,24 +101,31 @@ static unsigned long get_plt_size(const Elf32_Ehdr *hdr, ...@@ -101,24 +101,31 @@ static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
return ret; return ret;
} }
long module_core_size(const Elf32_Ehdr *hdr, int module_frob_arch_sections(Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs, Elf32_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *module) struct module *me)
{ {
module->arch.core_plt_offset = ALIGN(module->core_size, 4); unsigned int i;
return module->arch.core_plt_offset
+ get_plt_size(hdr, sechdrs, secstrings, 0);
}
long module_init_size(const Elf32_Ehdr *hdr, /* Find .plt and .pltinit sections */
const Elf32_Shdr *sechdrs, for (i = 0; i < hdr->e_shnum; i++) {
const char *secstrings, if (strcmp(secstrings + sechdrs[i].sh_name, ".plt.init") == 0)
struct module *module) me->arch.init_plt_section = i;
{ else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0)
module->arch.init_plt_offset = ALIGN(module->init_size, 4); me->arch.core_plt_section = i;
return module->arch.init_plt_offset }
+ get_plt_size(hdr, sechdrs, secstrings, 1); if (!me->arch.core_plt_section || !me->arch.init_plt_section) {
printk("Module doesn't contain .plt or .plt.init sections.\n");
return -ENOEXEC;
}
/* Override their sizes */
sechdrs[me->arch.core_plt_section].sh_size
= get_plt_size(hdr, sechdrs, secstrings, 0);
sechdrs[me->arch.init_plt_section].sh_size
= get_plt_size(hdr, sechdrs, secstrings, 1);
return 0;
} }
int apply_relocate(Elf32_Shdr *sechdrs, int apply_relocate(Elf32_Shdr *sechdrs,
...@@ -141,17 +148,20 @@ static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val) ...@@ -141,17 +148,20 @@ static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val)
} }
/* Set up a trampoline in the PLT to bounce us to the distant function */ /* Set up a trampoline in the PLT to bounce us to the distant function */
static uint32_t do_plt_call(void *location, Elf32_Addr val, struct module *mod) static uint32_t do_plt_call(void *location,
Elf32_Addr val,
Elf32_Shdr *sechdrs,
struct module *mod)
{ {
struct ppc_plt_entry *entry; struct ppc_plt_entry *entry;
DEBUGP("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location); DEBUGP("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
/* Init, or core PLT? */ /* Init, or core PLT? */
if (location >= mod->module_core if (location >= mod->module_core
&& location < mod->module_core + mod->arch.core_plt_offset) && location < mod->module_core + mod->core_size)
entry = mod->module_core + mod->arch.core_plt_offset; entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
else else
entry = mod->module_init + mod->arch.init_plt_offset; entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
/* Find this entry, or if that fails, the next avail. entry */ /* Find this entry, or if that fails, the next avail. entry */
while (entry->jump[0]) { while (entry->jump[0]) {
...@@ -220,7 +230,8 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, ...@@ -220,7 +230,8 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
case R_PPC_REL24: case R_PPC_REL24:
if ((int)(value - (uint32_t)location) < -0x02000000 if ((int)(value - (uint32_t)location) < -0x02000000
|| (int)(value - (uint32_t)location) >= 0x02000000) || (int)(value - (uint32_t)location) >= 0x02000000)
value = do_plt_call(location, value, module); value = do_plt_call(location, value,
sechdrs, module);
/* Only replace bits 2 through 26 */ /* Only replace bits 2 through 26 */
DEBUGP("REL24 value = %08X. location = %08X\n", DEBUGP("REL24 value = %08X. location = %08X\n",
......
...@@ -51,26 +51,15 @@ void module_free(struct module *mod, void *module_region) ...@@ -51,26 +51,15 @@ void module_free(struct module *mod, void *module_region)
table entries. */ table entries. */
} }
/* s390/s390x needs additional memory for GOT/PLT sections. */ int module_frob_arch_sections(const Elf_Ehdr *hdr,
long module_core_size(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
const Elf32_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *module) struct module *mod)
{ {
// FIXME: add space needed for GOT/PLT // FIXME: add space needed for GOT/PLT
return module->core_size; return 0;
}
long module_init_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->init_size;
} }
int apply_relocate(Elf_Shdr *sechdrs, int apply_relocate(Elf_Shdr *sechdrs,
const char *strtab, const char *strtab,
unsigned int symindex, unsigned int symindex,
......
...@@ -52,25 +52,15 @@ void module_free(struct module *mod, void *module_region) ...@@ -52,25 +52,15 @@ void module_free(struct module *mod, void *module_region)
} }
/* s390/s390x needs additional memory for GOT/PLT sections. */ /* s390/s390x needs additional memory for GOT/PLT sections. */
long module_core_size(const Elf32_Ehdr *hdr, int module_frob_arch_sections(const Elf_Ehdr *hdr,
const Elf32_Shdr *sechdrs, const Elf_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *module) struct module *mod)
{ {
// FIXME: add space needed for GOT/PLT // FIXME: add space needed for GOT/PLT
return module->core_size; return 0;
}
long module_init_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->init_size;
} }
int apply_relocate(Elf_Shdr *sechdrs, int apply_relocate(Elf_Shdr *sechdrs,
const char *strtab, const char *strtab,
unsigned int symindex, unsigned int symindex,
......
...@@ -37,20 +37,12 @@ void module_free(struct module *mod, void *module_region) ...@@ -37,20 +37,12 @@ void module_free(struct module *mod, void *module_region)
} }
/* We don't need anything special. */ /* We don't need anything special. */
long module_core_size(const Elf32_Ehdr *hdr, int module_frob_arch_sections(const Elf_Ehdr *hdr,
const Elf32_Shdr *sechdrs, const Elf_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->core_size;
}
long module_init_size(const Elf32_Ehdr *hdr,
const Elf32_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *module) struct module *mod)
{ {
return module->init_size; return 0;
} }
int apply_relocate(Elf32_Shdr *sechdrs, int apply_relocate(Elf32_Shdr *sechdrs,
......
...@@ -144,20 +144,12 @@ void module_free(struct module *mod, void *module_region) ...@@ -144,20 +144,12 @@ void module_free(struct module *mod, void *module_region)
} }
/* We don't need anything special. */ /* We don't need anything special. */
long module_core_size(const Elf64_Ehdr *hdr, int module_frob_arch_sections(const Elf_Ehdr *hdr,
const Elf64_Shdr *sechdrs, const Elf_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->core_size;
}
long module_init_size(const Elf64_Ehdr *hdr,
const Elf64_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *module) struct module *mod)
{ {
return module->init_size; return 0;
} }
int apply_relocate(Elf64_Shdr *sechdrs, int apply_relocate(Elf64_Shdr *sechdrs,
......
...@@ -26,20 +26,12 @@ ...@@ -26,20 +26,12 @@
#define DEBUGP(fmt...) #define DEBUGP(fmt...)
/* We don't need anything special. */ /* We don't need anything special. */
long module_core_size(const Elf64_Ehdr *hdr, int module_frob_arch_sections(const Elf_Ehdr *hdr,
const Elf64_Shdr *sechdrs, const Elf_Shdr *sechdrs,
const char *secstrings,
struct module *module)
{
return module->core_size;
}
long module_init_size(const Elf64_Ehdr *hdr,
const Elf64_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *module) struct module *mod)
{ {
return module->init_size; return 0;
} }
int apply_relocate_add(Elf64_Shdr *sechdrs, int apply_relocate_add(Elf64_Shdr *sechdrs,
......
...@@ -18,16 +18,17 @@ struct ppc_plt_entry ...@@ -18,16 +18,17 @@ struct ppc_plt_entry
struct mod_arch_specific struct mod_arch_specific
{ {
/* How much of the core is actually taken up with core (then /* Indices of PLT sections within module. */
we know the rest is for the PLT */ unsigned int core_plt_section, init_plt_section;
unsigned int core_plt_offset;
/* Same for init */
unsigned int init_plt_offset;
}; };
#define Elf_Shdr Elf32_Shdr #define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym #define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr #define Elf_Ehdr Elf32_Ehdr
/* Make empty sections for module_frob_arch_sections to expand. */
#ifdef MODULE
asm(".section .plt,\"aws\",@nobits; .align 3; .previous");
asm(".section .plt.init,\"aws\",@nobits; .align 3; .previous");
#endif
#endif /* _ASM_PPC_MODULE_H */ #endif /* _ASM_PPC_MODULE_H */
...@@ -15,17 +15,8 @@ unsigned long find_symbol_internal(Elf_Shdr *sechdrs, ...@@ -15,17 +15,8 @@ unsigned long find_symbol_internal(Elf_Shdr *sechdrs,
/* These must be implemented by the specific architecture */ /* These must be implemented by the specific architecture */
/* Total size to allocate for the non-releasable code; return len or /* Adjust arch-specific sections. Return 0 on success. */
-error. mod->core_size is the current generic tally. */ int module_frob_arch_sections(const Elf_Ehdr *hdr,
long module_core_size(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
const char *secstrings,
struct module *mod);
/* Total size of (if any) sections to be freed after init. Return 0
for none, len, or -error. mod->init_size is the current generic
tally. */
long module_init_size(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs, const Elf_Shdr *sechdrs,
const char *secstrings, const char *secstrings,
struct module *mod); struct module *mod);
......
/* Rewritten by Rusty Russell, on the backs of many others... /* Rewritten by Rusty Russell, on the backs of many others...
Copyright (C) 2002 Richard Henderson
Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM. Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
...@@ -27,6 +28,8 @@ ...@@ -27,6 +28,8 @@
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
...@@ -38,6 +41,13 @@ ...@@ -38,6 +41,13 @@
#define DEBUGP(fmt , a...) #define DEBUGP(fmt , a...)
#endif #endif
#ifndef ARCH_SHF_SMALL
#define ARCH_SHF_SMALL 0
#endif
/* If this is set, the section belongs in the init part of the module */
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
#define symbol_is(literal, string) \ #define symbol_is(literal, string) \
(strcmp(MODULE_SYMBOL_PREFIX literal, (string)) == 0) (strcmp(MODULE_SYMBOL_PREFIX literal, (string)) == 0)
...@@ -53,13 +63,6 @@ static inline int strong_try_module_get(struct module *mod) ...@@ -53,13 +63,6 @@ static inline int strong_try_module_get(struct module *mod)
return try_module_get(mod); return try_module_get(mod);
} }
/* Convenient structure for holding init and core sizes */
struct sizes
{
unsigned long init_size;
unsigned long core_size;
};
/* Stub function for modules which don't have an initfn */ /* Stub function for modules which don't have an initfn */
int init_module(void) int init_module(void)
{ {
...@@ -764,43 +767,6 @@ void *__symbol_get(const char *symbol) ...@@ -764,43 +767,6 @@ void *__symbol_get(const char *symbol)
} }
EXPORT_SYMBOL_GPL(__symbol_get); EXPORT_SYMBOL_GPL(__symbol_get);
/* Transfer one ELF section to the correct (init or core) area. */
static void *copy_section(const char *name,
void *base,
Elf_Shdr *sechdr,
struct module *mod,
struct sizes *used)
{
void *dest;
unsigned long *use;
unsigned long max;
/* Only copy to init section if there is one */
if (strstr(name, ".init") && mod->module_init) {
dest = mod->module_init;
use = &used->init_size;
max = mod->init_size;
} else {
dest = mod->module_core;
use = &used->core_size;
max = mod->core_size;
}
/* Align up */
*use = ALIGN(*use, sechdr->sh_addralign);
dest += *use;
*use += sechdr->sh_size;
if (*use > max)
return ERR_PTR(-ENOEXEC);
/* May not actually be in the file (eg. bss). */
if (sechdr->sh_type != SHT_NOBITS)
memcpy(dest, base + sechdr->sh_offset, sechdr->sh_size);
return dest;
}
/* Deal with the given section */ /* Deal with the given section */
static int handle_section(const char *name, static int handle_section(const char *name,
Elf_Shdr *sechdrs, Elf_Shdr *sechdrs,
...@@ -902,33 +868,66 @@ static int simplify_symbols(Elf_Shdr *sechdrs, ...@@ -902,33 +868,66 @@ static int simplify_symbols(Elf_Shdr *sechdrs,
return 0; return 0;
} }
/* Get the total allocation size of the init and non-init sections */ /* Update size with this section: return offset. */
static struct sizes get_sizes(const Elf_Ehdr *hdr, static long get_offset(unsigned long *size, Elf_Shdr *sechdr)
const Elf_Shdr *sechdrs,
const char *secstrings)
{ {
struct sizes ret = { 0, 0 }; long ret;
unsigned i;
/* Everything marked ALLOC (this includes the exported
symbols) */
for (i = 1; i < hdr->e_shnum; i++) {
unsigned long *add;
/* If it's called *.init*, and we're init, we're interested */ ret = ALIGN(*size, sechdr->sh_addralign ?: 1);
if (strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) *size = ret + sechdr->sh_size;
add = &ret.init_size; return ret;
else }
add = &ret.core_size;
if (sechdrs[i].sh_flags & SHF_ALLOC) { /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
/* Pad up to required alignment */ might -- code, read-only data, read-write data, small data. Tally
*add = ALIGN(*add, sechdrs[i].sh_addralign ?: 1); sizes, and place the offsets into sh_link fields: high bit means it
*add += sechdrs[i].sh_size; belongs in init. */
static void layout_sections(struct module *mod,
const Elf_Ehdr *hdr,
Elf_Shdr *sechdrs,
const char *secstrings)
{
static unsigned long const masks[][2] = {
{ SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },
{ SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },
{ SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },
{ ARCH_SHF_SMALL | SHF_ALLOC, 0 }
};
unsigned int m, i;
for (i = 0; i < hdr->e_shnum; i++)
sechdrs[i].sh_link = ~0UL;
DEBUGP("Core section allocation order:\n");
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
for (i = 0; i < hdr->e_shnum; ++i) {
Elf_Shdr *s = &sechdrs[i];
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|| (s->sh_flags & masks[m][1])
|| s->sh_link != ~0UL
|| strstr(secstrings + s->sh_name, ".init"))
continue;
s->sh_link = get_offset(&mod->core_size, s);
DEBUGP("\t%s\n", name);
} }
} }
return ret; DEBUGP("Init section allocation order:\n");
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
for (i = 0; i < hdr->e_shnum; ++i) {
Elf_Shdr *s = &sechdrs[i];
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|| (s->sh_flags & masks[m][1])
|| s->sh_link != ~0UL
|| !strstr(secstrings + s->sh_name, ".init"))
continue;
s->sh_link = (get_offset(&mod->init_size, s)
| INIT_OFFSET_MASK);
DEBUGP("\t%s\n", name);
}
}
} }
/* Allocate and load the module */ /* Allocate and load the module */
...@@ -942,7 +941,6 @@ static struct module *load_module(void *umod, ...@@ -942,7 +941,6 @@ static struct module *load_module(void *umod,
unsigned int i, symindex, exportindex, strindex, setupindex, exindex, unsigned int i, symindex, exportindex, strindex, setupindex, exindex,
modindex, obsparmindex; modindex, obsparmindex;
long arglen; long arglen;
struct sizes sizes, used;
struct module *mod; struct module *mod;
long err = 0; long err = 0;
void *ptr = NULL; /* Stops spurious gcc uninitialized warning */ void *ptr = NULL; /* Stops spurious gcc uninitialized warning */
...@@ -1063,23 +1061,15 @@ static struct module *load_module(void *umod, ...@@ -1063,23 +1061,15 @@ static struct module *load_module(void *umod,
mod->state = MODULE_STATE_COMING; mod->state = MODULE_STATE_COMING;
/* How much space will we need? */ /* Allow arches to frob section contents and sizes. */
sizes = get_sizes(hdr, sechdrs, secstrings); err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
/* Set these up, and allow archs to manipulate them. */
mod->core_size = sizes.core_size;
mod->init_size = sizes.init_size;
/* Allow archs to add to them. */
err = module_init_size(hdr, sechdrs, secstrings, mod);
if (err < 0) if (err < 0)
goto free_mod; goto free_mod;
mod->init_size = err;
err = module_core_size(hdr, sechdrs, secstrings, mod); /* Determine total sizes, and put offsets in sh_link. For now
if (err < 0) this is done generically; there doesn't appear to be any
goto free_mod; special cases for the architectures. */
mod->core_size = err; layout_sections(mod, hdr, sechdrs, secstrings);
/* Do the allocs. */ /* Do the allocs. */
ptr = module_alloc(mod->core_size); ptr = module_alloc(mod->core_size);
...@@ -1098,25 +1088,27 @@ static struct module *load_module(void *umod, ...@@ -1098,25 +1088,27 @@ static struct module *load_module(void *umod,
memset(ptr, 0, mod->init_size); memset(ptr, 0, mod->init_size);
mod->module_init = ptr; mod->module_init = ptr;
/* Transfer each section which requires ALLOC, and set sh_addr /* Transfer each section which specifies SHF_ALLOC */
fields to absolute addresses. */ for (i = 0; i < hdr->e_shnum; i++) {
used.core_size = 0; void *dest;
used.init_size = 0;
for (i = 1; i < hdr->e_shnum; i++) { if (!(sechdrs[i].sh_flags & SHF_ALLOC))
if (sechdrs[i].sh_flags & SHF_ALLOC) { continue;
ptr = copy_section(secstrings + sechdrs[i].sh_name,
hdr, &sechdrs[i], mod, &used); if (sechdrs[i].sh_link & INIT_OFFSET_MASK)
if (IS_ERR(ptr)) dest = mod->module_init
goto cleanup; + (sechdrs[i].sh_link & ~INIT_OFFSET_MASK);
sechdrs[i].sh_addr = (unsigned long)ptr; else
/* Have we just copied __this_module across? */ dest = mod->module_core + sechdrs[i].sh_link;
if (i == modindex)
mod = ptr; if (sechdrs[i].sh_type != SHT_NOBITS)
} memcpy(dest, (void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size);
/* Update sh_addr to point to copy in image. */
sechdrs[i].sh_addr = (unsigned long)dest;
} }
/* Don't use more than we allocated! */ /* Module has been moved. */
if (used.init_size > mod->init_size || used.core_size > mod->core_size) mod = (void *)sechdrs[modindex].sh_addr;
BUG();
/* Now we've moved module, initialize linked lists, etc. */ /* Now we've moved module, initialize linked lists, etc. */
module_unload_init(mod); module_unload_init(mod);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment