Linus TorvaldsLinux-2.6.12-rc21da177e21 years agoHistory
822 lines · 21.6 KB · C
RawDownload
/*    Kernel dynamically loadable module help for PARISC. * *    The best reference for this stuff is probably the Processor- *    Specific ELF Supplement for PA-RISC: *        http://ftp.parisc-linux.org/docs/arch/elf-pa-hp.pdf * *    Linux/PA-RISC Project (http://www.parisc-linux.org/) *    Copyright (C) 2003 Randolph Chung <tausq at debian . org> * * *    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 *    the Free Software Foundation; either version 2 of the License, or *    (at your option) any later version. * *    This program is distributed in the hope that it will be useful, *    but WITHOUT ANY WARRANTY; without even the implied warranty of *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *    GNU General Public License for more details. * *    You should have received a copy of the GNU General Public License *    along with this program; if not, write to the Free Software *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * * *    Notes: *    - SEGREL32 handling *      We are not doing SEGREL32 handling correctly. According to the ABI, we *      should do a value offset, like this: *			if (is_init(me, (void *)val)) *				val -= (uint32_t)me->module_init; *			else *				val -= (uint32_t)me->module_core; *	However, SEGREL32 is used only for PARISC unwind entries, and we want *	those entries to have an absolute address, and not just an offset. * *	The unwind table mechanism has the ability to specify an offset for  *	the unwind table; however, because we split off the init functions into *	a different piece of memory, it is not possible to do this using a  *	single offset. Instead, we use the above hack for now. */ #include <linux/moduleloader.h>#include <linux/elf.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/string.h>#include <linux/kernel.h> #include <asm/unwind.h> #if 0#define DEBUGP printk#else#define DEBUGP(fmt...)#endif #define CHECK_RELOC(val, bits) \	if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 )  ||	\	     ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \		printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \		me->name, strtab + sym->st_name, (unsigned long)val, bits); \		return -ENOEXEC;			\	} /* Maximum number of GOT entries. We use a long displacement ldd from * the bottom of the table, which has a maximum signed displacement of * 0x3fff; however, since we're only going forward, this becomes * 0x1fff, and thus, since each GOT entry is 8 bytes long we can have * at most 1023 entries */#define MAX_GOTS	1023 /* three functions to determine where in the module core * or init pieces the location is */static inline int is_init(struct module *me, void *loc){	return (loc >= me->module_init &&		loc <= (me->module_init + me->init_size));} static inline int is_core(struct module *me, void *loc){	return (loc >= me->module_core &&		loc <= (me->module_core + me->core_size));} static inline int is_local(struct module *me, void *loc){	return is_init(me, loc) || is_core(me, loc);}  #ifndef __LP64__struct got_entry {	Elf32_Addr addr;}; #define Elf_Fdesc	Elf32_Fdesc struct stub_entry {	Elf32_Word insns[2]; /* each stub entry has two insns */};#elsestruct got_entry {	Elf64_Addr addr;}; #define Elf_Fdesc	Elf64_Fdesc struct stub_entry {	Elf64_Word insns[4]; /* each stub entry has four insns */};#endif /* Field selection types defined by hppa */#define rnd(x)			(((x)+0x1000)&~0x1fff)/* fsel: full 32 bits */#define fsel(v,a)		((v)+(a))/* lsel: select left 21 bits */#define lsel(v,a)		(((v)+(a))>>11)/* rsel: select right 11 bits */#define rsel(v,a)		(((v)+(a))&0x7ff)/* lrsel with rounding of addend to nearest 8k */#define lrsel(v,a)		(((v)+rnd(a))>>11)/* rrsel with rounding of addend to nearest 8k */#define rrsel(v,a)		((((v)+rnd(a))&0x7ff)+((a)-rnd(a))) #define mask(x,sz)		((x) & ~((1<<(sz))-1))  /* The reassemble_* functions prepare an immediate value for   insertion into an opcode. pa-risc uses all sorts of weird bitfields   in the instruction to hold the value.  */static inline int reassemble_14(int as14){	return (((as14 & 0x1fff) << 1) |		((as14 & 0x2000) >> 13));} static inline int reassemble_17(int as17){	return (((as17 & 0x10000) >> 16) |		((as17 & 0x0f800) << 5) |		((as17 & 0x00400) >> 8) |		((as17 & 0x003ff) << 3));} static inline int reassemble_21(int as21){	return (((as21 & 0x100000) >> 20) |		((as21 & 0x0ffe00) >> 8) |		((as21 & 0x000180) << 7) |		((as21 & 0x00007c) << 14) |		((as21 & 0x000003) << 12));} static inline int reassemble_22(int as22){	return (((as22 & 0x200000) >> 21) |		((as22 & 0x1f0000) << 5) |		((as22 & 0x00f800) << 5) |		((as22 & 0x000400) >> 8) |		((as22 & 0x0003ff) << 3));} void *module_alloc(unsigned long size){	if (size == 0)		return NULL;	return vmalloc(size);} #ifndef __LP64__static inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n){	return 0;} static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n){	return 0;} static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n){	unsigned long cnt = 0; 	for (; n > 0; n--, rela++)	{		switch (ELF32_R_TYPE(rela->r_info)) {			case R_PARISC_PCREL17F:			case R_PARISC_PCREL22F:				cnt++;		}	} 	return cnt;}#elsestatic inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n){	unsigned long cnt = 0; 	for (; n > 0; n--, rela++)	{		switch (ELF64_R_TYPE(rela->r_info)) {			case R_PARISC_LTOFF21L:			case R_PARISC_LTOFF14R:			case R_PARISC_PCREL22F:				cnt++;		}	} 	return cnt;} static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n){	unsigned long cnt = 0; 	for (; n > 0; n--, rela++)	{		switch (ELF64_R_TYPE(rela->r_info)) {			case R_PARISC_FPTR64:				cnt++;		}	} 	return cnt;} static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n){	unsigned long cnt = 0; 	for (; n > 0; n--, rela++)	{		switch (ELF64_R_TYPE(rela->r_info)) {			case R_PARISC_PCREL22F:				cnt++;		}	} 	return cnt;}#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. */} #define CONST int module_frob_arch_sections(CONST Elf_Ehdr *hdr,			      CONST Elf_Shdr *sechdrs,			      CONST char *secstrings,			      struct module *me){	unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0;	unsigned int i; 	for (i = 1; i < hdr->e_shnum; i++) {		const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset;		unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); 		if (strncmp(secstrings + sechdrs[i].sh_name,			    ".PARISC.unwind", 14) == 0)			me->arch.unwind_section = i; 		if (sechdrs[i].sh_type != SHT_RELA)			continue; 		/* some of these are not relevant for 32-bit/64-bit		 * we leave them here to make the code common. the		 * compiler will do its thing and optimize out the		 * stuff we don't need		 */		gots += count_gots(rels, nrels);		fdescs += count_fdescs(rels, nrels);		if(strncmp(secstrings + sechdrs[i].sh_name,			   ".rela.init", 10) == 0)			init_stubs += count_stubs(rels, nrels);		else			stubs += count_stubs(rels, nrels);	} 	/* align things a bit */	me->core_size = ALIGN(me->core_size, 16);	me->arch.got_offset = me->core_size;	me->core_size += gots * sizeof(struct got_entry); 	me->core_size = ALIGN(me->core_size, 16);	me->arch.fdesc_offset = me->core_size;	me->core_size += fdescs * sizeof(Elf_Fdesc); 	me->core_size = ALIGN(me->core_size, 16);	me->arch.stub_offset = me->core_size;	me->core_size += stubs * sizeof(struct stub_entry); 	me->init_size = ALIGN(me->init_size, 16);	me->arch.init_stub_offset = me->init_size;	me->init_size += init_stubs * sizeof(struct stub_entry); 	me->arch.got_max = gots;	me->arch.fdesc_max = fdescs;	me->arch.stub_max = stubs;	me->arch.init_stub_max = init_stubs; 	return 0;} #ifdef __LP64__static Elf64_Word get_got(struct module *me, unsigned long value, long addend){	unsigned int i;	struct got_entry *got; 	value += addend; 	BUG_ON(value == 0); 	got = me->module_core + me->arch.got_offset;	for (i = 0; got[i].addr; i++)		if (got[i].addr == value)			goto out; 	BUG_ON(++me->arch.got_count > me->arch.got_max); 	got[i].addr = value; out:	DEBUGP("GOT ENTRY %d[%x] val %lx\n", i, i*sizeof(struct got_entry),	       value);	return i * sizeof(struct got_entry);}#endif /* __LP64__ */ #ifdef __LP64__static Elf_Addr get_fdesc(struct module *me, unsigned long value){	Elf_Fdesc *fdesc = me->module_core + me->arch.fdesc_offset; 	if (!value) {		printk(KERN_ERR "%s: zero OPD requested!\n", me->name);		return 0;	} 	/* Look for existing fdesc entry. */	while (fdesc->addr) {		if (fdesc->addr == value)			return (Elf_Addr)fdesc;		fdesc++;	} 	BUG_ON(++me->arch.fdesc_count > me->arch.fdesc_max); 	/* Create new one */	fdesc->addr = value;	fdesc->gp = (Elf_Addr)me->module_core + me->arch.got_offset;	return (Elf_Addr)fdesc;}#endif /* __LP64__ */ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,	int millicode, int init_section){	unsigned long i;	struct stub_entry *stub; 	if(init_section) {		i = me->arch.init_stub_count++;		BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max);		stub = me->module_init + me->arch.init_stub_offset + 			i * sizeof(struct stub_entry);	} else {		i = me->arch.stub_count++;		BUG_ON(me->arch.stub_count > me->arch.stub_max);		stub = me->module_core + me->arch.stub_offset + 			i * sizeof(struct stub_entry);	} #ifndef __LP64__/* for 32-bit the stub looks like this: * 	ldil L'XXX,%r1 * 	be,n R'XXX(%sr4,%r1) */	//value = *(unsigned long *)((value + addend) & ~3); /* why? */ 	stub->insns[0] = 0x20200000;	/* ldil L'XXX,%r1	*/	stub->insns[1] = 0xe0202002;	/* be,n R'XXX(%sr4,%r1)	*/ 	stub->insns[0] |= reassemble_21(lrsel(value, addend));	stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4); #else/* for 64-bit we have two kinds of stubs: * for normal function calls: * 	ldd 0(%dp),%dp * 	ldd 10(%dp), %r1 * 	bve (%r1) * 	ldd 18(%dp), %dp * * for millicode: * 	ldil 0, %r1 * 	ldo 0(%r1), %r1 * 	ldd 10(%r1), %r1 * 	bve,n (%r1) */	if (!millicode)	{		stub->insns[0] = 0x537b0000;	/* ldd 0(%dp),%dp	*/		stub->insns[1] = 0x53610020;	/* ldd 10(%dp),%r1	*/		stub->insns[2] = 0xe820d000;	/* bve (%r1)		*/		stub->insns[3] = 0x537b0030;	/* ldd 18(%dp),%dp	*/ 		stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff);	}	else	{		stub->insns[0] = 0x20200000;	/* ldil 0,%r1		*/		stub->insns[1] = 0x34210000;	/* ldo 0(%r1), %r1	*/		stub->insns[2] = 0x50210020;	/* ldd 10(%r1),%r1	*/		stub->insns[3] = 0xe820d002;	/* bve,n (%r1)		*/ 		stub->insns[0] |= reassemble_21(lrsel(value, addend));		stub->insns[1] |= reassemble_14(rrsel(value, addend));	}#endif 	return (Elf_Addr)stub;} int apply_relocate(Elf_Shdr *sechdrs,		   const char *strtab,		   unsigned int symindex,		   unsigned int relsec,		   struct module *me){	/* parisc should not need this ... */	printk(KERN_ERR "module %s: RELOCATION unsupported\n",	       me->name);	return -ENOEXEC;} #ifndef __LP64__int apply_relocate_add(Elf_Shdr *sechdrs,		       const char *strtab,		       unsigned int symindex,		       unsigned int relsec,		       struct module *me){	int i;	Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;	Elf32_Sym *sym;	Elf32_Word *loc;	Elf32_Addr val;	Elf32_Sword addend;	Elf32_Addr dot;	//unsigned long dp = (unsigned long)$global$;	register unsigned long dp asm ("r27"); 	DEBUGP("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 */		loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr		      + rel[i].r_offset;		/* This is the symbol it is referring to */		sym = (Elf32_Sym *)sechdrs[symindex].sh_addr			+ ELF32_R_SYM(rel[i].r_info);		if (!sym->st_value) {			printk(KERN_WARNING "%s: Unknown symbol %s\n",			       me->name, strtab + sym->st_name);			return -ENOENT;		}		//dot = (sechdrs[relsec].sh_addr + rel->r_offset) & ~0x03;		dot =  (Elf32_Addr)loc & ~0x03; 		val = sym->st_value;		addend = rel[i].r_addend; #if 0#define r(t) ELF32_R_TYPE(rel[i].r_info)==t ? #t :		DEBUGP("Symbol %s loc 0x%x val 0x%x addend 0x%x: %s\n",			strtab + sym->st_name,			(uint32_t)loc, val, addend,			r(R_PARISC_PLABEL32)			r(R_PARISC_DIR32)			r(R_PARISC_DIR21L)			r(R_PARISC_DIR14R)			r(R_PARISC_SEGREL32)			r(R_PARISC_DPREL21L)			r(R_PARISC_DPREL14R)			r(R_PARISC_PCREL17F)			r(R_PARISC_PCREL22F)			"UNKNOWN");#undef r

Lines 1–500 of 822.