diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2003-01-01 03:02:38 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-01-01 03:02:38 -0800 |
| commit | 5b2f80762d125d8a3aa4710129eca6bbbcc4d1ad (patch) | |
| tree | 75a18cd4d5141d0b39ae1df8d35f7352fd33b04f /kernel | |
| parent | 18f7a1aea0dff1bb189913d62a34329689c33a15 (diff) | |
[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()".
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/module.c | 188 |
1 files changed, 90 insertions, 98 deletions
diff --git a/kernel/module.c b/kernel/module.c index 79769b09b3d2..680deb635e30 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1,4 +1,5 @@ /* Rewritten by Rusty Russell, on the backs of many others... + Copyright (C) 2002 Richard Henderson Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM. This program is free software; you can redistribute it and/or modify @@ -27,6 +28,8 @@ #include <linux/rcupdate.h> #include <linux/cpu.h> #include <linux/moduleparam.h> +#include <linux/errno.h> +#include <linux/err.h> #include <asm/uaccess.h> #include <asm/semaphore.h> #include <asm/pgalloc.h> @@ -38,6 +41,13 @@ #define DEBUGP(fmt , a...) #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) \ (strcmp(MODULE_SYMBOL_PREFIX literal, (string)) == 0) @@ -53,13 +63,6 @@ static inline int strong_try_module_get(struct module *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 */ int init_module(void) { @@ -764,43 +767,6 @@ void *__symbol_get(const char *symbol) } 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 */ static int handle_section(const char *name, Elf_Shdr *sechdrs, @@ -902,33 +868,66 @@ static int simplify_symbols(Elf_Shdr *sechdrs, return 0; } -/* Get the total allocation size of the init and non-init sections */ -static struct sizes get_sizes(const Elf_Ehdr *hdr, - const Elf_Shdr *sechdrs, - const char *secstrings) +/* Update size with this section: return offset. */ +static long get_offset(unsigned long *size, Elf_Shdr *sechdr) { - struct sizes ret = { 0, 0 }; - unsigned i; - - /* Everything marked ALLOC (this includes the exported - symbols) */ - for (i = 1; i < hdr->e_shnum; i++) { - unsigned long *add; + long ret; - /* If it's called *.init*, and we're init, we're interested */ - if (strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) - add = &ret.init_size; - else - add = &ret.core_size; + ret = ALIGN(*size, sechdr->sh_addralign ?: 1); + *size = ret + sechdr->sh_size; + return ret; +} - if (sechdrs[i].sh_flags & SHF_ALLOC) { - /* Pad up to required alignment */ - *add = ALIGN(*add, sechdrs[i].sh_addralign ?: 1); - *add += sechdrs[i].sh_size; +/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld + might -- code, read-only data, read-write data, small data. Tally + sizes, and place the offsets into sh_link fields: high bit means it + 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 */ @@ -942,7 +941,6 @@ static struct module *load_module(void *umod, unsigned int i, symindex, exportindex, strindex, setupindex, exindex, modindex, obsparmindex; long arglen; - struct sizes sizes, used; struct module *mod; long err = 0; void *ptr = NULL; /* Stops spurious gcc uninitialized warning */ @@ -1063,23 +1061,15 @@ static struct module *load_module(void *umod, mod->state = MODULE_STATE_COMING; - /* How much space will we need? */ - sizes = get_sizes(hdr, sechdrs, secstrings); - - /* 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); + /* Allow arches to frob section contents and sizes. */ + err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod); if (err < 0) goto free_mod; - mod->init_size = err; - err = module_core_size(hdr, sechdrs, secstrings, mod); - if (err < 0) - goto free_mod; - mod->core_size = err; + /* Determine total sizes, and put offsets in sh_link. For now + this is done generically; there doesn't appear to be any + special cases for the architectures. */ + layout_sections(mod, hdr, sechdrs, secstrings); /* Do the allocs. */ ptr = module_alloc(mod->core_size); @@ -1098,25 +1088,27 @@ static struct module *load_module(void *umod, memset(ptr, 0, mod->init_size); mod->module_init = ptr; - /* Transfer each section which requires ALLOC, and set sh_addr - fields to absolute addresses. */ - used.core_size = 0; - used.init_size = 0; - for (i = 1; i < hdr->e_shnum; i++) { - if (sechdrs[i].sh_flags & SHF_ALLOC) { - ptr = copy_section(secstrings + sechdrs[i].sh_name, - hdr, &sechdrs[i], mod, &used); - if (IS_ERR(ptr)) - goto cleanup; - sechdrs[i].sh_addr = (unsigned long)ptr; - /* Have we just copied __this_module across? */ - if (i == modindex) - mod = ptr; - } + /* Transfer each section which specifies SHF_ALLOC */ + for (i = 0; i < hdr->e_shnum; i++) { + void *dest; + + if (!(sechdrs[i].sh_flags & SHF_ALLOC)) + continue; + + if (sechdrs[i].sh_link & INIT_OFFSET_MASK) + dest = mod->module_init + + (sechdrs[i].sh_link & ~INIT_OFFSET_MASK); + else + dest = mod->module_core + sechdrs[i].sh_link; + + 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! */ - if (used.init_size > mod->init_size || used.core_size > mod->core_size) - BUG(); + /* Module has been moved. */ + mod = (void *)sechdrs[modindex].sh_addr; /* Now we've moved module, initialize linked lists, etc. */ module_unload_init(mod); |
