|
@@ -65,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;
|
|
|
|
|
@@ -115,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;
|
|
@@ -179,13 +177,52 @@ module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
|
|
|
- struct module *me)
|
|
|
+static int apply_rela_bits(Elf_Addr loc, Elf_Addr val,
|
|
|
+ int sign, int bits, int shift)
|
|
|
+{
|
|
|
+ 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,
|
|
|
+ const char *strtab, struct module *me)
|
|
|
{
|
|
|
struct mod_arch_syminfo *info;
|
|
|
Elf_Addr loc, val;
|
|
|
int r_type, r_sym;
|
|
|
+ int rc;
|
|
|
|
|
|
/* This is where to make the change */
|
|
|
loc = base + rela->r_offset;
|
|
@@ -205,20 +242,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. */
|
|
@@ -227,15 +261,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. */
|
|
@@ -260,26 +294,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. */
|
|
@@ -321,17 +353,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. */
|
|
@@ -339,20 +371,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. */
|
|
@@ -360,19 +392,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;
|
|
@@ -388,7 +426,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;
|
|
|
}
|