module: add load_info
Btw, here's a patch that _looks_ large, but it really pretty trivial, and sets things up so that it would be way easier to split off pieces of the module loading. The reason it looks large is that it creates a "module_info" structure that contains all the module state that we're building up while loading, instead of having individual variables for all the indices etc. So the patch ends up being large, because every "symindex" access instead becomes "info.index.sym" etc. That may be a few characters longer, but it then means that we can just pass a pointer to that "info" structure around. and let all the pieces fill it in very naturally. As an example of that, the patch also moves the initialization of all those convenience variables into a "setup_module_info()" function. And at this point it really does become very natural to start to peel off some of the error labels and move them into the helper functions - now the "truncated" case is gone, and is handled inside that setup function instead. So maybe you don't like this approach, and it does make the variable accesses a bit longer, but I don't think unreadably so. And the patch really does look big and scary, but there really should be absolutely no semantic changes - most of it was a trivial and mindless rename. In fact, it was so mindless that I on purpose kept the existing helper functions looking like this: - err = check_modinfo(mod, sechdrs, infoindex, versindex); + err = check_modinfo(mod, info.sechdrs, info.index.info, info.index.vers); rather than changing them to just take the "info" pointer. IOW, a second phase (if you think the approach is ok) would change that calling convention to just do err = check_modinfo(mod, &info); (and same for "layout_sections()", "layout_symtabs()" etc.) Similarly, while right now it makes things _look_ bigger, with things like this: versindex = find_sec(hdr, sechdrs, secstrings, "__versions"); becoming info->index.vers = find_sec(info->hdr, info->sechdrs, info->secstrings, "__versions"); in the new "setup_module_info()" function, that's again just a result of it being a search-and-replace patch. By using the 'info' pointer, we could just change the 'find_sec()' interface so that it ends up being info->index.vers = find_sec(info, "__versions"); instead, and then we'd actually have a shorter and more readable line. So for a lot of those mindless variable name expansions there's would be room for separate cleanups. I didn't move quite everything in there - if we do this to layout_symtabs, for example, we'd want to move the percpu, symoffs, stroffs, *strmap variables to be fields in that module_info structure too. But that's a much smaller patch, I moved just the really core stuff that is currently being set up and used in various parts. But even in this rough form, it removes close to 70 lines from that function (but adds 22 lines overall, of course - the structure definition, the helper function declarations and call-sites etc etc). Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
44032e6316
commit
3264d3f9dd
228
kernel/module.c
228
kernel/module.c
|
@ -2148,8 +2148,17 @@ static inline void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
|
|||
}
|
||||
#endif
|
||||
|
||||
static int copy_and_check(Elf_Ehdr **hdrp,
|
||||
const void __user *umod, unsigned long len)
|
||||
struct load_info {
|
||||
Elf_Ehdr *hdr;
|
||||
unsigned long len;
|
||||
Elf_Shdr *sechdrs;
|
||||
char *secstrings, *args, *strtab;
|
||||
struct {
|
||||
unsigned int sym, str, mod, vers, info, pcpu;
|
||||
} index;
|
||||
};
|
||||
|
||||
static int copy_and_check(struct load_info *info, const void __user *umod, unsigned long len)
|
||||
{
|
||||
int err;
|
||||
Elf_Ehdr *hdr;
|
||||
|
@ -2159,7 +2168,7 @@ static int copy_and_check(Elf_Ehdr **hdrp,
|
|||
|
||||
/* Suck in entire file: we'll want most of it. */
|
||||
/* vmalloc barfs on "unusual" numbers. Check here */
|
||||
if (len > 64 * 1024 * 1024 || (hdr = *hdrp = vmalloc(len)) == NULL)
|
||||
if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(hdr, umod, len) != 0) {
|
||||
|
@ -2181,6 +2190,8 @@ static int copy_and_check(Elf_Ehdr **hdrp,
|
|||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
info->hdr = hdr;
|
||||
info->len = len;
|
||||
return 0;
|
||||
|
||||
free_hdr:
|
||||
|
@ -2188,6 +2199,80 @@ free_hdr:
|
|||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up our basic convenience variables (pointers to section headers,
|
||||
* search for module section index etc), and do some basic section
|
||||
* verification.
|
||||
*
|
||||
* Return the temporary module pointer (we'll replace it with the final
|
||||
* one when we move the module sections around).
|
||||
*/
|
||||
static struct module *setup_load_info(struct load_info *info)
|
||||
{
|
||||
unsigned int i;
|
||||
struct module *mod;
|
||||
|
||||
/* Set up the convenience variables */
|
||||
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
|
||||
info->secstrings = (void *)info->hdr + info->sechdrs[info->hdr->e_shstrndx].sh_offset;
|
||||
info->sechdrs[0].sh_addr = 0;
|
||||
|
||||
for (i = 1; i < info->hdr->e_shnum; i++) {
|
||||
if (info->sechdrs[i].sh_type != SHT_NOBITS
|
||||
&& info->len < info->sechdrs[i].sh_offset + info->sechdrs[i].sh_size)
|
||||
goto truncated;
|
||||
|
||||
/* Mark all sections sh_addr with their address in the
|
||||
temporary image. */
|
||||
info->sechdrs[i].sh_addr = (size_t)info->hdr + info->sechdrs[i].sh_offset;
|
||||
|
||||
/* Internal symbols and strings. */
|
||||
if (info->sechdrs[i].sh_type == SHT_SYMTAB) {
|
||||
info->index.sym = i;
|
||||
info->index.str = info->sechdrs[i].sh_link;
|
||||
info->strtab = (char *)info->hdr + info->sechdrs[info->index.str].sh_offset;
|
||||
}
|
||||
#ifndef CONFIG_MODULE_UNLOAD
|
||||
/* Don't load .exit sections */
|
||||
if (strstarts(info->secstrings+info->sechdrs[i].sh_name, ".exit"))
|
||||
info->sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
#endif
|
||||
}
|
||||
|
||||
info->index.mod = find_sec(info->hdr, info->sechdrs, info->secstrings,
|
||||
".gnu.linkonce.this_module");
|
||||
if (!info->index.mod) {
|
||||
printk(KERN_WARNING "No module found in object\n");
|
||||
return ERR_PTR(-ENOEXEC);
|
||||
}
|
||||
/* This is temporary: point mod into copy of data. */
|
||||
mod = (void *)info->sechdrs[info->index.mod].sh_addr;
|
||||
|
||||
if (info->index.sym == 0) {
|
||||
printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
|
||||
mod->name);
|
||||
return ERR_PTR(-ENOEXEC);
|
||||
}
|
||||
|
||||
info->index.vers = find_sec(info->hdr, info->sechdrs, info->secstrings, "__versions");
|
||||
info->index.info = find_sec(info->hdr, info->sechdrs, info->secstrings, ".modinfo");
|
||||
info->index.pcpu = find_pcpusec(info->hdr, info->sechdrs, info->secstrings);
|
||||
|
||||
/* Don't keep modinfo and version sections. */
|
||||
info->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
|
||||
/* Check module struct version now, before we try to use module. */
|
||||
if (!check_modstruct_version(info->sechdrs, info->index.vers, mod))
|
||||
return ERR_PTR(-ENOEXEC);
|
||||
|
||||
return mod;
|
||||
|
||||
truncated:
|
||||
printk(KERN_ERR "Module len %lu truncated\n", info->len);
|
||||
return ERR_PTR(-ENOEXEC);
|
||||
}
|
||||
|
||||
static int check_modinfo(struct module *mod,
|
||||
const Elf_Shdr *sechdrs,
|
||||
unsigned int infoindex, unsigned int versindex)
|
||||
|
@ -2412,13 +2497,7 @@ static noinline struct module *load_module(void __user *umod,
|
|||
unsigned long len,
|
||||
const char __user *uargs)
|
||||
{
|
||||
Elf_Ehdr *hdr;
|
||||
Elf_Shdr *sechdrs;
|
||||
char *secstrings, *args, *strtab = NULL;
|
||||
unsigned int i;
|
||||
unsigned int symindex = 0;
|
||||
unsigned int strindex = 0;
|
||||
unsigned int modindex, versindex, infoindex, pcpuindex;
|
||||
struct load_info info = { NULL, };
|
||||
struct module *mod;
|
||||
long err;
|
||||
unsigned long symoffs, stroffs, *strmap;
|
||||
|
@ -2429,80 +2508,28 @@ static noinline struct module *load_module(void __user *umod,
|
|||
DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
|
||||
umod, len, uargs);
|
||||
|
||||
err = copy_and_check(&hdr, umod, len);
|
||||
err = copy_and_check(&info, umod, len);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
/* Convenience variables */
|
||||
sechdrs = (void *)hdr + hdr->e_shoff;
|
||||
secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
||||
sechdrs[0].sh_addr = 0;
|
||||
|
||||
for (i = 1; i < hdr->e_shnum; i++) {
|
||||
if (sechdrs[i].sh_type != SHT_NOBITS
|
||||
&& len < sechdrs[i].sh_offset + sechdrs[i].sh_size)
|
||||
goto truncated;
|
||||
|
||||
/* Mark all sections sh_addr with their address in the
|
||||
temporary image. */
|
||||
sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;
|
||||
|
||||
/* Internal symbols and strings. */
|
||||
if (sechdrs[i].sh_type == SHT_SYMTAB) {
|
||||
symindex = i;
|
||||
strindex = sechdrs[i].sh_link;
|
||||
strtab = (char *)hdr + sechdrs[strindex].sh_offset;
|
||||
}
|
||||
#ifndef CONFIG_MODULE_UNLOAD
|
||||
/* Don't load .exit sections */
|
||||
if (strstarts(secstrings+sechdrs[i].sh_name, ".exit"))
|
||||
sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
#endif
|
||||
}
|
||||
|
||||
modindex = find_sec(hdr, sechdrs, secstrings,
|
||||
".gnu.linkonce.this_module");
|
||||
if (!modindex) {
|
||||
printk(KERN_WARNING "No module found in object\n");
|
||||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
/* This is temporary: point mod into copy of data. */
|
||||
mod = (void *)sechdrs[modindex].sh_addr;
|
||||
|
||||
if (symindex == 0) {
|
||||
printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
|
||||
mod->name);
|
||||
err = -ENOEXEC;
|
||||
mod = setup_load_info(&info);
|
||||
if (IS_ERR(mod)) {
|
||||
err = PTR_ERR(mod);
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
|
||||
infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
|
||||
pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
|
||||
|
||||
/* Don't keep modinfo and version sections. */
|
||||
sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
sechdrs[versindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
|
||||
/* Check module struct version now, before we try to use module. */
|
||||
if (!check_modstruct_version(sechdrs, versindex, mod)) {
|
||||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
err = check_modinfo(mod, sechdrs, infoindex, versindex);
|
||||
err = check_modinfo(mod, info.sechdrs, info.index.info, info.index.vers);
|
||||
if (err)
|
||||
goto free_hdr;
|
||||
|
||||
/* Now copy in args */
|
||||
args = strndup_user(uargs, ~0UL >> 1);
|
||||
if (IS_ERR(args)) {
|
||||
err = PTR_ERR(args);
|
||||
info.args = strndup_user(uargs, ~0UL >> 1);
|
||||
if (IS_ERR(info.args)) {
|
||||
err = PTR_ERR(info.args);
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
strmap = kzalloc(BITS_TO_LONGS(sechdrs[strindex].sh_size)
|
||||
strmap = kzalloc(BITS_TO_LONGS(info.sechdrs[info.index.str].sh_size)
|
||||
* sizeof(long), GFP_KERNEL);
|
||||
if (!strmap) {
|
||||
err = -ENOMEM;
|
||||
|
@ -2512,17 +2539,17 @@ static noinline struct module *load_module(void __user *umod,
|
|||
mod->state = MODULE_STATE_COMING;
|
||||
|
||||
/* Allow arches to frob section contents and sizes. */
|
||||
err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
|
||||
err = module_frob_arch_sections(info.hdr, info.sechdrs, info.secstrings, mod);
|
||||
if (err < 0)
|
||||
goto free_mod;
|
||||
|
||||
if (pcpuindex) {
|
||||
if (info.index.pcpu) {
|
||||
/* We have a special allocation for this section. */
|
||||
err = percpu_modalloc(mod, sechdrs[pcpuindex].sh_size,
|
||||
sechdrs[pcpuindex].sh_addralign);
|
||||
err = percpu_modalloc(mod, info.sechdrs[info.index.pcpu].sh_size,
|
||||
info.sechdrs[info.index.pcpu].sh_addralign);
|
||||
if (err)
|
||||
goto free_mod;
|
||||
sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
info.sechdrs[info.index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
||||
}
|
||||
/* Keep this around for failure path. */
|
||||
percpu = mod_percpu(mod);
|
||||
|
@ -2530,12 +2557,12 @@ static noinline struct module *load_module(void __user *umod,
|
|||
/* Determine total sizes, and put offsets in sh_entsize. For now
|
||||
this is done generically; there doesn't appear to be any
|
||||
special cases for the architectures. */
|
||||
layout_sections(mod, hdr, sechdrs, secstrings);
|
||||
symoffs = layout_symtab(mod, sechdrs, symindex, strindex, hdr,
|
||||
secstrings, &stroffs, strmap);
|
||||
layout_sections(mod, info.hdr, info.sechdrs, info.secstrings);
|
||||
symoffs = layout_symtab(mod, info.sechdrs, info.index.sym, info.index.str, info.hdr,
|
||||
info.secstrings, &stroffs, strmap);
|
||||
|
||||
/* Allocate and move to the final place */
|
||||
mod = move_module(mod, hdr, sechdrs, secstrings, modindex);
|
||||
mod = move_module(mod, info.hdr, info.sechdrs, info.secstrings, info.index.mod);
|
||||
if (IS_ERR(mod)) {
|
||||
err = PTR_ERR(mod);
|
||||
goto free_percpu;
|
||||
|
@ -2548,50 +2575,50 @@ static noinline struct module *load_module(void __user *umod,
|
|||
|
||||
/* Now we've got everything in the final locations, we can
|
||||
* find optional sections. */
|
||||
find_module_sections(mod, hdr, sechdrs, secstrings);
|
||||
find_module_sections(mod, info.hdr, info.sechdrs, info.secstrings);
|
||||
|
||||
err = check_module_license_and_versions(mod, sechdrs);
|
||||
err = check_module_license_and_versions(mod, info.sechdrs);
|
||||
if (err)
|
||||
goto free_unload;
|
||||
|
||||
/* Set up MODINFO_ATTR fields */
|
||||
setup_modinfo(mod, sechdrs, infoindex);
|
||||
setup_modinfo(mod, info.sechdrs, info.index.info);
|
||||
|
||||
/* Fix up syms, so that st_value is a pointer to location. */
|
||||
err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
|
||||
err = simplify_symbols(info.sechdrs, info.index.sym, info.strtab, info.index.vers, info.index.pcpu,
|
||||
mod);
|
||||
if (err < 0)
|
||||
goto cleanup;
|
||||
|
||||
err = apply_relocations(mod, hdr, sechdrs, symindex, strindex);
|
||||
err = apply_relocations(mod, info.hdr, info.sechdrs, info.index.sym, info.index.str);
|
||||
if (err < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Set up and sort exception table */
|
||||
mod->extable = section_objs(hdr, sechdrs, secstrings, "__ex_table",
|
||||
mod->extable = section_objs(info.hdr, info.sechdrs, info.secstrings, "__ex_table",
|
||||
sizeof(*mod->extable), &mod->num_exentries);
|
||||
sort_extable(mod->extable, mod->extable + mod->num_exentries);
|
||||
|
||||
/* Finally, copy percpu area over. */
|
||||
percpu_modcopy(mod, (void *)sechdrs[pcpuindex].sh_addr,
|
||||
sechdrs[pcpuindex].sh_size);
|
||||
percpu_modcopy(mod, (void *)info.sechdrs[info.index.pcpu].sh_addr,
|
||||
info.sechdrs[info.index.pcpu].sh_size);
|
||||
|
||||
add_kallsyms(mod, sechdrs, hdr->e_shnum, symindex, strindex,
|
||||
symoffs, stroffs, secstrings, strmap);
|
||||
add_kallsyms(mod, info.sechdrs, info.hdr->e_shnum, info.index.sym, info.index.str,
|
||||
symoffs, stroffs, info.secstrings, strmap);
|
||||
kfree(strmap);
|
||||
strmap = NULL;
|
||||
|
||||
if (!mod->taints)
|
||||
debug = section_objs(hdr, sechdrs, secstrings, "__verbose",
|
||||
debug = section_objs(info.hdr, info.sechdrs, info.secstrings, "__verbose",
|
||||
sizeof(*debug), &num_debug);
|
||||
|
||||
err = module_finalize(hdr, sechdrs, mod);
|
||||
err = module_finalize(info.hdr, info.sechdrs, mod);
|
||||
if (err < 0)
|
||||
goto cleanup;
|
||||
|
||||
flush_module_icache(mod);
|
||||
|
||||
mod->args = args;
|
||||
mod->args = info.args;
|
||||
|
||||
/* Now sew it into the lists so we can get lockdep and oops
|
||||
* info during argument parsing. Noone should access us, since
|
||||
|
@ -2625,11 +2652,11 @@ static noinline struct module *load_module(void __user *umod,
|
|||
if (err < 0)
|
||||
goto unlink;
|
||||
|
||||
add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
||||
add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
||||
add_sect_attrs(mod, info.hdr->e_shnum, info.secstrings, info.sechdrs);
|
||||
add_notes_attrs(mod, info.hdr->e_shnum, info.secstrings, info.sechdrs);
|
||||
|
||||
/* Get rid of temporary copy */
|
||||
vfree(hdr);
|
||||
vfree(info.hdr);
|
||||
|
||||
trace_module_load(mod);
|
||||
|
||||
|
@ -2657,16 +2684,11 @@ static noinline struct module *load_module(void __user *umod,
|
|||
free_percpu:
|
||||
free_percpu(percpu);
|
||||
free_mod:
|
||||
kfree(args);
|
||||
kfree(info.args);
|
||||
kfree(strmap);
|
||||
free_hdr:
|
||||
vfree(hdr);
|
||||
vfree(info.hdr);
|
||||
return ERR_PTR(err);
|
||||
|
||||
truncated:
|
||||
printk(KERN_ERR "Module len %lu truncated\n", len);
|
||||
err = -ENOEXEC;
|
||||
goto free_hdr;
|
||||
}
|
||||
|
||||
/* Call module constructors. */
|
||||
|
|
Loading…
Reference in New Issue