}
EXPORT_SYMBOL(unregister_module_notifier);
+struct load_info {
+ Elf_Ehdr *hdr;
+ unsigned long len;
+ Elf_Shdr *sechdrs;
+ char *secstrings, *args, *strtab;
+ unsigned long *strmap;
+ unsigned long symoffs, stroffs;
+ struct {
+ unsigned int sym, str, mod, vers, info, pcpu;
+ } index;
+};
+
/* We require a truly strong try_module_get(): 0 means failure due to
ongoing or failed initialization etc. */
static inline int strong_try_module_get(struct module *mod)
mod->percpu = __alloc_reserved_percpu(size, align);
if (!mod->percpu) {
printk(KERN_WARNING
- "Could not allocate %lu bytes percpu data\n", size);
+ "%s: Could not allocate %lu bytes percpu data\n",
+ mod->name, size);
return -ENOMEM;
}
mod->percpu_size = size;
* /sys/module/foo/sections stuff
* J. Corbet <corbet@lwn.net>
*/
-#if defined(CONFIG_KALLSYMS) && defined(CONFIG_SYSFS)
+#ifdef CONFIG_SYSFS
+#ifdef CONFIG_KALLSYMS
static inline bool sect_empty(const Elf_Shdr *sect)
{
return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0;
kfree(sect_attrs);
}
-static void add_sect_attrs(struct module *mod, unsigned int nsect,
- char *secstrings, Elf_Shdr *sechdrs)
+static void add_sect_attrs(struct module *mod, const struct load_info *info)
{
unsigned int nloaded = 0, i, size[2];
struct module_sect_attrs *sect_attrs;
struct attribute **gattr;
/* Count loaded sections and allocate structures */
- for (i = 0; i < nsect; i++)
- if (!sect_empty(&sechdrs[i]))
+ for (i = 0; i < info->hdr->e_shnum; i++)
+ if (!sect_empty(&info->sechdrs[i]))
nloaded++;
size[0] = ALIGN(sizeof(*sect_attrs)
+ nloaded * sizeof(sect_attrs->attrs[0]),
sect_attrs->nsections = 0;
sattr = §_attrs->attrs[0];
gattr = §_attrs->grp.attrs[0];
- for (i = 0; i < nsect; i++) {
- if (sect_empty(&sechdrs[i]))
+ for (i = 0; i < info->hdr->e_shnum; i++) {
+ Elf_Shdr *sec = &info->sechdrs[i];
+ if (sect_empty(sec))
continue;
- sattr->address = sechdrs[i].sh_addr;
- sattr->name = kstrdup(secstrings + sechdrs[i].sh_name,
+ sattr->address = sec->sh_addr;
+ sattr->name = kstrdup(info->secstrings + sec->sh_name,
GFP_KERNEL);
if (sattr->name == NULL)
goto out;
kfree(notes_attrs);
}
-static void add_notes_attrs(struct module *mod, unsigned int nsect,
- char *secstrings, Elf_Shdr *sechdrs)
+static void add_notes_attrs(struct module *mod, const struct load_info *info)
{
unsigned int notes, loaded, i;
struct module_notes_attrs *notes_attrs;
/* Count notes sections and allocate structures. */
notes = 0;
- for (i = 0; i < nsect; i++)
- if (!sect_empty(&sechdrs[i]) &&
- (sechdrs[i].sh_type == SHT_NOTE))
+ for (i = 0; i < info->hdr->e_shnum; i++)
+ if (!sect_empty(&info->sechdrs[i]) &&
+ (info->sechdrs[i].sh_type == SHT_NOTE))
++notes;
if (notes == 0)
notes_attrs->notes = notes;
nattr = ¬es_attrs->attrs[0];
- for (loaded = i = 0; i < nsect; ++i) {
- if (sect_empty(&sechdrs[i]))
+ for (loaded = i = 0; i < info->hdr->e_shnum; ++i) {
+ if (sect_empty(&info->sechdrs[i]))
continue;
- if (sechdrs[i].sh_type == SHT_NOTE) {
+ if (info->sechdrs[i].sh_type == SHT_NOTE) {
sysfs_bin_attr_init(nattr);
nattr->attr.name = mod->sect_attrs->attrs[loaded].name;
nattr->attr.mode = S_IRUGO;
- nattr->size = sechdrs[i].sh_size;
- nattr->private = (void *) sechdrs[i].sh_addr;
+ nattr->size = info->sechdrs[i].sh_size;
+ nattr->private = (void *) info->sechdrs[i].sh_addr;
nattr->read = module_notes_read;
++nattr;
}
#else
-static inline void add_sect_attrs(struct module *mod, unsigned int nsect,
- char *sectstrings, Elf_Shdr *sechdrs)
+static inline void add_sect_attrs(struct module *mod,
+ const struct load_info *info)
{
}
{
}
-static inline void add_notes_attrs(struct module *mod, unsigned int nsect,
- char *sectstrings, Elf_Shdr *sechdrs)
+static inline void add_notes_attrs(struct module *mod,
+ const struct load_info *info)
{
}
static inline void remove_notes_attrs(struct module *mod)
{
}
-#endif
+#endif /* CONFIG_KALLSYMS */
-#ifdef CONFIG_SYSFS
static void add_usage_links(struct module *mod)
{
#ifdef CONFIG_MODULE_UNLOAD
}
static int mod_sysfs_setup(struct module *mod,
+ const struct load_info *info,
struct kernel_param *kparam,
unsigned int num_params)
{
goto out_unreg_param;
add_usage_links(mod);
+ add_sect_attrs(mod, info);
+ add_notes_attrs(mod, info);
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
return 0;
static void mod_sysfs_fini(struct module *mod)
{
+ remove_notes_attrs(mod);
+ remove_sect_attrs(mod);
kobject_put(&mod->mkobj.kobj);
}
-#else /* CONFIG_SYSFS */
+#else /* !CONFIG_SYSFS */
-static inline int mod_sysfs_init(struct module *mod)
-{
- return 0;
-}
-
-static inline int mod_sysfs_setup(struct module *mod,
+static int mod_sysfs_setup(struct module *mod,
+ const struct load_info *info,
struct kernel_param *kparam,
unsigned int num_params)
{
return 0;
}
-static inline int module_add_modinfo_attrs(struct module *mod)
-{
- return 0;
-}
-
-static inline void module_remove_modinfo_attrs(struct module *mod)
+static void mod_sysfs_fini(struct module *mod)
{
}
-static void mod_sysfs_fini(struct module *mod)
+static void module_remove_modinfo_attrs(struct module *mod)
{
}
#endif /* CONFIG_SYSFS */
-static void mod_kobject_remove(struct module *mod)
+static void mod_sysfs_teardown(struct module *mod)
{
del_usage_links(mod);
module_remove_modinfo_attrs(mod);
mutex_lock(&module_mutex);
stop_machine(__unlink_module, mod, NULL);
mutex_unlock(&module_mutex);
- remove_notes_attrs(mod);
- remove_sect_attrs(mod);
- mod_kobject_remove(mod);
+ mod_sysfs_teardown(mod);
/* Remove dynamic debug info */
ddebug_remove_module(mod->name);
}
/* As per nm */
-static char elf_type(const Elf_Sym *sym,
- Elf_Shdr *sechdrs,
- const char *secstrings,
- struct module *mod)
+static char elf_type(const Elf_Sym *sym, const struct load_info *info)
{
+ const Elf_Shdr *sechdrs = info->sechdrs;
+
if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)
return 'v';
else
return 'b';
}
- if (strstarts(secstrings + sechdrs[sym->st_shndx].sh_name, ".debug"))
+ if (strstarts(info->secstrings + sechdrs[sym->st_shndx].sh_name,
+ ".debug")) {
return 'n';
+ }
return '?';
}
return symoffs;
}
-static void add_kallsyms(struct module *mod,
- Elf_Shdr *sechdrs,
- unsigned int shnum,
- unsigned int symindex,
- unsigned int strindex,
- unsigned long symoffs,
- unsigned long stroffs,
- const char *secstrings,
- unsigned long *strmap)
+static void add_kallsyms(struct module *mod, struct load_info *info)
{
unsigned int i, ndst;
const Elf_Sym *src;
Elf_Sym *dst;
char *s;
+ Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
- mod->symtab = (void *)sechdrs[symindex].sh_addr;
- mod->num_symtab = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
- mod->strtab = (void *)sechdrs[strindex].sh_addr;
+ mod->symtab = (void *)symsec->sh_addr;
+ mod->num_symtab = symsec->sh_size / sizeof(Elf_Sym);
+ /* Make sure we get permanent strtab: don't use info->strtab. */
+ mod->strtab = (void *)info->sechdrs[info->index.str].sh_addr;
/* Set types up while we still have access to sections. */
for (i = 0; i < mod->num_symtab; i++)
- mod->symtab[i].st_info
- = elf_type(&mod->symtab[i], sechdrs, secstrings, mod);
+ mod->symtab[i].st_info = elf_type(&mod->symtab[i], info);
- mod->core_symtab = dst = mod->module_core + symoffs;
+ mod->core_symtab = dst = mod->module_core + info->symoffs;
src = mod->symtab;
*dst = *src;
for (ndst = i = 1; i < mod->num_symtab; ++i, ++src) {
- if (!is_core_symbol(src, sechdrs, shnum))
+ if (!is_core_symbol(src, info->sechdrs, info->hdr->e_shnum))
continue;
dst[ndst] = *src;
- dst[ndst].st_name = bitmap_weight(strmap, dst[ndst].st_name);
+ dst[ndst].st_name = bitmap_weight(info->strmap,
+ dst[ndst].st_name);
++ndst;
}
mod->core_num_syms = ndst;
- mod->core_strtab = s = mod->module_core + stroffs;
- for (*s = 0, i = 1; i < sechdrs[strindex].sh_size; ++i)
- if (test_bit(i, strmap))
+ mod->core_strtab = s = mod->module_core + info->stroffs;
+ for (*s = 0, i = 1; i < info->sechdrs[info->index.str].sh_size; ++i)
+ if (test_bit(i, info->strmap))
*++s = mod->strtab[i];
}
#else
return 0;
}
-static inline void add_kallsyms(struct module *mod,
- Elf_Shdr *sechdrs,
- unsigned int shnum,
- unsigned int symindex,
- unsigned int strindex,
- unsigned long symoffs,
- unsigned long stroffs,
- const char *secstrings,
- const unsigned long *strmap)
+static void add_kallsyms(struct module *mod, struct load_info *info)
{
}
#endif /* CONFIG_KALLSYMS */
}
#endif
-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)
+/* Sets info->hdr, info->len and info->args. */
+static int copy_and_check(struct load_info *info,
+ const void __user *umod, unsigned long len,
+ const char __user *uargs)
{
int err;
Elf_Ehdr *hdr;
err = -ENOEXEC;
goto free_hdr;
}
+
+ /* Now copy in args */
+ info->args = strndup_user(uargs, ~0UL >> 1);
+ if (IS_ERR(info->args)) {
+ err = PTR_ERR(info->args);
+ goto free_hdr;
+ }
+
info->hdr = hdr;
info->len = len;
return 0;
return err;
}
+static void free_copy(struct load_info *info)
+{
+ kfree(info->args);
+ vfree(info->hdr);
+}
+
+static int rewrite_section_headers(struct load_info *info)
+{
+ unsigned int i;
+
+ /* This should always be true, but let's be sure. */
+ info->sechdrs[0].sh_addr = 0;
+
+ for (i = 1; i < info->hdr->e_shnum; i++) {
+ Elf_Shdr *shdr = &info->sechdrs[i];
+ if (shdr->sh_type != SHT_NOBITS
+ && info->len < shdr->sh_offset + shdr->sh_size) {
+ printk(KERN_ERR "Module len %lu truncated\n",
+ info->len);
+ return -ENOEXEC;
+ }
+
+ /* Mark all sections sh_addr with their address in the
+ temporary image. */
+ shdr->sh_addr = (size_t)info->hdr + shdr->sh_offset;
+
+#ifndef CONFIG_MODULE_UNLOAD
+ /* Don't load .exit sections */
+ if (strstarts(info->secstrings+shdr->sh_name, ".exit"))
+ shdr->sh_flags &= ~(unsigned long)SHF_ALLOC;
+#endif
+ }
+
+ /* Track but don't keep modinfo and version sections. */
+ 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->sechdrs[info->index.info].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ info->sechdrs[info->index.vers].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ return 0;
+}
+
/*
* Set up our basic convenience variables (pointers to section headers,
* search for module section index etc), and do some basic section
static struct module *setup_load_info(struct load_info *info)
{
unsigned int i;
+ int err;
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;
+ info->secstrings = (void *)info->hdr
+ + info->sechdrs[info->hdr->e_shstrndx].sh_offset;
- 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;
+ err = rewrite_section_headers(info);
+ if (err)
+ return ERR_PTR(err);
- /* Internal symbols and strings. */
+ /* Find internal symbols and strings. */
+ for (i = 1; i < info->hdr->e_shnum; i++) {
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;
+ info->strtab = (char *)info->hdr
+ + info->sechdrs[info->index.str].sh_offset;
+ break;
}
-#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,
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,
mod->name);
}
-static struct module *move_module(struct module *mod,
- Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
- const char *secstrings, unsigned modindex)
+static int move_module(struct module *mod,
+ Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+ const char *secstrings, unsigned modindex)
{
int i;
void *ptr;
*/
kmemleak_not_leak(ptr);
if (!ptr)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
memset(ptr, 0, mod->core_size);
mod->module_core = ptr;
kmemleak_ignore(ptr);
if (!ptr && mod->init_size) {
module_free(mod, mod->module_core);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
memset(ptr, 0, mod->init_size);
mod->module_init = ptr;
DEBUGP("\t0x%lx %s\n",
sechdrs[i].sh_addr, secstrings + sechdrs[i].sh_name);
}
- /* Module has been moved. */
- mod = (void *)sechdrs[modindex].sh_addr;
- kmemleak_load_module(mod, hdr, sechdrs, secstrings);
- return mod;
+
+ return 0;
}
static int check_module_license_and_versions(struct module *mod,
set_fs(old_fs);
}
-/* Allocate and load the module: note that size of section 0 is always
- zero, and we rely on this for optional sections. */
-static noinline struct module *load_module(void __user *umod,
- unsigned long len,
- const char __user *uargs)
+static struct module *layout_and_allocate(struct load_info *info)
{
- struct load_info info = { NULL, };
+ /* Module within temporary copy. */
struct module *mod;
- long err;
- unsigned long symoffs, stroffs, *strmap;
- void __percpu *percpu;
- struct _ddebug *debug = NULL;
- unsigned int num_debug = 0;
+ int err;
- DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
- umod, len, uargs);
+ mod = setup_load_info(info);
+ if (IS_ERR(mod))
+ return mod;
- err = copy_and_check(&info, umod, len);
+ err = check_modinfo(mod, info->sechdrs, info->index.info, info->index.vers);
if (err)
return ERR_PTR(err);
- mod = setup_load_info(&info);
- if (IS_ERR(mod)) {
- err = PTR_ERR(mod);
- goto free_hdr;
- }
-
- err = check_modinfo(mod, info.sechdrs, info.index.info, info.index.vers);
- if (err)
- goto free_hdr;
-
- /* Now copy in 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(info.sechdrs[info.index.str].sh_size)
- * sizeof(long), GFP_KERNEL);
- if (!strmap) {
- err = -ENOMEM;
- goto free_mod;
- }
-
- mod->state = MODULE_STATE_COMING;
-
/* Allow arches to frob section contents and sizes. */
- err = module_frob_arch_sections(info.hdr, info.sechdrs, info.secstrings, mod);
+ err = module_frob_arch_sections(info->hdr, info->sechdrs, info->secstrings, mod);
if (err < 0)
- goto free_mod;
+ goto free_args;
- if (info.index.pcpu) {
+ if (info->index.pcpu) {
/* We have a special allocation for this section. */
- err = percpu_modalloc(mod, info.sechdrs[info.index.pcpu].sh_size,
- info.sechdrs[info.index.pcpu].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;
- info.sechdrs[info.index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC;
+ goto free_args;
+ info->sechdrs[info->index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC;
}
- /* Keep this around for failure path. */
- percpu = mod_percpu(mod);
/* 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, info.hdr, info.sechdrs, info.secstrings);
- symoffs = layout_symtab(mod, info.sechdrs, info.index.sym, info.index.str, info.hdr,
- info.secstrings, &stroffs, strmap);
+ layout_sections(mod, info->hdr, info->sechdrs, info->secstrings);
+
+ info->strmap = kzalloc(BITS_TO_LONGS(info->sechdrs[info->index.str].sh_size)
+ * sizeof(long), GFP_KERNEL);
+ if (!info->strmap) {
+ err = -ENOMEM;
+ goto free_percpu;
+ }
+ info->symoffs = layout_symtab(mod, info->sechdrs, info->index.sym, info->index.str, info->hdr,
+ info->secstrings, &info->stroffs, info->strmap);
/* Allocate and move to the final place */
- mod = move_module(mod, info.hdr, info.sechdrs, info.secstrings, info.index.mod);
+ err = move_module(mod, info->hdr, info->sechdrs, info->secstrings, info->index.mod);
+ if (err)
+ goto free_strmap;
+
+ /* Module has been copied to its final place now: return it. */
+ mod = (void *)info->sechdrs[info->index.mod].sh_addr;
+ kmemleak_load_module(mod, info->hdr, info->sechdrs, info->secstrings);
+ return mod;
+
+free_strmap:
+ kfree(info->strmap);
+free_percpu:
+ percpu_modfree(mod);
+free_args:
+ kfree(info->args);
+ return ERR_PTR(err);
+}
+
+/* mod is no longer valid after this! */
+static void module_deallocate(struct module *mod, struct load_info *info)
+{
+ kfree(info->strmap);
+ percpu_modfree(mod);
+ module_free(mod, mod->module_init);
+ module_free(mod, mod->module_core);
+}
+
+/* Allocate and load the module: note that size of section 0 is always
+ zero, and we rely on this for optional sections. */
+static noinline struct module *load_module(void __user *umod,
+ unsigned long len,
+ const char __user *uargs)
+{
+ struct load_info info = { NULL, };
+ struct module *mod;
+ long err;
+ struct _ddebug *debug = NULL;
+ unsigned int num_debug = 0;
+
+ DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
+ umod, len, uargs);
+
+ /* Copy in the blobs from userspace, check they are vaguely sane. */
+ err = copy_and_check(&info, umod, len, uargs);
+ if (err)
+ return ERR_PTR(err);
+
+ /* Figure out module layout, and allocate all the memory. */
+ mod = layout_and_allocate(&info);
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
- goto free_percpu;
+ goto free_copy;
}
/* Now we've moved module, initialize linked lists, etc. */
err = module_unload_init(mod);
if (err)
- goto free_init;
+ goto free_module;
/* Now we've got everything in the final locations, we can
* find optional sections. */
err = simplify_symbols(info.sechdrs, info.index.sym, info.strtab, info.index.vers, info.index.pcpu,
mod);
if (err < 0)
- goto cleanup;
+ goto free_modinfo;
err = apply_relocations(mod, info.hdr, info.sechdrs, info.index.sym, info.index.str);
if (err < 0)
- goto cleanup;
+ goto free_modinfo;
/* Set up and sort exception table */
mod->extable = section_objs(info.hdr, info.sechdrs, info.secstrings, "__ex_table",
percpu_modcopy(mod, (void *)info.sechdrs[info.index.pcpu].sh_addr,
info.sechdrs[info.index.pcpu].sh_size);
- add_kallsyms(mod, info.sechdrs, info.hdr->e_shnum, info.index.sym, info.index.str,
- symoffs, stroffs, info.secstrings, strmap);
- kfree(strmap);
- strmap = NULL;
+ add_kallsyms(mod, &info);
if (!mod->taints)
debug = section_objs(info.hdr, info.sechdrs, info.secstrings, "__verbose",
err = module_finalize(info.hdr, info.sechdrs, mod);
if (err < 0)
- goto cleanup;
+ goto free_modinfo;
flush_module_icache(mod);
mod->args = info.args;
+ mod->state = MODULE_STATE_COMING;
+
/* Now sew it into the lists so we can get lockdep and oops
* info during argument parsing. Noone should access us, since
* strong_try_module_get() will fail.
if (err < 0)
goto unlink;
- err = mod_sysfs_setup(mod, mod->kp, mod->num_kp);
+ err = mod_sysfs_setup(mod, &info, mod->kp, mod->num_kp);
if (err < 0)
goto unlink;
- 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(info.hdr);
+ /* Get rid of temporary copy and strmap. */
+ kfree(info.strmap);
+ free_copy(&info);
trace_module_load(mod);
mutex_unlock(&module_mutex);
synchronize_sched();
module_arch_cleanup(mod);
- cleanup:
+ free_modinfo:
free_modinfo(mod);
free_unload:
module_unload_free(mod);
- free_init:
- module_free(mod, mod->module_init);
- module_free(mod, mod->module_core);
- /* mod will be freed with core. Don't access it beyond this line! */
- free_percpu:
- free_percpu(percpu);
- free_mod:
- kfree(info.args);
- kfree(strmap);
- free_hdr:
- vfree(info.hdr);
+ free_module:
+ module_deallocate(mod, &info);
+ free_copy:
+ free_copy(&info);
return ERR_PTR(err);
}