diff options
Diffstat (limited to 'arch/mips/kernel/module.c')
| -rw-r--r-- | arch/mips/kernel/module.c | 208 |
1 files changed, 48 insertions, 160 deletions
diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c index e7ed0ac4853..2a52568dbcd 100644 --- a/arch/mips/kernel/module.c +++ b/arch/mips/kernel/module.c @@ -22,13 +22,16 @@ #include <linux/moduleloader.h> #include <linux/elf.h> +#include <linux/mm.h> +#include <linux/numa.h> #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/kernel.h> -#include <linux/module.h> #include <linux/spinlock.h> +#include <linux/jump_label.h> + #include <asm/pgtable.h> /* MODULE_START */ struct mips_hi16 { @@ -37,47 +40,19 @@ struct mips_hi16 { Elf_Addr value; }; -static struct mips_hi16 *mips_hi16_list; - static LIST_HEAD(dbe_list); static DEFINE_SPINLOCK(dbe_lock); -void *module_alloc(unsigned long size) -{ #ifdef MODULE_START - struct vm_struct *area; - - size = PAGE_ALIGN(size); - if (!size) - return NULL; - - area = __get_vm_area(size, VM_ALLOC, MODULE_START, MODULE_END); - if (!area) - return NULL; - - return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL); -#else - if (size == 0) - return NULL; - return vmalloc(size); -#endif -} - -/* Free memory returned from module_alloc */ -void module_free(struct module *mod, void *module_region) -{ - vfree(module_region); - /* FIXME: If module_region == mod->init_region, trim exception - table entries. */ -} - -int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, - char *secstrings, struct module *mod) +void *module_alloc(unsigned long size) { - return 0; + return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END, + GFP_KERNEL, PAGE_KERNEL, NUMA_NO_NODE, + __builtin_return_address(0)); } +#endif -static int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) +int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) { return 0; } @@ -89,40 +64,14 @@ static int apply_r_mips_32_rel(struct module *me, u32 *location, Elf_Addr v) return 0; } -static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v) -{ - *location = v; - - return 0; -} - static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) { if (v % 4) { - printk(KERN_ERR "module %s: dangerous relocation\n", me->name); - return -ENOEXEC; - } - - if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { - printk(KERN_ERR - "module %s: relocation overflow\n", + pr_err("module %s: dangerous R_MIPS_26 REL relocation\n", me->name); return -ENOEXEC; } - *location = (*location & ~0x03ffffff) | - ((*location + (v >> 2)) & 0x03ffffff); - - return 0; -} - -static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v) -{ - if (v % 4) { - printk(KERN_ERR "module %s: dangerous relocation\n", me->name); - return -ENOEXEC; - } - if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { printk(KERN_ERR "module %s: relocation overflow\n", @@ -130,7 +79,8 @@ static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v) return -ENOEXEC; } - *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff); + *location = (*location & ~0x03ffffff) | + ((*location + (v >> 2)) & 0x03ffffff); return 0; } @@ -150,32 +100,34 @@ static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) n->addr = (Elf_Addr *)location; n->value = v; - n->next = mips_hi16_list; - mips_hi16_list = n; + n->next = me->arch.r_mips_hi16_list; + me->arch.r_mips_hi16_list = n; return 0; } -static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v) +static void free_relocation_chain(struct mips_hi16 *l) { - *location = (*location & 0xffff0000) | - ((((long long) v + 0x8000LL) >> 16) & 0xffff); + struct mips_hi16 *next; - return 0; + while (l) { + next = l->next; + kfree(l); + l = next; + } } static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) { unsigned long insnlo = *location; + struct mips_hi16 *l; Elf_Addr val, vallo; - /* Sign extend the addend we extract from the lo insn. */ + /* Sign extend the addend we extract from the lo insn. */ vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; - if (mips_hi16_list != NULL) { - struct mips_hi16 *l; - - l = mips_hi16_list; + if (me->arch.r_mips_hi16_list != NULL) { + l = me->arch.r_mips_hi16_list; while (l != NULL) { struct mips_hi16 *next; unsigned long insn; @@ -210,11 +162,11 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) l = next; } - mips_hi16_list = NULL; + me->arch.r_mips_hi16_list = NULL; } /* - * Ok, we're done with the HI16 relocs. Now deal with the LO16. + * Ok, we're done with the HI16 relocs. Now deal with the LO16. */ val = v + vallo; insnlo = (insnlo & ~0xffff) | (val & 0xffff); @@ -223,41 +175,12 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) return 0; out_danger: - printk(KERN_ERR "module %s: dangerous " "relocation\n", me->name); - - return -ENOEXEC; -} + free_relocation_chain(l); + me->arch.r_mips_hi16_list = NULL; -static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v) -{ - *location = (*location & 0xffff0000) | (v & 0xffff); - - return 0; -} - -static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v) -{ - *(Elf_Addr *)location = v; + pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name); - return 0; -} - -static int apply_r_mips_higher_rela(struct module *me, u32 *location, - Elf_Addr v) -{ - *location = (*location & 0xffff0000) | - ((((long long) v + 0x80008000LL) >> 32) & 0xffff); - - return 0; -} - -static int apply_r_mips_highest_rela(struct module *me, u32 *location, - Elf_Addr v) -{ - *location = (*location & 0xffff0000) | - ((((long long) v + 0x800080008000LL) >> 48) & 0xffff); - - return 0; + return -ENOEXEC; } static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, @@ -269,18 +192,6 @@ static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, [R_MIPS_LO16] = apply_r_mips_lo16_rel }; -static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, - Elf_Addr v) = { - [R_MIPS_NONE] = apply_r_mips_none, - [R_MIPS_32] = apply_r_mips_32_rela, - [R_MIPS_26] = apply_r_mips_26_rela, - [R_MIPS_HI16] = apply_r_mips_hi16_rela, - [R_MIPS_LO16] = apply_r_mips_lo16_rela, - [R_MIPS_64] = apply_r_mips_64_rela, - [R_MIPS_HIGHER] = apply_r_mips_higher_rela, - [R_MIPS_HIGHEST] = apply_r_mips_highest_rela -}; - int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) @@ -295,6 +206,7 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, pr_debug("Applying relocate section %u to %u\n", relsec, sechdrs[relsec].sh_info); + me->arch.r_mips_hi16_list = NULL; for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* This is where to make the change */ location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr @@ -302,7 +214,7 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, /* This is the symbol it is referring to */ sym = (Elf_Sym *)sechdrs[symindex].sh_addr + ELF_MIPS_R_SYM(rel[i]); - if (!sym->st_value) { + if (IS_ERR_VALUE(sym->st_value)) { /* Ignore unresolved weak symbol */ if (ELF_ST_BIND(sym->st_info) == STB_WEAK) continue; @@ -318,44 +230,17 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, return res; } - return 0; -} - -int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *me) -{ - Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr; - Elf_Sym *sym; - u32 *location; - unsigned int i; - Elf_Addr v; - int res; - - pr_debug("Applying relocate section %u to %u\n", relsec, - sechdrs[relsec].sh_info); - - for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { - /* This is where to make the change */ - location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr - + rel[i].r_offset; - /* This is the symbol it is referring to */ - sym = (Elf_Sym *)sechdrs[symindex].sh_addr - + ELF_MIPS_R_SYM(rel[i]); - if (!sym->st_value) { - /* Ignore unresolved weak symbol */ - if (ELF_ST_BIND(sym->st_info) == STB_WEAK) - continue; - printk(KERN_WARNING "%s: Unknown symbol %s\n", - me->name, strtab + sym->st_name); - return -ENOENT; - } - - v = sym->st_value + rel[i].r_addend; + /* + * Normally the hi16 list should be deallocated at this point. A + * malformed binary however could contain a series of R_MIPS_HI16 + * relocations not followed by a R_MIPS_LO16 relocation. In that + * case, free up the list and return an error. + */ + if (me->arch.r_mips_hi16_list) { + free_relocation_chain(me->arch.r_mips_hi16_list); + me->arch.r_mips_hi16_list = NULL; - res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v); - if (res) - return res; + return -ENOEXEC; } return 0; @@ -377,7 +262,7 @@ const struct exception_table_entry *search_module_dbetables(unsigned long addr) spin_unlock_irqrestore(&dbe_lock, flags); /* Now, if we found one, we are running inside it now, hence - we cannot unload the module, hence no refcnt needed. */ + we cannot unload the module, hence no refcnt needed. */ return e; } @@ -389,6 +274,9 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *s; char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + /* Make jump label nops. */ + jump_label_apply_nops(me); + INIT_LIST_HEAD(&me->arch.dbe_list); for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { if (strcmp("__dbe_table", secstrings + s->sh_name) != 0) |
