diff options
Diffstat (limited to 'arch/s390/kernel/module.c')
| -rw-r--r-- | arch/s390/kernel/module.c | 143 | 
1 files changed, 92 insertions, 51 deletions
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 4610deafd95..7845e15a17d 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -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 = -ENOEXEC;  	/* This is where to make the change */  	loc = base + rela->r_offset; @@ -197,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.  */ @@ -205,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.  */ @@ -227,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.  */ @@ -260,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.  */ @@ -321,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.  */ @@ -339,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.  */ @@ -360,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; @@ -388,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;  	}  |