aboutsummaryrefslogtreecommitdiff
path: root/arch/s390/kernel/module.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/kernel/module.c')
-rw-r--r--arch/s390/kernel/module.c163
1 files changed, 97 insertions, 66 deletions
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
index f7167ee4604..b89b59158b9 100644
--- a/arch/s390/kernel/module.c
+++ b/arch/s390/kernel/module.c
@@ -1,9 +1,8 @@
/*
- * arch/s390/kernel/module.c - Kernel module help for s390.
+ * Kernel module help for s390.
*
* S390 version
- * Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 2002, 2003
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*
@@ -45,12 +44,16 @@
#define PLT_ENTRY_SIZE 20
#endif /* CONFIG_64BIT */
+#ifdef CONFIG_64BIT
void *module_alloc(unsigned long size)
{
- if (size == 0)
+ if (PAGE_ALIGN(size) > MODULES_LEN)
return NULL;
- return vmalloc(size);
+ return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
+ GFP_KERNEL, PAGE_KERNEL, NUMA_NO_NODE,
+ __builtin_return_address(0));
}
+#endif
/* Free memory returned from module_alloc */
void module_free(struct module *mod, void *module_region)
@@ -62,8 +65,7 @@ void module_free(struct module *mod, void *module_region)
vfree(module_region);
}
-static void
-check_rela(Elf_Rela *rela, struct module *me)
+static void check_rela(Elf_Rela *rela, struct module *me)
{
struct mod_arch_syminfo *info;
@@ -112,9 +114,8 @@ check_rela(Elf_Rela *rela, struct module *me)
* Account for GOT and PLT relocations. We can't add sections for
* got and plt but we can increase the core module size.
*/
-int
-module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
- char *secstrings, struct module *me)
+int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+ char *secstrings, struct module *me)
{
Elf_Shdr *symtab;
Elf_Sym *symbols;
@@ -176,22 +177,52 @@ module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
return 0;
}
-int
-apply_relocate(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex,
- unsigned int relsec, struct module *me)
+static int apply_rela_bits(Elf_Addr loc, Elf_Addr val,
+ int sign, int bits, int shift)
{
- printk(KERN_ERR "module %s: RELOCATION unsupported\n",
- me->name);
- return -ENOEXEC;
+ unsigned long umax;
+ long min, max;
+
+ if (val & ((1UL << shift) - 1))
+ return -ENOEXEC;
+ if (sign) {
+ val = (Elf_Addr)(((long) val) >> shift);
+ min = -(1L << (bits - 1));
+ max = (1L << (bits - 1)) - 1;
+ if ((long) val < min || (long) val > max)
+ return -ENOEXEC;
+ } else {
+ val >>= shift;
+ umax = ((1UL << (bits - 1)) << 1) - 1;
+ if ((unsigned long) val > umax)
+ return -ENOEXEC;
+ }
+
+ if (bits == 8)
+ *(unsigned char *) loc = val;
+ else if (bits == 12)
+ *(unsigned short *) loc = (val & 0xfff) |
+ (*(unsigned short *) loc & 0xf000);
+ else if (bits == 16)
+ *(unsigned short *) loc = val;
+ else if (bits == 20)
+ *(unsigned int *) loc = (val & 0xfff) << 16 |
+ (val & 0xff000) >> 4 |
+ (*(unsigned int *) loc & 0xf00000ff);
+ else if (bits == 32)
+ *(unsigned int *) loc = val;
+ else if (bits == 64)
+ *(unsigned long *) loc = val;
+ return 0;
}
-static int
-apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
- struct module *me)
+static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
+ const char *strtab, struct module *me)
{
struct mod_arch_syminfo *info;
Elf_Addr loc, val;
int r_type, r_sym;
+ int rc = -ENOEXEC;
/* This is where to make the change */
loc = base + rela->r_offset;
@@ -203,6 +234,9 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val = symtab[r_sym].st_value;
switch (r_type) {
+ case R_390_NONE: /* No relocation. */
+ rc = 0;
+ break;
case R_390_8: /* Direct 8 bit. */
case R_390_12: /* Direct 12 bit. */
case R_390_16: /* Direct 16 bit. */
@@ -211,20 +245,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_64: /* Direct 64 bit. */
val += rela->r_addend;
if (r_type == R_390_8)
- *(unsigned char *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 8, 0);
else if (r_type == R_390_12)
- *(unsigned short *) loc = (val & 0xfff) |
- (*(unsigned short *) loc & 0xf000);
+ rc = apply_rela_bits(loc, val, 0, 12, 0);
else if (r_type == R_390_16)
- *(unsigned short *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_20)
- *(unsigned int *) loc =
- (*(unsigned int *) loc & 0xf00000ff) |
- (val & 0xfff) << 16 | (val & 0xff000) >> 4;
+ rc = apply_rela_bits(loc, val, 1, 20, 0);
else if (r_type == R_390_32)
- *(unsigned int *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_64)
- *(unsigned long *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 64, 0);
break;
case R_390_PC16: /* PC relative 16 bit. */
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
@@ -233,15 +264,15 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_PC64: /* PC relative 64 bit. */
val += rela->r_addend - loc;
if (r_type == R_390_PC16)
- *(unsigned short *) loc = val;
+ rc = apply_rela_bits(loc, val, 1, 16, 0);
else if (r_type == R_390_PC16DBL)
- *(unsigned short *) loc = val >> 1;
+ rc = apply_rela_bits(loc, val, 1, 16, 1);
else if (r_type == R_390_PC32DBL)
- *(unsigned int *) loc = val >> 1;
+ rc = apply_rela_bits(loc, val, 1, 32, 1);
else if (r_type == R_390_PC32)
- *(unsigned int *) loc = val;
+ rc = apply_rela_bits(loc, val, 1, 32, 0);
else if (r_type == R_390_PC64)
- *(unsigned long *) loc = val;
+ rc = apply_rela_bits(loc, val, 1, 64, 0);
break;
case R_390_GOT12: /* 12 bit GOT offset. */
case R_390_GOT16: /* 16 bit GOT offset. */
@@ -266,26 +297,24 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val = info->got_offset + rela->r_addend;
if (r_type == R_390_GOT12 ||
r_type == R_390_GOTPLT12)
- *(unsigned short *) loc = (val & 0xfff) |
- (*(unsigned short *) loc & 0xf000);
+ rc = apply_rela_bits(loc, val, 0, 12, 0);
else if (r_type == R_390_GOT16 ||
r_type == R_390_GOTPLT16)
- *(unsigned short *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_GOT20 ||
r_type == R_390_GOTPLT20)
- *(unsigned int *) loc =
- (*(unsigned int *) loc & 0xf00000ff) |
- (val & 0xfff) << 16 | (val & 0xff000) >> 4;
+ rc = apply_rela_bits(loc, val, 1, 20, 0);
else if (r_type == R_390_GOT32 ||
r_type == R_390_GOTPLT32)
- *(unsigned int *) loc = val;
- else if (r_type == R_390_GOTENT ||
- r_type == R_390_GOTPLTENT)
- *(unsigned int *) loc =
- (val + (Elf_Addr) me->module_core - loc) >> 1;
+ rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_GOT64 ||
r_type == R_390_GOTPLT64)
- *(unsigned long *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 64, 0);
+ else if (r_type == R_390_GOTENT ||
+ r_type == R_390_GOTPLTENT) {
+ val += (Elf_Addr) me->module_core - loc;
+ rc = apply_rela_bits(loc, val, 1, 32, 1);
+ }
break;
case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
@@ -327,17 +356,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val += rela->r_addend - loc;
}
if (r_type == R_390_PLT16DBL)
- *(unsigned short *) loc = val >> 1;
+ rc = apply_rela_bits(loc, val, 1, 16, 1);
else if (r_type == R_390_PLTOFF16)
- *(unsigned short *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_PLT32DBL)
- *(unsigned int *) loc = val >> 1;
+ rc = apply_rela_bits(loc, val, 1, 32, 1);
else if (r_type == R_390_PLT32 ||
r_type == R_390_PLTOFF32)
- *(unsigned int *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_PLT64 ||
r_type == R_390_PLTOFF64)
- *(unsigned long *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 64, 0);
break;
case R_390_GOTOFF16: /* 16 bit offset to GOT. */
case R_390_GOTOFF32: /* 32 bit offset to GOT. */
@@ -345,20 +374,20 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val = val + rela->r_addend -
((Elf_Addr) me->module_core + me->arch.got_offset);
if (r_type == R_390_GOTOFF16)
- *(unsigned short *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_GOTOFF32)
- *(unsigned int *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 32, 0);
else if (r_type == R_390_GOTOFF64)
- *(unsigned long *) loc = val;
+ rc = apply_rela_bits(loc, val, 0, 64, 0);
break;
case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */
val = (Elf_Addr) me->module_core + me->arch.got_offset +
rela->r_addend - loc;
if (r_type == R_390_GOTPC)
- *(unsigned int *) loc = val;
+ rc = apply_rela_bits(loc, val, 1, 32, 0);
else if (r_type == R_390_GOTPCDBL)
- *(unsigned int *) loc = val >> 1;
+ rc = apply_rela_bits(loc, val, 1, 32, 1);
break;
case R_390_COPY:
case R_390_GLOB_DAT: /* Create GOT entry. */
@@ -366,19 +395,25 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_RELATIVE: /* Adjust by program base. */
/* Only needed if we want to support loading of
modules linked with -shared. */
- break;
+ return -ENOEXEC;
default:
- printk(KERN_ERR "module %s: Unknown relocation: %u\n",
+ printk(KERN_ERR "module %s: unknown relocation: %u\n",
me->name, r_type);
return -ENOEXEC;
}
+ if (rc) {
+ printk(KERN_ERR "module %s: relocation error for symbol %s "
+ "(r_type %i, value 0x%lx)\n",
+ me->name, strtab + symtab[r_sym].st_name,
+ r_type, (unsigned long) val);
+ return rc;
+ }
return 0;
}
-int
-apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
- unsigned int symindex, unsigned int relsec,
- struct module *me)
+int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec,
+ struct module *me)
{
Elf_Addr base;
Elf_Sym *symtab;
@@ -394,7 +429,7 @@ apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
n = sechdrs[relsec].sh_size / sizeof(Elf_Rela);
for (i = 0; i < n; i++, rela++) {
- rc = apply_rela(rela, base, symtab, me);
+ rc = apply_rela(rela, base, symtab, strtab, me);
if (rc)
return rc;
}
@@ -409,7 +444,3 @@ int module_finalize(const Elf_Ehdr *hdr,
me->arch.syminfo = NULL;
return 0;
}
-
-void module_arch_cleanup(struct module *mod)
-{
-}