|
@@ -1,6 +1,6 @@
|
|
/*
|
|
/*
|
|
Copyright (C) 2002 Richard Henderson
|
|
Copyright (C) 2002 Richard Henderson
|
|
- Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM.
|
|
|
|
|
|
+ Copyright (C) 2001 Rusty Russell, 2002, 2010 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
|
|
it under the terms of the GNU General Public License as published by
|
|
it under the terms of the GNU General Public License as published by
|
|
@@ -110,6 +110,20 @@ int unregister_module_notifier(struct notifier_block * nb)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(unregister_module_notifier);
|
|
EXPORT_SYMBOL(unregister_module_notifier);
|
|
|
|
|
|
|
|
+struct load_info {
|
|
|
|
+ Elf_Ehdr *hdr;
|
|
|
|
+ unsigned long len;
|
|
|
|
+ Elf_Shdr *sechdrs;
|
|
|
|
+ char *secstrings, *strtab;
|
|
|
|
+ unsigned long *strmap;
|
|
|
|
+ unsigned long symoffs, stroffs;
|
|
|
|
+ struct _ddebug *debug;
|
|
|
|
+ unsigned int num_debug;
|
|
|
|
+ struct {
|
|
|
|
+ unsigned int sym, str, mod, vers, info, pcpu;
|
|
|
|
+ } index;
|
|
|
|
+};
|
|
|
|
+
|
|
/* We require a truly strong try_module_get(): 0 means failure due to
|
|
/* We require a truly strong try_module_get(): 0 means failure due to
|
|
ongoing or failed initialization etc. */
|
|
ongoing or failed initialization etc. */
|
|
static inline int strong_try_module_get(struct module *mod)
|
|
static inline int strong_try_module_get(struct module *mod)
|
|
@@ -140,42 +154,38 @@ void __module_put_and_exit(struct module *mod, long code)
|
|
EXPORT_SYMBOL(__module_put_and_exit);
|
|
EXPORT_SYMBOL(__module_put_and_exit);
|
|
|
|
|
|
/* Find a module section: 0 means not found. */
|
|
/* Find a module section: 0 means not found. */
|
|
-static unsigned int find_sec(Elf_Ehdr *hdr,
|
|
|
|
- Elf_Shdr *sechdrs,
|
|
|
|
- const char *secstrings,
|
|
|
|
- const char *name)
|
|
|
|
|
|
+static unsigned int find_sec(const struct load_info *info, const char *name)
|
|
{
|
|
{
|
|
unsigned int i;
|
|
unsigned int i;
|
|
|
|
|
|
- for (i = 1; i < hdr->e_shnum; i++)
|
|
|
|
|
|
+ for (i = 1; i < info->hdr->e_shnum; i++) {
|
|
|
|
+ Elf_Shdr *shdr = &info->sechdrs[i];
|
|
/* Alloc bit cleared means "ignore it." */
|
|
/* Alloc bit cleared means "ignore it." */
|
|
- if ((sechdrs[i].sh_flags & SHF_ALLOC)
|
|
|
|
- && strcmp(secstrings+sechdrs[i].sh_name, name) == 0)
|
|
|
|
|
|
+ if ((shdr->sh_flags & SHF_ALLOC)
|
|
|
|
+ && strcmp(info->secstrings + shdr->sh_name, name) == 0)
|
|
return i;
|
|
return i;
|
|
|
|
+ }
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* Find a module section, or NULL. */
|
|
/* Find a module section, or NULL. */
|
|
-static void *section_addr(Elf_Ehdr *hdr, Elf_Shdr *shdrs,
|
|
|
|
- const char *secstrings, const char *name)
|
|
|
|
|
|
+static void *section_addr(const struct load_info *info, const char *name)
|
|
{
|
|
{
|
|
/* Section 0 has sh_addr 0. */
|
|
/* Section 0 has sh_addr 0. */
|
|
- return (void *)shdrs[find_sec(hdr, shdrs, secstrings, name)].sh_addr;
|
|
|
|
|
|
+ return (void *)info->sechdrs[find_sec(info, name)].sh_addr;
|
|
}
|
|
}
|
|
|
|
|
|
/* Find a module section, or NULL. Fill in number of "objects" in section. */
|
|
/* Find a module section, or NULL. Fill in number of "objects" in section. */
|
|
-static void *section_objs(Elf_Ehdr *hdr,
|
|
|
|
- Elf_Shdr *sechdrs,
|
|
|
|
- const char *secstrings,
|
|
|
|
|
|
+static void *section_objs(const struct load_info *info,
|
|
const char *name,
|
|
const char *name,
|
|
size_t object_size,
|
|
size_t object_size,
|
|
unsigned int *num)
|
|
unsigned int *num)
|
|
{
|
|
{
|
|
- unsigned int sec = find_sec(hdr, sechdrs, secstrings, name);
|
|
|
|
|
|
+ unsigned int sec = find_sec(info, name);
|
|
|
|
|
|
/* Section 0 has sh_addr 0 and sh_size 0. */
|
|
/* Section 0 has sh_addr 0 and sh_size 0. */
|
|
- *num = sechdrs[sec].sh_size / object_size;
|
|
|
|
- return (void *)sechdrs[sec].sh_addr;
|
|
|
|
|
|
+ *num = info->sechdrs[sec].sh_size / object_size;
|
|
|
|
+ return (void *)info->sechdrs[sec].sh_addr;
|
|
}
|
|
}
|
|
|
|
|
|
/* Provided by the linker */
|
|
/* Provided by the linker */
|
|
@@ -227,7 +237,7 @@ bool each_symbol(bool (*fn)(const struct symsearch *arr, struct module *owner,
|
|
unsigned int symnum, void *data), void *data)
|
|
unsigned int symnum, void *data), void *data)
|
|
{
|
|
{
|
|
struct module *mod;
|
|
struct module *mod;
|
|
- const struct symsearch arr[] = {
|
|
|
|
|
|
+ static const struct symsearch arr[] = {
|
|
{ __start___ksymtab, __stop___ksymtab, __start___kcrctab,
|
|
{ __start___ksymtab, __stop___ksymtab, __start___kcrctab,
|
|
NOT_GPL_ONLY, false },
|
|
NOT_GPL_ONLY, false },
|
|
{ __start___ksymtab_gpl, __stop___ksymtab_gpl,
|
|
{ __start___ksymtab_gpl, __stop___ksymtab_gpl,
|
|
@@ -392,7 +402,8 @@ static int percpu_modalloc(struct module *mod,
|
|
mod->percpu = __alloc_reserved_percpu(size, align);
|
|
mod->percpu = __alloc_reserved_percpu(size, align);
|
|
if (!mod->percpu) {
|
|
if (!mod->percpu) {
|
|
printk(KERN_WARNING
|
|
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;
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
mod->percpu_size = size;
|
|
mod->percpu_size = size;
|
|
@@ -404,11 +415,9 @@ static void percpu_modfree(struct module *mod)
|
|
free_percpu(mod->percpu);
|
|
free_percpu(mod->percpu);
|
|
}
|
|
}
|
|
|
|
|
|
-static unsigned int find_pcpusec(Elf_Ehdr *hdr,
|
|
|
|
- Elf_Shdr *sechdrs,
|
|
|
|
- const char *secstrings)
|
|
|
|
|
|
+static unsigned int find_pcpusec(struct load_info *info)
|
|
{
|
|
{
|
|
- return find_sec(hdr, sechdrs, secstrings, ".data..percpu");
|
|
|
|
|
|
+ return find_sec(info, ".data..percpu");
|
|
}
|
|
}
|
|
|
|
|
|
static void percpu_modcopy(struct module *mod,
|
|
static void percpu_modcopy(struct module *mod,
|
|
@@ -468,9 +477,7 @@ static inline int percpu_modalloc(struct module *mod,
|
|
static inline void percpu_modfree(struct module *mod)
|
|
static inline void percpu_modfree(struct module *mod)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
-static inline unsigned int find_pcpusec(Elf_Ehdr *hdr,
|
|
|
|
- Elf_Shdr *sechdrs,
|
|
|
|
- const char *secstrings)
|
|
|
|
|
|
+static unsigned int find_pcpusec(struct load_info *info)
|
|
{
|
|
{
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -524,21 +531,21 @@ static char last_unloaded_module[MODULE_NAME_LEN+1];
|
|
EXPORT_TRACEPOINT_SYMBOL(module_get);
|
|
EXPORT_TRACEPOINT_SYMBOL(module_get);
|
|
|
|
|
|
/* Init the unload section of the module. */
|
|
/* Init the unload section of the module. */
|
|
-static void module_unload_init(struct module *mod)
|
|
|
|
|
|
+static int module_unload_init(struct module *mod)
|
|
{
|
|
{
|
|
- int cpu;
|
|
|
|
|
|
+ mod->refptr = alloc_percpu(struct module_ref);
|
|
|
|
+ if (!mod->refptr)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
INIT_LIST_HEAD(&mod->source_list);
|
|
INIT_LIST_HEAD(&mod->source_list);
|
|
INIT_LIST_HEAD(&mod->target_list);
|
|
INIT_LIST_HEAD(&mod->target_list);
|
|
- for_each_possible_cpu(cpu) {
|
|
|
|
- per_cpu_ptr(mod->refptr, cpu)->incs = 0;
|
|
|
|
- per_cpu_ptr(mod->refptr, cpu)->decs = 0;
|
|
|
|
- }
|
|
|
|
|
|
|
|
/* Hold reference count during initialization. */
|
|
/* Hold reference count during initialization. */
|
|
__this_cpu_write(mod->refptr->incs, 1);
|
|
__this_cpu_write(mod->refptr->incs, 1);
|
|
/* Backwards compatibility macros put refcount during init. */
|
|
/* Backwards compatibility macros put refcount during init. */
|
|
mod->waiter = current;
|
|
mod->waiter = current;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* Does a already use b? */
|
|
/* Does a already use b? */
|
|
@@ -618,6 +625,8 @@ static void module_unload_free(struct module *mod)
|
|
kfree(use);
|
|
kfree(use);
|
|
}
|
|
}
|
|
mutex_unlock(&module_mutex);
|
|
mutex_unlock(&module_mutex);
|
|
|
|
+
|
|
|
|
+ free_percpu(mod->refptr);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_MODULE_FORCE_UNLOAD
|
|
#ifdef CONFIG_MODULE_FORCE_UNLOAD
|
|
@@ -891,8 +900,9 @@ int ref_module(struct module *a, struct module *b)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(ref_module);
|
|
EXPORT_SYMBOL_GPL(ref_module);
|
|
|
|
|
|
-static inline void module_unload_init(struct module *mod)
|
|
|
|
|
|
+static inline int module_unload_init(struct module *mod)
|
|
{
|
|
{
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
#endif /* CONFIG_MODULE_UNLOAD */
|
|
#endif /* CONFIG_MODULE_UNLOAD */
|
|
|
|
|
|
@@ -1051,10 +1061,9 @@ static inline int same_magic(const char *amagic, const char *bmagic,
|
|
#endif /* CONFIG_MODVERSIONS */
|
|
#endif /* CONFIG_MODVERSIONS */
|
|
|
|
|
|
/* Resolve a symbol for this module. I.e. if we find one, record usage. */
|
|
/* Resolve a symbol for this module. I.e. if we find one, record usage. */
|
|
-static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,
|
|
|
|
- unsigned int versindex,
|
|
|
|
|
|
+static const struct kernel_symbol *resolve_symbol(struct module *mod,
|
|
|
|
+ const struct load_info *info,
|
|
const char *name,
|
|
const char *name,
|
|
- struct module *mod,
|
|
|
|
char ownername[])
|
|
char ownername[])
|
|
{
|
|
{
|
|
struct module *owner;
|
|
struct module *owner;
|
|
@@ -1068,7 +1077,8 @@ static const struct kernel_symbol *resolve_symbol(Elf_Shdr *sechdrs,
|
|
if (!sym)
|
|
if (!sym)
|
|
goto unlock;
|
|
goto unlock;
|
|
|
|
|
|
- if (!check_version(sechdrs, versindex, name, mod, crc, owner)) {
|
|
|
|
|
|
+ if (!check_version(info->sechdrs, info->index.vers, name, mod, crc,
|
|
|
|
+ owner)) {
|
|
sym = ERR_PTR(-EINVAL);
|
|
sym = ERR_PTR(-EINVAL);
|
|
goto getname;
|
|
goto getname;
|
|
}
|
|
}
|
|
@@ -1087,21 +1097,20 @@ unlock:
|
|
return sym;
|
|
return sym;
|
|
}
|
|
}
|
|
|
|
|
|
-static const struct kernel_symbol *resolve_symbol_wait(Elf_Shdr *sechdrs,
|
|
|
|
- unsigned int versindex,
|
|
|
|
- const char *name,
|
|
|
|
- struct module *mod)
|
|
|
|
|
|
+static const struct kernel_symbol *
|
|
|
|
+resolve_symbol_wait(struct module *mod,
|
|
|
|
+ const struct load_info *info,
|
|
|
|
+ const char *name)
|
|
{
|
|
{
|
|
const struct kernel_symbol *ksym;
|
|
const struct kernel_symbol *ksym;
|
|
- char ownername[MODULE_NAME_LEN];
|
|
|
|
|
|
+ char owner[MODULE_NAME_LEN];
|
|
|
|
|
|
if (wait_event_interruptible_timeout(module_wq,
|
|
if (wait_event_interruptible_timeout(module_wq,
|
|
- !IS_ERR(ksym = resolve_symbol(sechdrs, versindex, name,
|
|
|
|
- mod, ownername)) ||
|
|
|
|
- PTR_ERR(ksym) != -EBUSY,
|
|
|
|
|
|
+ !IS_ERR(ksym = resolve_symbol(mod, info, name, owner))
|
|
|
|
+ || PTR_ERR(ksym) != -EBUSY,
|
|
30 * HZ) <= 0) {
|
|
30 * HZ) <= 0) {
|
|
printk(KERN_WARNING "%s: gave up waiting for init of module %s.\n",
|
|
printk(KERN_WARNING "%s: gave up waiting for init of module %s.\n",
|
|
- mod->name, ownername);
|
|
|
|
|
|
+ mod->name, owner);
|
|
}
|
|
}
|
|
return ksym;
|
|
return ksym;
|
|
}
|
|
}
|
|
@@ -1110,8 +1119,9 @@ static const struct kernel_symbol *resolve_symbol_wait(Elf_Shdr *sechdrs,
|
|
* /sys/module/foo/sections stuff
|
|
* /sys/module/foo/sections stuff
|
|
* J. Corbet <corbet@lwn.net>
|
|
* 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)
|
|
static inline bool sect_empty(const Elf_Shdr *sect)
|
|
{
|
|
{
|
|
return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0;
|
|
return !(sect->sh_flags & SHF_ALLOC) || sect->sh_size == 0;
|
|
@@ -1148,8 +1158,7 @@ static void free_sect_attrs(struct module_sect_attrs *sect_attrs)
|
|
kfree(sect_attrs);
|
|
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];
|
|
unsigned int nloaded = 0, i, size[2];
|
|
struct module_sect_attrs *sect_attrs;
|
|
struct module_sect_attrs *sect_attrs;
|
|
@@ -1157,8 +1166,8 @@ static void add_sect_attrs(struct module *mod, unsigned int nsect,
|
|
struct attribute **gattr;
|
|
struct attribute **gattr;
|
|
|
|
|
|
/* Count loaded sections and allocate structures */
|
|
/* 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++;
|
|
nloaded++;
|
|
size[0] = ALIGN(sizeof(*sect_attrs)
|
|
size[0] = ALIGN(sizeof(*sect_attrs)
|
|
+ nloaded * sizeof(sect_attrs->attrs[0]),
|
|
+ nloaded * sizeof(sect_attrs->attrs[0]),
|
|
@@ -1175,11 +1184,12 @@ static void add_sect_attrs(struct module *mod, unsigned int nsect,
|
|
sect_attrs->nsections = 0;
|
|
sect_attrs->nsections = 0;
|
|
sattr = §_attrs->attrs[0];
|
|
sattr = §_attrs->attrs[0];
|
|
gattr = §_attrs->grp.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;
|
|
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);
|
|
GFP_KERNEL);
|
|
if (sattr->name == NULL)
|
|
if (sattr->name == NULL)
|
|
goto out;
|
|
goto out;
|
|
@@ -1247,8 +1257,7 @@ static void free_notes_attrs(struct module_notes_attrs *notes_attrs,
|
|
kfree(notes_attrs);
|
|
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;
|
|
unsigned int notes, loaded, i;
|
|
struct module_notes_attrs *notes_attrs;
|
|
struct module_notes_attrs *notes_attrs;
|
|
@@ -1260,9 +1269,9 @@ static void add_notes_attrs(struct module *mod, unsigned int nsect,
|
|
|
|
|
|
/* Count notes sections and allocate structures. */
|
|
/* Count notes sections and allocate structures. */
|
|
notes = 0;
|
|
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;
|
|
++notes;
|
|
|
|
|
|
if (notes == 0)
|
|
if (notes == 0)
|
|
@@ -1276,15 +1285,15 @@ static void add_notes_attrs(struct module *mod, unsigned int nsect,
|
|
|
|
|
|
notes_attrs->notes = notes;
|
|
notes_attrs->notes = notes;
|
|
nattr = ¬es_attrs->attrs[0];
|
|
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;
|
|
continue;
|
|
- if (sechdrs[i].sh_type == SHT_NOTE) {
|
|
|
|
|
|
+ if (info->sechdrs[i].sh_type == SHT_NOTE) {
|
|
sysfs_bin_attr_init(nattr);
|
|
sysfs_bin_attr_init(nattr);
|
|
nattr->attr.name = mod->sect_attrs->attrs[loaded].name;
|
|
nattr->attr.name = mod->sect_attrs->attrs[loaded].name;
|
|
nattr->attr.mode = S_IRUGO;
|
|
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->read = module_notes_read;
|
|
++nattr;
|
|
++nattr;
|
|
}
|
|
}
|
|
@@ -1315,8 +1324,8 @@ static void remove_notes_attrs(struct module *mod)
|
|
|
|
|
|
#else
|
|
#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)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1324,17 +1333,16 @@ static inline void remove_sect_attrs(struct module *mod)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
-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)
|
|
static inline void remove_notes_attrs(struct module *mod)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
-#endif
|
|
|
|
|
|
+#endif /* CONFIG_KALLSYMS */
|
|
|
|
|
|
-#ifdef CONFIG_SYSFS
|
|
|
|
static void add_usage_links(struct module *mod)
|
|
static void add_usage_links(struct module *mod)
|
|
{
|
|
{
|
|
#ifdef CONFIG_MODULE_UNLOAD
|
|
#ifdef CONFIG_MODULE_UNLOAD
|
|
@@ -1439,6 +1447,7 @@ out:
|
|
}
|
|
}
|
|
|
|
|
|
static int mod_sysfs_setup(struct module *mod,
|
|
static int mod_sysfs_setup(struct module *mod,
|
|
|
|
+ const struct load_info *info,
|
|
struct kernel_param *kparam,
|
|
struct kernel_param *kparam,
|
|
unsigned int num_params)
|
|
unsigned int num_params)
|
|
{
|
|
{
|
|
@@ -1463,6 +1472,8 @@ static int mod_sysfs_setup(struct module *mod,
|
|
goto out_unreg_param;
|
|
goto out_unreg_param;
|
|
|
|
|
|
add_usage_links(mod);
|
|
add_usage_links(mod);
|
|
|
|
+ add_sect_attrs(mod, info);
|
|
|
|
+ add_notes_attrs(mod, info);
|
|
|
|
|
|
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
|
|
kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
|
|
return 0;
|
|
return 0;
|
|
@@ -1479,33 +1490,26 @@ out:
|
|
|
|
|
|
static void mod_sysfs_fini(struct module *mod)
|
|
static void mod_sysfs_fini(struct module *mod)
|
|
{
|
|
{
|
|
|
|
+ remove_notes_attrs(mod);
|
|
|
|
+ remove_sect_attrs(mod);
|
|
kobject_put(&mod->mkobj.kobj);
|
|
kobject_put(&mod->mkobj.kobj);
|
|
}
|
|
}
|
|
|
|
|
|
-#else /* CONFIG_SYSFS */
|
|
|
|
-
|
|
|
|
-static inline int mod_sysfs_init(struct module *mod)
|
|
|
|
-{
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
|
|
+#else /* !CONFIG_SYSFS */
|
|
|
|
|
|
-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,
|
|
struct kernel_param *kparam,
|
|
unsigned int num_params)
|
|
unsigned int num_params)
|
|
{
|
|
{
|
|
return 0;
|
|
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)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1515,7 +1519,7 @@ static void del_usage_links(struct module *mod)
|
|
|
|
|
|
#endif /* CONFIG_SYSFS */
|
|
#endif /* CONFIG_SYSFS */
|
|
|
|
|
|
-static void mod_kobject_remove(struct module *mod)
|
|
|
|
|
|
+static void mod_sysfs_teardown(struct module *mod)
|
|
{
|
|
{
|
|
del_usage_links(mod);
|
|
del_usage_links(mod);
|
|
module_remove_modinfo_attrs(mod);
|
|
module_remove_modinfo_attrs(mod);
|
|
@@ -1545,9 +1549,7 @@ static void free_module(struct module *mod)
|
|
mutex_lock(&module_mutex);
|
|
mutex_lock(&module_mutex);
|
|
stop_machine(__unlink_module, mod, NULL);
|
|
stop_machine(__unlink_module, mod, NULL);
|
|
mutex_unlock(&module_mutex);
|
|
mutex_unlock(&module_mutex);
|
|
- remove_notes_attrs(mod);
|
|
|
|
- remove_sect_attrs(mod);
|
|
|
|
- mod_kobject_remove(mod);
|
|
|
|
|
|
+ mod_sysfs_teardown(mod);
|
|
|
|
|
|
/* Remove dynamic debug info */
|
|
/* Remove dynamic debug info */
|
|
ddebug_remove_module(mod->name);
|
|
ddebug_remove_module(mod->name);
|
|
@@ -1565,10 +1567,7 @@ static void free_module(struct module *mod)
|
|
module_free(mod, mod->module_init);
|
|
module_free(mod, mod->module_init);
|
|
kfree(mod->args);
|
|
kfree(mod->args);
|
|
percpu_modfree(mod);
|
|
percpu_modfree(mod);
|
|
-#if defined(CONFIG_MODULE_UNLOAD)
|
|
|
|
- if (mod->refptr)
|
|
|
|
- free_percpu(mod->refptr);
|
|
|
|
-#endif
|
|
|
|
|
|
+
|
|
/* Free lock-classes: */
|
|
/* Free lock-classes: */
|
|
lockdep_free_key_range(mod->module_core, mod->core_size);
|
|
lockdep_free_key_range(mod->module_core, mod->core_size);
|
|
|
|
|
|
@@ -1634,25 +1633,23 @@ static int verify_export_symbols(struct module *mod)
|
|
}
|
|
}
|
|
|
|
|
|
/* Change all symbols so that st_value encodes the pointer directly. */
|
|
/* Change all symbols so that st_value encodes the pointer directly. */
|
|
-static int simplify_symbols(Elf_Shdr *sechdrs,
|
|
|
|
- unsigned int symindex,
|
|
|
|
- const char *strtab,
|
|
|
|
- unsigned int versindex,
|
|
|
|
- unsigned int pcpuindex,
|
|
|
|
- struct module *mod)
|
|
|
|
-{
|
|
|
|
- Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr;
|
|
|
|
|
|
+static int simplify_symbols(struct module *mod, const struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
|
|
|
|
+ Elf_Sym *sym = (void *)symsec->sh_addr;
|
|
unsigned long secbase;
|
|
unsigned long secbase;
|
|
- unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym);
|
|
|
|
|
|
+ unsigned int i;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
const struct kernel_symbol *ksym;
|
|
const struct kernel_symbol *ksym;
|
|
|
|
|
|
- for (i = 1; i < n; i++) {
|
|
|
|
|
|
+ for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) {
|
|
|
|
+ const char *name = info->strtab + sym[i].st_name;
|
|
|
|
+
|
|
switch (sym[i].st_shndx) {
|
|
switch (sym[i].st_shndx) {
|
|
case SHN_COMMON:
|
|
case SHN_COMMON:
|
|
/* We compiled with -fno-common. These are not
|
|
/* We compiled with -fno-common. These are not
|
|
supposed to happen. */
|
|
supposed to happen. */
|
|
- DEBUGP("Common symbol: %s\n", strtab + sym[i].st_name);
|
|
|
|
|
|
+ DEBUGP("Common symbol: %s\n", name);
|
|
printk("%s: please compile with -fno-common\n",
|
|
printk("%s: please compile with -fno-common\n",
|
|
mod->name);
|
|
mod->name);
|
|
ret = -ENOEXEC;
|
|
ret = -ENOEXEC;
|
|
@@ -1665,9 +1662,7 @@ static int simplify_symbols(Elf_Shdr *sechdrs,
|
|
break;
|
|
break;
|
|
|
|
|
|
case SHN_UNDEF:
|
|
case SHN_UNDEF:
|
|
- ksym = resolve_symbol_wait(sechdrs, versindex,
|
|
|
|
- strtab + sym[i].st_name,
|
|
|
|
- mod);
|
|
|
|
|
|
+ ksym = resolve_symbol_wait(mod, info, name);
|
|
/* Ok if resolved. */
|
|
/* Ok if resolved. */
|
|
if (ksym && !IS_ERR(ksym)) {
|
|
if (ksym && !IS_ERR(ksym)) {
|
|
sym[i].st_value = ksym->value;
|
|
sym[i].st_value = ksym->value;
|
|
@@ -1679,17 +1674,16 @@ static int simplify_symbols(Elf_Shdr *sechdrs,
|
|
break;
|
|
break;
|
|
|
|
|
|
printk(KERN_WARNING "%s: Unknown symbol %s (err %li)\n",
|
|
printk(KERN_WARNING "%s: Unknown symbol %s (err %li)\n",
|
|
- mod->name, strtab + sym[i].st_name,
|
|
|
|
- PTR_ERR(ksym));
|
|
|
|
|
|
+ mod->name, name, PTR_ERR(ksym));
|
|
ret = PTR_ERR(ksym) ?: -ENOENT;
|
|
ret = PTR_ERR(ksym) ?: -ENOENT;
|
|
break;
|
|
break;
|
|
|
|
|
|
default:
|
|
default:
|
|
/* Divert to percpu allocation if a percpu var. */
|
|
/* Divert to percpu allocation if a percpu var. */
|
|
- if (sym[i].st_shndx == pcpuindex)
|
|
|
|
|
|
+ if (sym[i].st_shndx == info->index.pcpu)
|
|
secbase = (unsigned long)mod_percpu(mod);
|
|
secbase = (unsigned long)mod_percpu(mod);
|
|
else
|
|
else
|
|
- secbase = sechdrs[sym[i].st_shndx].sh_addr;
|
|
|
|
|
|
+ secbase = info->sechdrs[sym[i].st_shndx].sh_addr;
|
|
sym[i].st_value += secbase;
|
|
sym[i].st_value += secbase;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
@@ -1698,6 +1692,35 @@ static int simplify_symbols(Elf_Shdr *sechdrs,
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int apply_relocations(struct module *mod, const struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ unsigned int i;
|
|
|
|
+ int err = 0;
|
|
|
|
+
|
|
|
|
+ /* Now do relocations. */
|
|
|
|
+ for (i = 1; i < info->hdr->e_shnum; i++) {
|
|
|
|
+ unsigned int infosec = info->sechdrs[i].sh_info;
|
|
|
|
+
|
|
|
|
+ /* Not a valid relocation section? */
|
|
|
|
+ if (infosec >= info->hdr->e_shnum)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ /* Don't bother with non-allocated sections */
|
|
|
|
+ if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (info->sechdrs[i].sh_type == SHT_REL)
|
|
|
|
+ err = apply_relocate(info->sechdrs, info->strtab,
|
|
|
|
+ info->index.sym, i, mod);
|
|
|
|
+ else if (info->sechdrs[i].sh_type == SHT_RELA)
|
|
|
|
+ err = apply_relocate_add(info->sechdrs, info->strtab,
|
|
|
|
+ info->index.sym, i, mod);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
/* Additional bytes needed by arch in front of individual sections */
|
|
/* Additional bytes needed by arch in front of individual sections */
|
|
unsigned int __weak arch_mod_section_prepend(struct module *mod,
|
|
unsigned int __weak arch_mod_section_prepend(struct module *mod,
|
|
unsigned int section)
|
|
unsigned int section)
|
|
@@ -1722,10 +1745,7 @@ static long get_offset(struct module *mod, unsigned int *size,
|
|
might -- code, read-only data, read-write data, small data. Tally
|
|
might -- code, read-only data, read-write data, small data. Tally
|
|
sizes, and place the offsets into sh_entsize fields: high bit means it
|
|
sizes, and place the offsets into sh_entsize fields: high bit means it
|
|
belongs in init. */
|
|
belongs in init. */
|
|
-static void layout_sections(struct module *mod,
|
|
|
|
- const Elf_Ehdr *hdr,
|
|
|
|
- Elf_Shdr *sechdrs,
|
|
|
|
- const char *secstrings)
|
|
|
|
|
|
+static void layout_sections(struct module *mod, struct load_info *info)
|
|
{
|
|
{
|
|
static unsigned long const masks[][2] = {
|
|
static unsigned long const masks[][2] = {
|
|
/* NOTE: all executable code must be the first section
|
|
/* NOTE: all executable code must be the first section
|
|
@@ -1738,21 +1758,22 @@ static void layout_sections(struct module *mod,
|
|
};
|
|
};
|
|
unsigned int m, i;
|
|
unsigned int m, i;
|
|
|
|
|
|
- for (i = 0; i < hdr->e_shnum; i++)
|
|
|
|
- sechdrs[i].sh_entsize = ~0UL;
|
|
|
|
|
|
+ for (i = 0; i < info->hdr->e_shnum; i++)
|
|
|
|
+ info->sechdrs[i].sh_entsize = ~0UL;
|
|
|
|
|
|
DEBUGP("Core section allocation order:\n");
|
|
DEBUGP("Core section allocation order:\n");
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
- for (i = 0; i < hdr->e_shnum; ++i) {
|
|
|
|
- Elf_Shdr *s = &sechdrs[i];
|
|
|
|
|
|
+ for (i = 0; i < info->hdr->e_shnum; ++i) {
|
|
|
|
+ Elf_Shdr *s = &info->sechdrs[i];
|
|
|
|
+ const char *sname = info->secstrings + s->sh_name;
|
|
|
|
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
|| (s->sh_flags & masks[m][1])
|
|
|| (s->sh_flags & masks[m][1])
|
|
|| s->sh_entsize != ~0UL
|
|
|| s->sh_entsize != ~0UL
|
|
- || strstarts(secstrings + s->sh_name, ".init"))
|
|
|
|
|
|
+ || strstarts(sname, ".init"))
|
|
continue;
|
|
continue;
|
|
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
|
|
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
|
|
- DEBUGP("\t%s\n", secstrings + s->sh_name);
|
|
|
|
|
|
+ DEBUGP("\t%s\n", name);
|
|
}
|
|
}
|
|
if (m == 0)
|
|
if (m == 0)
|
|
mod->core_text_size = mod->core_size;
|
|
mod->core_text_size = mod->core_size;
|
|
@@ -1760,17 +1781,18 @@ static void layout_sections(struct module *mod,
|
|
|
|
|
|
DEBUGP("Init section allocation order:\n");
|
|
DEBUGP("Init section allocation order:\n");
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
|
|
- for (i = 0; i < hdr->e_shnum; ++i) {
|
|
|
|
- Elf_Shdr *s = &sechdrs[i];
|
|
|
|
|
|
+ for (i = 0; i < info->hdr->e_shnum; ++i) {
|
|
|
|
+ Elf_Shdr *s = &info->sechdrs[i];
|
|
|
|
+ const char *sname = info->secstrings + s->sh_name;
|
|
|
|
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
|
|| (s->sh_flags & masks[m][1])
|
|
|| (s->sh_flags & masks[m][1])
|
|
|| s->sh_entsize != ~0UL
|
|
|| s->sh_entsize != ~0UL
|
|
- || !strstarts(secstrings + s->sh_name, ".init"))
|
|
|
|
|
|
+ || !strstarts(sname, ".init"))
|
|
continue;
|
|
continue;
|
|
s->sh_entsize = (get_offset(mod, &mod->init_size, s, i)
|
|
s->sh_entsize = (get_offset(mod, &mod->init_size, s, i)
|
|
| INIT_OFFSET_MASK);
|
|
| INIT_OFFSET_MASK);
|
|
- DEBUGP("\t%s\n", secstrings + s->sh_name);
|
|
|
|
|
|
+ DEBUGP("\t%s\n", sname);
|
|
}
|
|
}
|
|
if (m == 0)
|
|
if (m == 0)
|
|
mod->init_text_size = mod->init_size;
|
|
mod->init_text_size = mod->init_size;
|
|
@@ -1809,33 +1831,28 @@ static char *next_string(char *string, unsigned long *secsize)
|
|
return string;
|
|
return string;
|
|
}
|
|
}
|
|
|
|
|
|
-static char *get_modinfo(Elf_Shdr *sechdrs,
|
|
|
|
- unsigned int info,
|
|
|
|
- const char *tag)
|
|
|
|
|
|
+static char *get_modinfo(struct load_info *info, const char *tag)
|
|
{
|
|
{
|
|
char *p;
|
|
char *p;
|
|
unsigned int taglen = strlen(tag);
|
|
unsigned int taglen = strlen(tag);
|
|
- unsigned long size = sechdrs[info].sh_size;
|
|
|
|
|
|
+ Elf_Shdr *infosec = &info->sechdrs[info->index.info];
|
|
|
|
+ unsigned long size = infosec->sh_size;
|
|
|
|
|
|
- for (p = (char *)sechdrs[info].sh_addr; p; p = next_string(p, &size)) {
|
|
|
|
|
|
+ for (p = (char *)infosec->sh_addr; p; p = next_string(p, &size)) {
|
|
if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
|
|
if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
|
|
return p + taglen + 1;
|
|
return p + taglen + 1;
|
|
}
|
|
}
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs,
|
|
|
|
- unsigned int infoindex)
|
|
|
|
|
|
+static void setup_modinfo(struct module *mod, struct load_info *info)
|
|
{
|
|
{
|
|
struct module_attribute *attr;
|
|
struct module_attribute *attr;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
for (i = 0; (attr = modinfo_attrs[i]); i++) {
|
|
for (i = 0; (attr = modinfo_attrs[i]); i++) {
|
|
if (attr->setup)
|
|
if (attr->setup)
|
|
- attr->setup(mod,
|
|
|
|
- get_modinfo(sechdrs,
|
|
|
|
- infoindex,
|
|
|
|
- attr->attr.name));
|
|
|
|
|
|
+ attr->setup(mod, get_modinfo(info, attr->attr.name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1876,11 +1893,10 @@ static int is_exported(const char *name, unsigned long value,
|
|
}
|
|
}
|
|
|
|
|
|
/* As per nm */
|
|
/* 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_BIND(sym->st_info) == STB_WEAK) {
|
|
if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)
|
|
if (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)
|
|
return 'v';
|
|
return 'v';
|
|
@@ -1910,8 +1926,10 @@ static char elf_type(const Elf_Sym *sym,
|
|
else
|
|
else
|
|
return 'b';
|
|
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 'n';
|
|
|
|
+ }
|
|
return '?';
|
|
return '?';
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1936,127 +1954,96 @@ static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
-static unsigned long layout_symtab(struct module *mod,
|
|
|
|
- Elf_Shdr *sechdrs,
|
|
|
|
- unsigned int symindex,
|
|
|
|
- unsigned int strindex,
|
|
|
|
- const Elf_Ehdr *hdr,
|
|
|
|
- const char *secstrings,
|
|
|
|
- unsigned long *pstroffs,
|
|
|
|
- unsigned long *strmap)
|
|
|
|
|
|
+static void layout_symtab(struct module *mod, struct load_info *info)
|
|
{
|
|
{
|
|
- unsigned long symoffs;
|
|
|
|
- Elf_Shdr *symsect = sechdrs + symindex;
|
|
|
|
- Elf_Shdr *strsect = sechdrs + strindex;
|
|
|
|
|
|
+ Elf_Shdr *symsect = info->sechdrs + info->index.sym;
|
|
|
|
+ Elf_Shdr *strsect = info->sechdrs + info->index.str;
|
|
const Elf_Sym *src;
|
|
const Elf_Sym *src;
|
|
- const char *strtab;
|
|
|
|
unsigned int i, nsrc, ndst;
|
|
unsigned int i, nsrc, ndst;
|
|
|
|
|
|
/* Put symbol section at end of init part of module. */
|
|
/* Put symbol section at end of init part of module. */
|
|
symsect->sh_flags |= SHF_ALLOC;
|
|
symsect->sh_flags |= SHF_ALLOC;
|
|
symsect->sh_entsize = get_offset(mod, &mod->init_size, symsect,
|
|
symsect->sh_entsize = get_offset(mod, &mod->init_size, symsect,
|
|
- symindex) | INIT_OFFSET_MASK;
|
|
|
|
- DEBUGP("\t%s\n", secstrings + symsect->sh_name);
|
|
|
|
|
|
+ info->index.sym) | INIT_OFFSET_MASK;
|
|
|
|
+ DEBUGP("\t%s\n", info->secstrings + symsect->sh_name);
|
|
|
|
|
|
- src = (void *)hdr + symsect->sh_offset;
|
|
|
|
|
|
+ src = (void *)info->hdr + symsect->sh_offset;
|
|
nsrc = symsect->sh_size / sizeof(*src);
|
|
nsrc = symsect->sh_size / sizeof(*src);
|
|
- strtab = (void *)hdr + strsect->sh_offset;
|
|
|
|
for (ndst = i = 1; i < nsrc; ++i, ++src)
|
|
for (ndst = i = 1; i < nsrc; ++i, ++src)
|
|
- if (is_core_symbol(src, sechdrs, hdr->e_shnum)) {
|
|
|
|
|
|
+ if (is_core_symbol(src, info->sechdrs, info->hdr->e_shnum)) {
|
|
unsigned int j = src->st_name;
|
|
unsigned int j = src->st_name;
|
|
|
|
|
|
- while(!__test_and_set_bit(j, strmap) && strtab[j])
|
|
|
|
|
|
+ while (!__test_and_set_bit(j, info->strmap)
|
|
|
|
+ && info->strtab[j])
|
|
++j;
|
|
++j;
|
|
++ndst;
|
|
++ndst;
|
|
}
|
|
}
|
|
|
|
|
|
/* Append room for core symbols at end of core part. */
|
|
/* Append room for core symbols at end of core part. */
|
|
- symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
|
|
|
|
- mod->core_size = symoffs + ndst * sizeof(Elf_Sym);
|
|
|
|
|
|
+ info->symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
|
|
|
|
+ mod->core_size = info->symoffs + ndst * sizeof(Elf_Sym);
|
|
|
|
|
|
/* Put string table section at end of init part of module. */
|
|
/* Put string table section at end of init part of module. */
|
|
strsect->sh_flags |= SHF_ALLOC;
|
|
strsect->sh_flags |= SHF_ALLOC;
|
|
strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
|
|
strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
|
|
- strindex) | INIT_OFFSET_MASK;
|
|
|
|
- DEBUGP("\t%s\n", secstrings + strsect->sh_name);
|
|
|
|
|
|
+ info->index.str) | INIT_OFFSET_MASK;
|
|
|
|
+ DEBUGP("\t%s\n", info->secstrings + strsect->sh_name);
|
|
|
|
|
|
/* Append room for core symbols' strings at end of core part. */
|
|
/* Append room for core symbols' strings at end of core part. */
|
|
- *pstroffs = mod->core_size;
|
|
|
|
- __set_bit(0, strmap);
|
|
|
|
- mod->core_size += bitmap_weight(strmap, strsect->sh_size);
|
|
|
|
-
|
|
|
|
- return symoffs;
|
|
|
|
|
|
+ info->stroffs = mod->core_size;
|
|
|
|
+ __set_bit(0, info->strmap);
|
|
|
|
+ mod->core_size += bitmap_weight(info->strmap, strsect->sh_size);
|
|
}
|
|
}
|
|
|
|
|
|
-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, const struct load_info *info)
|
|
{
|
|
{
|
|
unsigned int i, ndst;
|
|
unsigned int i, ndst;
|
|
const Elf_Sym *src;
|
|
const Elf_Sym *src;
|
|
Elf_Sym *dst;
|
|
Elf_Sym *dst;
|
|
char *s;
|
|
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. */
|
|
/* Set types up while we still have access to sections. */
|
|
for (i = 0; i < mod->num_symtab; i++)
|
|
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;
|
|
src = mod->symtab;
|
|
*dst = *src;
|
|
*dst = *src;
|
|
for (ndst = i = 1; i < mod->num_symtab; ++i, ++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;
|
|
continue;
|
|
dst[ndst] = *src;
|
|
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;
|
|
++ndst;
|
|
}
|
|
}
|
|
mod->core_num_syms = 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];
|
|
*++s = mod->strtab[i];
|
|
}
|
|
}
|
|
#else
|
|
#else
|
|
-static inline unsigned long layout_symtab(struct module *mod,
|
|
|
|
- Elf_Shdr *sechdrs,
|
|
|
|
- unsigned int symindex,
|
|
|
|
- unsigned int strindex,
|
|
|
|
- const Elf_Ehdr *hdr,
|
|
|
|
- const char *secstrings,
|
|
|
|
- unsigned long *pstroffs,
|
|
|
|
- unsigned long *strmap)
|
|
|
|
|
|
+static inline void layout_symtab(struct module *mod, struct load_info *info)
|
|
{
|
|
{
|
|
- 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 /* CONFIG_KALLSYMS */
|
|
|
|
|
|
static void dynamic_debug_setup(struct _ddebug *debug, unsigned int num)
|
|
static void dynamic_debug_setup(struct _ddebug *debug, unsigned int num)
|
|
{
|
|
{
|
|
|
|
+ if (!debug)
|
|
|
|
+ return;
|
|
#ifdef CONFIG_DYNAMIC_DEBUG
|
|
#ifdef CONFIG_DYNAMIC_DEBUG
|
|
if (ddebug_add_module(debug, num, debug->modname))
|
|
if (ddebug_add_module(debug, num, debug->modname))
|
|
printk(KERN_ERR "dynamic debug error adding module: %s\n",
|
|
printk(KERN_ERR "dynamic debug error adding module: %s\n",
|
|
@@ -2087,65 +2074,47 @@ static void *module_alloc_update_bounds(unsigned long size)
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_KMEMLEAK
|
|
#ifdef CONFIG_DEBUG_KMEMLEAK
|
|
-static void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
|
|
|
|
- Elf_Shdr *sechdrs, char *secstrings)
|
|
|
|
|
|
+static void kmemleak_load_module(const struct module *mod,
|
|
|
|
+ const struct load_info *info)
|
|
{
|
|
{
|
|
unsigned int i;
|
|
unsigned int i;
|
|
|
|
|
|
/* only scan the sections containing data */
|
|
/* only scan the sections containing data */
|
|
kmemleak_scan_area(mod, sizeof(struct module), GFP_KERNEL);
|
|
kmemleak_scan_area(mod, sizeof(struct module), GFP_KERNEL);
|
|
|
|
|
|
- for (i = 1; i < hdr->e_shnum; i++) {
|
|
|
|
- if (!(sechdrs[i].sh_flags & SHF_ALLOC))
|
|
|
|
|
|
+ for (i = 1; i < info->hdr->e_shnum; i++) {
|
|
|
|
+ const char *name = info->secstrings + info->sechdrs[i].sh_name;
|
|
|
|
+ if (!(info->sechdrs[i].sh_flags & SHF_ALLOC))
|
|
continue;
|
|
continue;
|
|
- if (strncmp(secstrings + sechdrs[i].sh_name, ".data", 5) != 0
|
|
|
|
- && strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) != 0)
|
|
|
|
|
|
+ if (!strstarts(name, ".data") && !strstarts(name, ".bss"))
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- kmemleak_scan_area((void *)sechdrs[i].sh_addr,
|
|
|
|
- sechdrs[i].sh_size, GFP_KERNEL);
|
|
|
|
|
|
+ kmemleak_scan_area((void *)info->sechdrs[i].sh_addr,
|
|
|
|
+ info->sechdrs[i].sh_size, GFP_KERNEL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
#else
|
|
-static inline void kmemleak_load_module(struct module *mod, Elf_Ehdr *hdr,
|
|
|
|
- Elf_Shdr *sechdrs, char *secstrings)
|
|
|
|
|
|
+static inline void kmemleak_load_module(const struct module *mod,
|
|
|
|
+ const struct load_info *info)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
-/* 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)
|
|
|
|
|
|
+/* Sets info->hdr and info->len. */
|
|
|
|
+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;
|
|
Elf_Ehdr *hdr;
|
|
- Elf_Shdr *sechdrs;
|
|
|
|
- char *secstrings, *args, *modmagic, *strtab = NULL;
|
|
|
|
- char *staging;
|
|
|
|
- unsigned int i;
|
|
|
|
- unsigned int symindex = 0;
|
|
|
|
- unsigned int strindex = 0;
|
|
|
|
- unsigned int modindex, versindex, infoindex, pcpuindex;
|
|
|
|
- struct module *mod;
|
|
|
|
- long err = 0;
|
|
|
|
- void *ptr = NULL; /* Stops spurious gcc warning */
|
|
|
|
- unsigned long symoffs, stroffs, *strmap;
|
|
|
|
- void __percpu *percpu;
|
|
|
|
- struct _ddebug *debug = NULL;
|
|
|
|
- unsigned int num_debug = 0;
|
|
|
|
|
|
|
|
- mm_segment_t old_fs;
|
|
|
|
-
|
|
|
|
- DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
|
|
|
|
- umod, len, uargs);
|
|
|
|
if (len < sizeof(*hdr))
|
|
if (len < sizeof(*hdr))
|
|
- return ERR_PTR(-ENOEXEC);
|
|
|
|
|
|
+ return -ENOEXEC;
|
|
|
|
|
|
/* Suck in entire file: we'll want most of it. */
|
|
/* Suck in entire file: we'll want most of it. */
|
|
/* vmalloc barfs on "unusual" numbers. Check here */
|
|
/* vmalloc barfs on "unusual" numbers. Check here */
|
|
if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
|
|
if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
|
|
- return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
if (copy_from_user(hdr, umod, len) != 0) {
|
|
if (copy_from_user(hdr, umod, len) != 0) {
|
|
err = -EFAULT;
|
|
err = -EFAULT;
|
|
@@ -2153,135 +2122,225 @@ static noinline struct module *load_module(void __user *umod,
|
|
}
|
|
}
|
|
|
|
|
|
/* Sanity checks against insmoding binaries or wrong arch,
|
|
/* Sanity checks against insmoding binaries or wrong arch,
|
|
- weird elf version */
|
|
|
|
|
|
+ weird elf version */
|
|
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
|
|
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
|
|
|| hdr->e_type != ET_REL
|
|
|| hdr->e_type != ET_REL
|
|
|| !elf_check_arch(hdr)
|
|
|| !elf_check_arch(hdr)
|
|
- || hdr->e_shentsize != sizeof(*sechdrs)) {
|
|
|
|
|
|
+ || hdr->e_shentsize != sizeof(Elf_Shdr)) {
|
|
err = -ENOEXEC;
|
|
err = -ENOEXEC;
|
|
goto free_hdr;
|
|
goto free_hdr;
|
|
}
|
|
}
|
|
|
|
|
|
- if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
|
|
|
|
- goto truncated;
|
|
|
|
|
|
+ if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr)) {
|
|
|
|
+ err = -ENOEXEC;
|
|
|
|
+ goto free_hdr;
|
|
|
|
+ }
|
|
|
|
|
|
- /* Convenience variables */
|
|
|
|
- sechdrs = (void *)hdr + hdr->e_shoff;
|
|
|
|
- secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
|
|
|
- sechdrs[0].sh_addr = 0;
|
|
|
|
|
|
+ info->hdr = hdr;
|
|
|
|
+ info->len = len;
|
|
|
|
+ return 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;
|
|
|
|
|
|
+free_hdr:
|
|
|
|
+ vfree(hdr);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void free_copy(struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ 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
|
|
/* Mark all sections sh_addr with their address in the
|
|
temporary image. */
|
|
temporary image. */
|
|
- sechdrs[i].sh_addr = (size_t)hdr + sechdrs[i].sh_offset;
|
|
|
|
|
|
+ shdr->sh_addr = (size_t)info->hdr + shdr->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
|
|
#ifndef CONFIG_MODULE_UNLOAD
|
|
/* Don't load .exit sections */
|
|
/* Don't load .exit sections */
|
|
- if (strstarts(secstrings+sechdrs[i].sh_name, ".exit"))
|
|
|
|
- sechdrs[i].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
|
|
|
|
|
+ if (strstarts(info->secstrings+shdr->sh_name, ".exit"))
|
|
|
|
+ shdr->sh_flags &= ~(unsigned long)SHF_ALLOC;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
- modindex = find_sec(hdr, sechdrs, secstrings,
|
|
|
|
- ".gnu.linkonce.this_module");
|
|
|
|
- if (!modindex) {
|
|
|
|
|
|
+ /* Track but don't keep modinfo and version sections. */
|
|
|
|
+ info->index.vers = find_sec(info, "__versions");
|
|
|
|
+ info->index.info = find_sec(info, ".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
|
|
|
|
+ * 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;
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ err = rewrite_section_headers(info);
|
|
|
|
+ if (err)
|
|
|
|
+ return ERR_PTR(err);
|
|
|
|
+
|
|
|
|
+ /* 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;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
|
|
|
|
+ if (!info->index.mod) {
|
|
printk(KERN_WARNING "No module found in object\n");
|
|
printk(KERN_WARNING "No module found in object\n");
|
|
- err = -ENOEXEC;
|
|
|
|
- goto free_hdr;
|
|
|
|
|
|
+ return ERR_PTR(-ENOEXEC);
|
|
}
|
|
}
|
|
/* This is temporary: point mod into copy of data. */
|
|
/* This is temporary: point mod into copy of data. */
|
|
- mod = (void *)sechdrs[modindex].sh_addr;
|
|
|
|
|
|
+ mod = (void *)info->sechdrs[info->index.mod].sh_addr;
|
|
|
|
|
|
- if (symindex == 0) {
|
|
|
|
|
|
+ if (info->index.sym == 0) {
|
|
printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
|
|
printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
|
|
mod->name);
|
|
mod->name);
|
|
- err = -ENOEXEC;
|
|
|
|
- goto free_hdr;
|
|
|
|
|
|
+ return ERR_PTR(-ENOEXEC);
|
|
}
|
|
}
|
|
|
|
|
|
- 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;
|
|
|
|
|
|
+ info->index.pcpu = find_pcpusec(info);
|
|
|
|
|
|
/* Check module struct version now, before we try to use module. */
|
|
/* Check module struct version now, before we try to use module. */
|
|
- if (!check_modstruct_version(sechdrs, versindex, mod)) {
|
|
|
|
- err = -ENOEXEC;
|
|
|
|
- goto free_hdr;
|
|
|
|
- }
|
|
|
|
|
|
+ if (!check_modstruct_version(info->sechdrs, info->index.vers, mod))
|
|
|
|
+ return ERR_PTR(-ENOEXEC);
|
|
|
|
+
|
|
|
|
+ return mod;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int check_modinfo(struct module *mod, struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ const char *modmagic = get_modinfo(info, "vermagic");
|
|
|
|
+ int err;
|
|
|
|
|
|
- modmagic = get_modinfo(sechdrs, infoindex, "vermagic");
|
|
|
|
/* This is allowed: modprobe --force will invalidate it. */
|
|
/* This is allowed: modprobe --force will invalidate it. */
|
|
if (!modmagic) {
|
|
if (!modmagic) {
|
|
err = try_to_force_load(mod, "bad vermagic");
|
|
err = try_to_force_load(mod, "bad vermagic");
|
|
if (err)
|
|
if (err)
|
|
- goto free_hdr;
|
|
|
|
- } else if (!same_magic(modmagic, vermagic, versindex)) {
|
|
|
|
|
|
+ return err;
|
|
|
|
+ } else if (!same_magic(modmagic, vermagic, info->index.vers)) {
|
|
printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
|
|
printk(KERN_ERR "%s: version magic '%s' should be '%s'\n",
|
|
mod->name, modmagic, vermagic);
|
|
mod->name, modmagic, vermagic);
|
|
- err = -ENOEXEC;
|
|
|
|
- goto free_hdr;
|
|
|
|
|
|
+ return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
|
|
- staging = get_modinfo(sechdrs, infoindex, "staging");
|
|
|
|
- if (staging) {
|
|
|
|
|
|
+ if (get_modinfo(info, "staging")) {
|
|
add_taint_module(mod, TAINT_CRAP);
|
|
add_taint_module(mod, TAINT_CRAP);
|
|
printk(KERN_WARNING "%s: module is from the staging directory,"
|
|
printk(KERN_WARNING "%s: module is from the staging directory,"
|
|
" the quality is unknown, you have been warned.\n",
|
|
" the quality is unknown, you have been warned.\n",
|
|
mod->name);
|
|
mod->name);
|
|
}
|
|
}
|
|
|
|
|
|
- /* Now copy in args */
|
|
|
|
- args = strndup_user(uargs, ~0UL >> 1);
|
|
|
|
- if (IS_ERR(args)) {
|
|
|
|
- err = PTR_ERR(args);
|
|
|
|
- goto free_hdr;
|
|
|
|
- }
|
|
|
|
|
|
+ /* Set up license info based on the info section */
|
|
|
|
+ set_license(mod, get_modinfo(info, "license"));
|
|
|
|
|
|
- strmap = kzalloc(BITS_TO_LONGS(sechdrs[strindex].sh_size)
|
|
|
|
- * sizeof(long), GFP_KERNEL);
|
|
|
|
- if (!strmap) {
|
|
|
|
- err = -ENOMEM;
|
|
|
|
- goto free_mod;
|
|
|
|
- }
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- mod->state = MODULE_STATE_COMING;
|
|
|
|
|
|
+static void find_module_sections(struct module *mod, struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ mod->kp = section_objs(info, "__param",
|
|
|
|
+ sizeof(*mod->kp), &mod->num_kp);
|
|
|
|
+ mod->syms = section_objs(info, "__ksymtab",
|
|
|
|
+ sizeof(*mod->syms), &mod->num_syms);
|
|
|
|
+ mod->crcs = section_addr(info, "__kcrctab");
|
|
|
|
+ mod->gpl_syms = section_objs(info, "__ksymtab_gpl",
|
|
|
|
+ sizeof(*mod->gpl_syms),
|
|
|
|
+ &mod->num_gpl_syms);
|
|
|
|
+ mod->gpl_crcs = section_addr(info, "__kcrctab_gpl");
|
|
|
|
+ mod->gpl_future_syms = section_objs(info,
|
|
|
|
+ "__ksymtab_gpl_future",
|
|
|
|
+ sizeof(*mod->gpl_future_syms),
|
|
|
|
+ &mod->num_gpl_future_syms);
|
|
|
|
+ mod->gpl_future_crcs = section_addr(info, "__kcrctab_gpl_future");
|
|
|
|
|
|
- /* Allow arches to frob section contents and sizes. */
|
|
|
|
- err = module_frob_arch_sections(hdr, sechdrs, secstrings, mod);
|
|
|
|
- if (err < 0)
|
|
|
|
- goto free_mod;
|
|
|
|
|
|
+#ifdef CONFIG_UNUSED_SYMBOLS
|
|
|
|
+ mod->unused_syms = section_objs(info, "__ksymtab_unused",
|
|
|
|
+ sizeof(*mod->unused_syms),
|
|
|
|
+ &mod->num_unused_syms);
|
|
|
|
+ mod->unused_crcs = section_addr(info, "__kcrctab_unused");
|
|
|
|
+ mod->unused_gpl_syms = section_objs(info, "__ksymtab_unused_gpl",
|
|
|
|
+ sizeof(*mod->unused_gpl_syms),
|
|
|
|
+ &mod->num_unused_gpl_syms);
|
|
|
|
+ mod->unused_gpl_crcs = section_addr(info, "__kcrctab_unused_gpl");
|
|
|
|
+#endif
|
|
|
|
+#ifdef CONFIG_CONSTRUCTORS
|
|
|
|
+ mod->ctors = section_objs(info, ".ctors",
|
|
|
|
+ sizeof(*mod->ctors), &mod->num_ctors);
|
|
|
|
+#endif
|
|
|
|
|
|
- if (pcpuindex) {
|
|
|
|
- /* We have a special allocation for this section. */
|
|
|
|
- err = percpu_modalloc(mod, sechdrs[pcpuindex].sh_size,
|
|
|
|
- sechdrs[pcpuindex].sh_addralign);
|
|
|
|
- if (err)
|
|
|
|
- goto free_mod;
|
|
|
|
- sechdrs[pcpuindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
|
|
|
|
- }
|
|
|
|
- /* Keep this around for failure path. */
|
|
|
|
- percpu = mod_percpu(mod);
|
|
|
|
|
|
+#ifdef CONFIG_TRACEPOINTS
|
|
|
|
+ mod->tracepoints = section_objs(info, "__tracepoints",
|
|
|
|
+ sizeof(*mod->tracepoints),
|
|
|
|
+ &mod->num_tracepoints);
|
|
|
|
+#endif
|
|
|
|
+#ifdef CONFIG_EVENT_TRACING
|
|
|
|
+ mod->trace_events = section_objs(info, "_ftrace_events",
|
|
|
|
+ sizeof(*mod->trace_events),
|
|
|
|
+ &mod->num_trace_events);
|
|
|
|
+ /*
|
|
|
|
+ * This section contains pointers to allocated objects in the trace
|
|
|
|
+ * code and not scanning it leads to false positives.
|
|
|
|
+ */
|
|
|
|
+ kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
|
|
|
|
+ mod->num_trace_events, GFP_KERNEL);
|
|
|
|
+#endif
|
|
|
|
+#ifdef CONFIG_FTRACE_MCOUNT_RECORD
|
|
|
|
+ /* sechdrs[0].sh_size is always zero */
|
|
|
|
+ mod->ftrace_callsites = section_objs(info, "__mcount_loc",
|
|
|
|
+ sizeof(*mod->ftrace_callsites),
|
|
|
|
+ &mod->num_ftrace_callsites);
|
|
|
|
+#endif
|
|
|
|
|
|
- /* 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);
|
|
|
|
|
|
+ mod->extable = section_objs(info, "__ex_table",
|
|
|
|
+ sizeof(*mod->extable), &mod->num_exentries);
|
|
|
|
+
|
|
|
|
+ if (section_addr(info, "__obsparm"))
|
|
|
|
+ printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
|
|
|
|
+ mod->name);
|
|
|
|
+
|
|
|
|
+ info->debug = section_objs(info, "__verbose",
|
|
|
|
+ sizeof(*info->debug), &info->num_debug);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int move_module(struct module *mod, struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+ void *ptr;
|
|
|
|
|
|
/* Do the allocs. */
|
|
/* Do the allocs. */
|
|
ptr = module_alloc_update_bounds(mod->core_size);
|
|
ptr = module_alloc_update_bounds(mod->core_size);
|
|
@@ -2291,10 +2350,9 @@ static noinline struct module *load_module(void __user *umod,
|
|
* leak.
|
|
* leak.
|
|
*/
|
|
*/
|
|
kmemleak_not_leak(ptr);
|
|
kmemleak_not_leak(ptr);
|
|
- if (!ptr) {
|
|
|
|
- err = -ENOMEM;
|
|
|
|
- goto free_percpu;
|
|
|
|
- }
|
|
|
|
|
|
+ if (!ptr)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
memset(ptr, 0, mod->core_size);
|
|
memset(ptr, 0, mod->core_size);
|
|
mod->module_core = ptr;
|
|
mod->module_core = ptr;
|
|
|
|
|
|
@@ -2307,50 +2365,40 @@ static noinline struct module *load_module(void __user *umod,
|
|
*/
|
|
*/
|
|
kmemleak_ignore(ptr);
|
|
kmemleak_ignore(ptr);
|
|
if (!ptr && mod->init_size) {
|
|
if (!ptr && mod->init_size) {
|
|
- err = -ENOMEM;
|
|
|
|
- goto free_core;
|
|
|
|
|
|
+ module_free(mod, mod->module_core);
|
|
|
|
+ return -ENOMEM;
|
|
}
|
|
}
|
|
memset(ptr, 0, mod->init_size);
|
|
memset(ptr, 0, mod->init_size);
|
|
mod->module_init = ptr;
|
|
mod->module_init = ptr;
|
|
|
|
|
|
/* Transfer each section which specifies SHF_ALLOC */
|
|
/* Transfer each section which specifies SHF_ALLOC */
|
|
DEBUGP("final section addresses:\n");
|
|
DEBUGP("final section addresses:\n");
|
|
- for (i = 0; i < hdr->e_shnum; i++) {
|
|
|
|
|
|
+ for (i = 0; i < info->hdr->e_shnum; i++) {
|
|
void *dest;
|
|
void *dest;
|
|
|
|
+ Elf_Shdr *shdr = &info->sechdrs[i];
|
|
|
|
|
|
- if (!(sechdrs[i].sh_flags & SHF_ALLOC))
|
|
|
|
|
|
+ if (!(shdr->sh_flags & SHF_ALLOC))
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- if (sechdrs[i].sh_entsize & INIT_OFFSET_MASK)
|
|
|
|
|
|
+ if (shdr->sh_entsize & INIT_OFFSET_MASK)
|
|
dest = mod->module_init
|
|
dest = mod->module_init
|
|
- + (sechdrs[i].sh_entsize & ~INIT_OFFSET_MASK);
|
|
|
|
|
|
+ + (shdr->sh_entsize & ~INIT_OFFSET_MASK);
|
|
else
|
|
else
|
|
- dest = mod->module_core + sechdrs[i].sh_entsize;
|
|
|
|
|
|
+ dest = mod->module_core + shdr->sh_entsize;
|
|
|
|
|
|
- if (sechdrs[i].sh_type != SHT_NOBITS)
|
|
|
|
- memcpy(dest, (void *)sechdrs[i].sh_addr,
|
|
|
|
- sechdrs[i].sh_size);
|
|
|
|
|
|
+ if (shdr->sh_type != SHT_NOBITS)
|
|
|
|
+ memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
|
|
/* Update sh_addr to point to copy in image. */
|
|
/* Update sh_addr to point to copy in image. */
|
|
- sechdrs[i].sh_addr = (unsigned long)dest;
|
|
|
|
- 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);
|
|
|
|
-
|
|
|
|
-#if defined(CONFIG_MODULE_UNLOAD)
|
|
|
|
- mod->refptr = alloc_percpu(struct module_ref);
|
|
|
|
- if (!mod->refptr) {
|
|
|
|
- err = -ENOMEM;
|
|
|
|
- goto free_init;
|
|
|
|
|
|
+ shdr->sh_addr = (unsigned long)dest;
|
|
|
|
+ DEBUGP("\t0x%lx %s\n",
|
|
|
|
+ shdr->sh_addr, info->secstrings + shdr->sh_name);
|
|
}
|
|
}
|
|
-#endif
|
|
|
|
- /* Now we've moved module, initialize linked lists, etc. */
|
|
|
|
- module_unload_init(mod);
|
|
|
|
|
|
|
|
- /* Set up license info based on the info section */
|
|
|
|
- set_license(mod, get_modinfo(sechdrs, infoindex, "license"));
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
|
|
+static int check_module_license_and_versions(struct module *mod)
|
|
|
|
+{
|
|
/*
|
|
/*
|
|
* ndiswrapper is under GPL by itself, but loads proprietary modules.
|
|
* ndiswrapper is under GPL by itself, but loads proprietary modules.
|
|
* Don't use add_taint_module(), as it would prevent ndiswrapper from
|
|
* Don't use add_taint_module(), as it would prevent ndiswrapper from
|
|
@@ -2363,77 +2411,6 @@ static noinline struct module *load_module(void __user *umod,
|
|
if (strcmp(mod->name, "driverloader") == 0)
|
|
if (strcmp(mod->name, "driverloader") == 0)
|
|
add_taint_module(mod, TAINT_PROPRIETARY_MODULE);
|
|
add_taint_module(mod, TAINT_PROPRIETARY_MODULE);
|
|
|
|
|
|
- /* Set up MODINFO_ATTR fields */
|
|
|
|
- setup_modinfo(mod, sechdrs, infoindex);
|
|
|
|
-
|
|
|
|
- /* Fix up syms, so that st_value is a pointer to location. */
|
|
|
|
- err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,
|
|
|
|
- mod);
|
|
|
|
- if (err < 0)
|
|
|
|
- goto cleanup;
|
|
|
|
-
|
|
|
|
- /* Now we've got everything in the final locations, we can
|
|
|
|
- * find optional sections. */
|
|
|
|
- mod->kp = section_objs(hdr, sechdrs, secstrings, "__param",
|
|
|
|
- sizeof(*mod->kp), &mod->num_kp);
|
|
|
|
- mod->syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab",
|
|
|
|
- sizeof(*mod->syms), &mod->num_syms);
|
|
|
|
- mod->crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab");
|
|
|
|
- mod->gpl_syms = section_objs(hdr, sechdrs, secstrings, "__ksymtab_gpl",
|
|
|
|
- sizeof(*mod->gpl_syms),
|
|
|
|
- &mod->num_gpl_syms);
|
|
|
|
- mod->gpl_crcs = section_addr(hdr, sechdrs, secstrings, "__kcrctab_gpl");
|
|
|
|
- mod->gpl_future_syms = section_objs(hdr, sechdrs, secstrings,
|
|
|
|
- "__ksymtab_gpl_future",
|
|
|
|
- sizeof(*mod->gpl_future_syms),
|
|
|
|
- &mod->num_gpl_future_syms);
|
|
|
|
- mod->gpl_future_crcs = section_addr(hdr, sechdrs, secstrings,
|
|
|
|
- "__kcrctab_gpl_future");
|
|
|
|
-
|
|
|
|
-#ifdef CONFIG_UNUSED_SYMBOLS
|
|
|
|
- mod->unused_syms = section_objs(hdr, sechdrs, secstrings,
|
|
|
|
- "__ksymtab_unused",
|
|
|
|
- sizeof(*mod->unused_syms),
|
|
|
|
- &mod->num_unused_syms);
|
|
|
|
- mod->unused_crcs = section_addr(hdr, sechdrs, secstrings,
|
|
|
|
- "__kcrctab_unused");
|
|
|
|
- mod->unused_gpl_syms = section_objs(hdr, sechdrs, secstrings,
|
|
|
|
- "__ksymtab_unused_gpl",
|
|
|
|
- sizeof(*mod->unused_gpl_syms),
|
|
|
|
- &mod->num_unused_gpl_syms);
|
|
|
|
- mod->unused_gpl_crcs = section_addr(hdr, sechdrs, secstrings,
|
|
|
|
- "__kcrctab_unused_gpl");
|
|
|
|
-#endif
|
|
|
|
-#ifdef CONFIG_CONSTRUCTORS
|
|
|
|
- mod->ctors = section_objs(hdr, sechdrs, secstrings, ".ctors",
|
|
|
|
- sizeof(*mod->ctors), &mod->num_ctors);
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-#ifdef CONFIG_TRACEPOINTS
|
|
|
|
- mod->tracepoints = section_objs(hdr, sechdrs, secstrings,
|
|
|
|
- "__tracepoints",
|
|
|
|
- sizeof(*mod->tracepoints),
|
|
|
|
- &mod->num_tracepoints);
|
|
|
|
-#endif
|
|
|
|
-#ifdef CONFIG_EVENT_TRACING
|
|
|
|
- mod->trace_events = section_objs(hdr, sechdrs, secstrings,
|
|
|
|
- "_ftrace_events",
|
|
|
|
- sizeof(*mod->trace_events),
|
|
|
|
- &mod->num_trace_events);
|
|
|
|
- /*
|
|
|
|
- * This section contains pointers to allocated objects in the trace
|
|
|
|
- * code and not scanning it leads to false positives.
|
|
|
|
- */
|
|
|
|
- kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
|
|
|
|
- mod->num_trace_events, GFP_KERNEL);
|
|
|
|
-#endif
|
|
|
|
-#ifdef CONFIG_FTRACE_MCOUNT_RECORD
|
|
|
|
- /* sechdrs[0].sh_size is always zero */
|
|
|
|
- mod->ftrace_callsites = section_objs(hdr, sechdrs, secstrings,
|
|
|
|
- "__mcount_loc",
|
|
|
|
- sizeof(*mod->ftrace_callsites),
|
|
|
|
- &mod->num_ftrace_callsites);
|
|
|
|
-#endif
|
|
|
|
#ifdef CONFIG_MODVERSIONS
|
|
#ifdef CONFIG_MODVERSIONS
|
|
if ((mod->num_syms && !mod->crcs)
|
|
if ((mod->num_syms && !mod->crcs)
|
|
|| (mod->num_gpl_syms && !mod->gpl_crcs)
|
|
|| (mod->num_gpl_syms && !mod->gpl_crcs)
|
|
@@ -2443,56 +2420,16 @@ static noinline struct module *load_module(void __user *umod,
|
|
|| (mod->num_unused_gpl_syms && !mod->unused_gpl_crcs)
|
|
|| (mod->num_unused_gpl_syms && !mod->unused_gpl_crcs)
|
|
#endif
|
|
#endif
|
|
) {
|
|
) {
|
|
- err = try_to_force_load(mod,
|
|
|
|
- "no versions for exported symbols");
|
|
|
|
- if (err)
|
|
|
|
- goto cleanup;
|
|
|
|
|
|
+ return try_to_force_load(mod,
|
|
|
|
+ "no versions for exported symbols");
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
|
|
- /* Now do relocations. */
|
|
|
|
- for (i = 1; i < hdr->e_shnum; i++) {
|
|
|
|
- const char *strtab = (char *)sechdrs[strindex].sh_addr;
|
|
|
|
- unsigned int info = sechdrs[i].sh_info;
|
|
|
|
-
|
|
|
|
- /* Not a valid relocation section? */
|
|
|
|
- if (info >= hdr->e_shnum)
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- /* Don't bother with non-allocated sections */
|
|
|
|
- if (!(sechdrs[info].sh_flags & SHF_ALLOC))
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- if (sechdrs[i].sh_type == SHT_REL)
|
|
|
|
- err = apply_relocate(sechdrs, strtab, symindex, i,mod);
|
|
|
|
- else if (sechdrs[i].sh_type == SHT_RELA)
|
|
|
|
- err = apply_relocate_add(sechdrs, strtab, symindex, i,
|
|
|
|
- mod);
|
|
|
|
- if (err < 0)
|
|
|
|
- goto cleanup;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Set up and sort exception table */
|
|
|
|
- mod->extable = section_objs(hdr, sechdrs, 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);
|
|
|
|
-
|
|
|
|
- add_kallsyms(mod, sechdrs, hdr->e_shnum, symindex, strindex,
|
|
|
|
- symoffs, stroffs, secstrings, strmap);
|
|
|
|
- kfree(strmap);
|
|
|
|
- strmap = NULL;
|
|
|
|
-
|
|
|
|
- if (!mod->taints)
|
|
|
|
- debug = section_objs(hdr, sechdrs, secstrings, "__verbose",
|
|
|
|
- sizeof(*debug), &num_debug);
|
|
|
|
-
|
|
|
|
- err = module_finalize(hdr, sechdrs, mod);
|
|
|
|
- if (err < 0)
|
|
|
|
- goto cleanup;
|
|
|
|
|
|
+static void flush_module_icache(const struct module *mod)
|
|
|
|
+{
|
|
|
|
+ mm_segment_t old_fs;
|
|
|
|
|
|
/* flush the icache in correct context */
|
|
/* flush the icache in correct context */
|
|
old_fs = get_fs();
|
|
old_fs = get_fs();
|
|
@@ -2511,11 +2448,160 @@ static noinline struct module *load_module(void __user *umod,
|
|
(unsigned long)mod->module_core + mod->core_size);
|
|
(unsigned long)mod->module_core + mod->core_size);
|
|
|
|
|
|
set_fs(old_fs);
|
|
set_fs(old_fs);
|
|
|
|
+}
|
|
|
|
|
|
- mod->args = args;
|
|
|
|
- if (section_addr(hdr, sechdrs, secstrings, "__obsparm"))
|
|
|
|
- printk(KERN_WARNING "%s: Ignoring obsolete parameters\n",
|
|
|
|
- mod->name);
|
|
|
|
|
|
+static struct module *layout_and_allocate(struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ /* Module within temporary copy. */
|
|
|
|
+ struct module *mod;
|
|
|
|
+ Elf_Shdr *pcpusec;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ mod = setup_load_info(info);
|
|
|
|
+ if (IS_ERR(mod))
|
|
|
|
+ return mod;
|
|
|
|
+
|
|
|
|
+ err = check_modinfo(mod, info);
|
|
|
|
+ if (err)
|
|
|
|
+ return ERR_PTR(err);
|
|
|
|
+
|
|
|
|
+ /* Allow arches to frob section contents and sizes. */
|
|
|
|
+ err = module_frob_arch_sections(info->hdr, info->sechdrs,
|
|
|
|
+ info->secstrings, mod);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ pcpusec = &info->sechdrs[info->index.pcpu];
|
|
|
|
+ if (pcpusec->sh_size) {
|
|
|
|
+ /* We have a special allocation for this section. */
|
|
|
|
+ err = percpu_modalloc(mod,
|
|
|
|
+ pcpusec->sh_size, pcpusec->sh_addralign);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
|
|
+ pcpusec->sh_flags &= ~(unsigned long)SHF_ALLOC;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* 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);
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+ layout_symtab(mod, info);
|
|
|
|
+
|
|
|
|
+ /* Allocate and move to the final place */
|
|
|
|
+ err = move_module(mod, info);
|
|
|
|
+ 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);
|
|
|
|
+ return mod;
|
|
|
|
+
|
|
|
|
+free_strmap:
|
|
|
|
+ kfree(info->strmap);
|
|
|
|
+free_percpu:
|
|
|
|
+ percpu_modfree(mod);
|
|
|
|
+out:
|
|
|
|
+ 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);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int post_relocation(struct module *mod, const struct load_info *info)
|
|
|
|
+{
|
|
|
|
+ /* Sort exception table now relocations are done. */
|
|
|
|
+ sort_extable(mod->extable, mod->extable + mod->num_exentries);
|
|
|
|
+
|
|
|
|
+ /* Copy relocated percpu area over. */
|
|
|
|
+ percpu_modcopy(mod, (void *)info->sechdrs[info->index.pcpu].sh_addr,
|
|
|
|
+ info->sechdrs[info->index.pcpu].sh_size);
|
|
|
|
+
|
|
|
|
+ /* Setup kallsyms-specific fields. */
|
|
|
|
+ add_kallsyms(mod, info);
|
|
|
|
+
|
|
|
|
+ /* Arch-specific module finalizing. */
|
|
|
|
+ return module_finalize(info->hdr, info->sechdrs, mod);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Allocate and load the module: note that size of section 0 is always
|
|
|
|
+ zero, and we rely on this for optional sections. */
|
|
|
|
+static struct module *load_module(void __user *umod,
|
|
|
|
+ unsigned long len,
|
|
|
|
+ const char __user *uargs)
|
|
|
|
+{
|
|
|
|
+ struct load_info info = { NULL, };
|
|
|
|
+ struct module *mod;
|
|
|
|
+ long err;
|
|
|
|
+
|
|
|
|
+ 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_copy;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Now module is in final location, initialize linked lists, etc. */
|
|
|
|
+ err = module_unload_init(mod);
|
|
|
|
+ if (err)
|
|
|
|
+ goto free_module;
|
|
|
|
+
|
|
|
|
+ /* Now we've got everything in the final locations, we can
|
|
|
|
+ * find optional sections. */
|
|
|
|
+ find_module_sections(mod, &info);
|
|
|
|
+
|
|
|
|
+ err = check_module_license_and_versions(mod);
|
|
|
|
+ if (err)
|
|
|
|
+ goto free_unload;
|
|
|
|
+
|
|
|
|
+ /* Set up MODINFO_ATTR fields */
|
|
|
|
+ setup_modinfo(mod, &info);
|
|
|
|
+
|
|
|
|
+ /* Fix up syms, so that st_value is a pointer to location. */
|
|
|
|
+ err = simplify_symbols(mod, &info);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto free_modinfo;
|
|
|
|
+
|
|
|
|
+ err = apply_relocations(mod, &info);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto free_modinfo;
|
|
|
|
+
|
|
|
|
+ err = post_relocation(mod, &info);
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto free_modinfo;
|
|
|
|
+
|
|
|
|
+ flush_module_icache(mod);
|
|
|
|
+
|
|
|
|
+ /* Now copy in args */
|
|
|
|
+ mod->args = strndup_user(uargs, ~0UL >> 1);
|
|
|
|
+ if (IS_ERR(mod->args)) {
|
|
|
|
+ err = PTR_ERR(mod->args);
|
|
|
|
+ goto free_arch_cleanup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Mark state as coming so strong_try_module_get() ignores us. */
|
|
|
|
+ mod->state = MODULE_STATE_COMING;
|
|
|
|
|
|
/* Now sew it into the lists so we can get lockdep and oops
|
|
/* Now sew it into the lists so we can get lockdep and oops
|
|
* info during argument parsing. Noone should access us, since
|
|
* info during argument parsing. Noone should access us, since
|
|
@@ -2530,8 +2616,9 @@ static noinline struct module *load_module(void __user *umod,
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
|
|
- if (debug)
|
|
|
|
- dynamic_debug_setup(debug, num_debug);
|
|
|
|
|
|
+ /* This has to be done once we're sure module name is unique. */
|
|
|
|
+ if (!mod->taints)
|
|
|
|
+ dynamic_debug_setup(info.debug, info.num_debug);
|
|
|
|
|
|
/* Find duplicate symbols */
|
|
/* Find duplicate symbols */
|
|
err = verify_export_symbols(mod);
|
|
err = verify_export_symbols(mod);
|
|
@@ -2541,23 +2628,22 @@ static noinline struct module *load_module(void __user *umod,
|
|
list_add_rcu(&mod->list, &modules);
|
|
list_add_rcu(&mod->list, &modules);
|
|
mutex_unlock(&module_mutex);
|
|
mutex_unlock(&module_mutex);
|
|
|
|
|
|
|
|
+ /* Module is ready to execute: parsing args may do that. */
|
|
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);
|
|
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, NULL);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
goto unlink;
|
|
goto unlink;
|
|
|
|
|
|
- err = mod_sysfs_setup(mod, mod->kp, mod->num_kp);
|
|
|
|
|
|
+ /* Link in to syfs. */
|
|
|
|
+ err = mod_sysfs_setup(mod, &info, mod->kp, mod->num_kp);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
goto unlink;
|
|
goto unlink;
|
|
|
|
|
|
- add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
|
|
|
- add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
|
|
|
|
-
|
|
|
|
- /* Get rid of temporary copy */
|
|
|
|
- vfree(hdr);
|
|
|
|
-
|
|
|
|
- trace_module_load(mod);
|
|
|
|
|
|
+ /* Get rid of temporary copy and strmap. */
|
|
|
|
+ kfree(info.strmap);
|
|
|
|
+ free_copy(&info);
|
|
|
|
|
|
/* Done! */
|
|
/* Done! */
|
|
|
|
+ trace_module_load(mod);
|
|
return mod;
|
|
return mod;
|
|
|
|
|
|
unlink:
|
|
unlink:
|
|
@@ -2565,35 +2651,23 @@ static noinline struct module *load_module(void __user *umod,
|
|
/* Unlink carefully: kallsyms could be walking list. */
|
|
/* Unlink carefully: kallsyms could be walking list. */
|
|
list_del_rcu(&mod->list);
|
|
list_del_rcu(&mod->list);
|
|
ddebug:
|
|
ddebug:
|
|
- dynamic_debug_remove(debug);
|
|
|
|
|
|
+ if (!mod->taints)
|
|
|
|
+ dynamic_debug_remove(info.debug);
|
|
unlock:
|
|
unlock:
|
|
mutex_unlock(&module_mutex);
|
|
mutex_unlock(&module_mutex);
|
|
synchronize_sched();
|
|
synchronize_sched();
|
|
|
|
+ kfree(mod->args);
|
|
|
|
+ free_arch_cleanup:
|
|
module_arch_cleanup(mod);
|
|
module_arch_cleanup(mod);
|
|
- cleanup:
|
|
|
|
|
|
+ free_modinfo:
|
|
free_modinfo(mod);
|
|
free_modinfo(mod);
|
|
|
|
+ free_unload:
|
|
module_unload_free(mod);
|
|
module_unload_free(mod);
|
|
-#if defined(CONFIG_MODULE_UNLOAD)
|
|
|
|
- free_percpu(mod->refptr);
|
|
|
|
- free_init:
|
|
|
|
-#endif
|
|
|
|
- module_free(mod, mod->module_init);
|
|
|
|
- free_core:
|
|
|
|
- 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(args);
|
|
|
|
- kfree(strmap);
|
|
|
|
- free_hdr:
|
|
|
|
- vfree(hdr);
|
|
|
|
|
|
+ free_module:
|
|
|
|
+ module_deallocate(mod, &info);
|
|
|
|
+ free_copy:
|
|
|
|
+ free_copy(&info);
|
|
return ERR_PTR(err);
|
|
return ERR_PTR(err);
|
|
-
|
|
|
|
- truncated:
|
|
|
|
- printk(KERN_ERR "Module len %lu truncated\n", len);
|
|
|
|
- err = -ENOEXEC;
|
|
|
|
- goto free_hdr;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Call module constructors. */
|
|
/* Call module constructors. */
|