diff options
-rw-r--r-- | ldso/ldso/sparc/elfinterp.c | 506 |
1 files changed, 302 insertions, 204 deletions
diff --git a/ldso/ldso/sparc/elfinterp.c b/ldso/ldso/sparc/elfinterp.c index 8b169b575..4becf348b 100644 --- a/ldso/ldso/sparc/elfinterp.c +++ b/ldso/ldso/sparc/elfinterp.c @@ -33,236 +33,334 @@ References to symbols in sharable libraries can be resolved by either an ELF sharable library or a linux style of shared library. */ /* Disclaimer: I have never seen any AT&T source code for SVr4, nor have - I ever taken any courses on internals. This program was developed using - information available through the book "UNIX SYSTEM V RELEASE 4, - Programmers guide: Ansi C and Programming Support Tools", which did - a more than adequate job of explaining everything required to get this - working. */ + I ever taken any courses on internals. This program was developed using + information available through the book "UNIX SYSTEM V RELEASE 4, + Programmers guide: Ansi C and Programming Support Tools", which did + a more than adequate job of explaining everything required to get this + working. */ + +/* Some SPARC opcodes we need to use for self-modifying code. */ +#define OPCODE_NOP 0x01000000 /* nop */ +#define OPCODE_CALL 0x40000000 /* call ?; add PC-rel word address */ +#define OPCODE_SETHI_G1 0x03000000 /* sethi ?, %g1; add value>>10 */ +#define OPCODE_JMP_G1 0x81c06000 /* jmp %g1+?; add lo 10 bits of value */ +#define OPCODE_SAVE_SP 0x9de3bfa8 /* save %sp, -(16+6)*4, %sp */ +#define OPCODE_BA 0x30800000 /* b,a ?; add PC-rel word address */ extern int _dl_linux_resolve(void); -unsigned int _dl_linux_resolver(unsigned int reloc_entry, unsigned int * plt) +unsigned long +_dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) { - int reloc_type; - Elf32_Rela * this_reloc; - char * strtab; - Elf32_Sym * symtab; - Elf32_Rela * rel_addr; - struct elf_resolve * tpnt; - int symtab_index; - char * new_addr; - char ** got_addr; - unsigned int instr_addr; - tpnt = (struct elf_resolve *) plt[2]; - - rel_addr = (Elf32_Rela *)tpnt->dynamic_info[DT_JMPREL]; - - /* - * Generate the correct relocation index into the .rela.plt section. - */ - reloc_entry = (reloc_entry >> 10) - 0xc; - - this_reloc = (Elf32_Rela *) ((char *) rel_addr + reloc_entry); - - reloc_type = ELF32_R_TYPE(this_reloc->r_info); - symtab_index = ELF32_R_SYM(this_reloc->r_info); - - symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB]; - strtab = (char *)tpnt->dynamic_info[DT_STRTAB]; - -#ifdef __SUPPORT_LD_DEBUG__ - if (_dl_debug_symbols) { - _dl_dprintf(2, "tpnt = %x\n", tpnt); - _dl_dprintf(2, "reloc = %x\n", this_reloc); - _dl_dprintf(2, "symtab = %x\n", symtab); - _dl_dprintf(2, "strtab = %x\n", strtab); - } + int reloc_type; + ELF_RELOC *this_reloc; + char *strtab; + ElfW(Sym) *symtab; + int symtab_index; + char *rel_addr; + char *new_addr; + char **got_addr; + ElfW(Addr) instr_addr; + char *symname; + + rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL]; + /* + * Generate the correct relocation index into the .rela.plt section. + */ + reloc_entry = (reloc_entry >> 10) - 0xc; + + this_reloc = (ELF_RELOC *)(rel_addr + reloc_entry); + reloc_type = ELF_R_TYPE(this_reloc->r_info); + symtab_index = ELF_R_SYM(this_reloc->r_info); + + symtab = (ElfW(Sym) *)tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *)tpnt->dynamic_info[DT_STRTAB]; + symname = strtab + symtab[symtab_index].st_name; + + if (unlikely(reloc_type != R_SPARC_JMP_SLOT)) { + _dl_dprintf(2, "%s: Incorrect relocation type in jump relocations\n", + _dl_progname); + _dl_exit(1); + } + + /* Address of the jump instruction to fix up. */ + instr_addr = (this_reloc->r_offset + tpnt->loadaddr); + got_addr = (char **)instr_addr; + + /* Get the address of the GOT entry */ + new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT); + if (unlikely(!new_addr)) { + _dl_dprintf(2, "%s: Can't resolve symbol '%s'\n", _dl_progname, symname); + _dl_exit(1); + } + +#if defined (__SUPPORT_LD_DEBUG__) + if ((unsigned long)got_addr < 0x40000000) { + if (_dl_debug_bindings) { + _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname); + if (_dl_debug_detail) + _dl_dprintf(_dl_debug_file, + "\tpatched: %x ==> %x @ %x\n", + *got_addr, new_addr, got_addr); + } + } + if (!_dl_debug_nofixups) #endif + { + got_addr[1] = (char *) (0x03000000 | (((unsigned int) new_addr >> 10) & 0x3fffff)); + got_addr[2] = (char *) (0x81c06000 | ((unsigned int) new_addr & 0x3ff)); + } + return (unsigned long)new_addr; +} + +static int +_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, + unsigned long rel_addr, unsigned long rel_size, + int (*reloc_fnc)(struct elf_resolve *tpnt, struct dyn_elf *scope, + ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)) +{ + unsigned int i; + char *strtab; + ElfW(Sym) *symtab; + ELF_RELOC *rpnt; + int symtab_index; - if (unlikely(reloc_type != R_SPARC_JMP_SLOT)) { - _dl_dprintf(2, "%s: incorrect relocation type in jump relocations (%d)\n", - _dl_progname, reloc_type); - _dl_exit(30); - }; + /* Parse the relocation information. */ + rpnt = (ELF_RELOC *)rel_addr; + rel_size /= sizeof(ELF_RELOC); - /* Address of jump instruction to fix up */ - instr_addr = ((int)this_reloc->r_offset + (int)tpnt->loadaddr); - got_addr = (char **) instr_addr; + symtab = (ElfW(Sym) *)tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *)tpnt->dynamic_info[DT_STRTAB]; -#ifdef __SUPPORT_LD_DEBUG__ - if (_dl_debug_symbols) { - _dl_dprintf(2, "symtab_index %x\n", symtab_index); + for (i = 0; i < rel_size; i++, rpnt++) { + int res; - _dl_dprintf(2, "Resolving symbol %s\n", - strtab + symtab[symtab_index].st_name); - } -#endif + symtab_index = ELF_R_SYM(rpnt->r_info); - /* Get the address of the GOT entry */ - new_addr = _dl_find_hash(strtab + symtab[symtab_index].st_name, - tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT); - if(unlikely(!new_addr)) { - _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", - _dl_progname, strtab + symtab[symtab_index].st_name); - _dl_exit(31); - }; + debug_sym(symtab, strtab, symtab_index); + debug_reloc(symtab, strtab, rpnt); + res = reloc_fnc(tpnt, scope, rpnt, symtab, strtab); + + if (res == 0) + continue; + + _dl_dprintf(2, "\n%s: ", _dl_progname); + + if (symtab_index) + _dl_dprintf(2, "symbol '%s': ", + strtab + symtab[symtab_index].st_name); + + if (unlikely(res < 0)) { + int reloc_type = ELF_R_TYPE(rpnt->r_info); + + _dl_dprintf(2, "can't handle reloc type " #if defined (__SUPPORT_LD_DEBUG__) - if ((unsigned long) got_addr < 0x40000000) - { - if (_dl_debug_bindings) - { - _dl_dprintf(_dl_debug_file, "\nresolve function: %s", - strtab + symtab[symtab_index].st_name); - if(_dl_debug_detail) _dl_dprintf(_dl_debug_file, - "\tpatch %x ==> %x @ %x", *got_addr, new_addr, got_addr); + "%s\n", _dl_reltypes(reloc_type)); +#else + "%x\n", reloc_type); +#endif + _dl_exit(-res); + } else if (unlikely(res > 0)) { + _dl_dprintf(2, "can't resolve symbol\n"); + return res; } } - if (!_dl_debug_nofixups) { - got_addr[1] = (char *) (0x03000000 | (((unsigned int) new_addr >> 10) & 0x3fffff)); - got_addr[2] = (char *) (0x81c06000 | ((unsigned int) new_addr & 0x3ff)); + + return 0; +} + +static int +_dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope, + ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab) +{ + int reloc_type; + int symtab_index; + char *symname; + ElfW(Sym) *sym; + ElfW(Addr) *reloc_addr; + ElfW(Addr) symbol_addr; +#if defined (__SUPPORT_LD_DEBUG__) + ElfW(Addr) old_val; +#endif + + reloc_addr = (ElfW(Addr)*)(tpnt->loadaddr + (unsigned long)rpnt->r_offset); + reloc_type = ELF_R_TYPE(rpnt->r_info); + symtab_index = ELF_R_SYM(rpnt->r_info); + sym = &symtab[symtab_index]; + symbol_addr = 0; + symname = strtab + sym->st_name; + + if (symtab_index) { + symbol_addr = (ElfW(Addr))_dl_find_hash(symname, scope, tpnt, + elf_machine_type_class(reloc_type)); + /* + * We want to allow undefined references to weak symbols - this + * might have been intentional. We should not be linking local + * symbols here, so all bases should be covered. + */ + if (unlikely(!symbol_addr && ELF_ST_BIND(sym->st_info) != STB_WEAK)) { + _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname); + _dl_exit(1); + } } -#else - got_addr[1] = (char *) (0x03000000 | (((unsigned int) new_addr >> 10) & 0x3fffff)); - got_addr[2] = (char *) (0x81c06000 | ((unsigned int) new_addr & 0x3ff)); + +#if defined (__SUPPORT_LD_DEBUG__) + old_val = *reloc_addr; #endif - _dl_dprintf(2, "Address = %x\n",new_addr); - _dl_exit(32); + symbol_addr += rpnt->r_addend; /* Assume copy relocs have zero addend. */ + + switch (reloc_type) { + case R_SPARC_NONE: + break; + +#if 0 /* these dont really seem to be useful */ + case R_SPARC_8: + *(char *) reloc_addr = symbol_addr; + break; + case R_SPARC_16: + *(short *) reloc_addr = symbol_addr; + break; + case R_SPARC_DISP8: + *(char *) reloc_addr = (symbol_addr) - (Elf32_Addr) reloc_addr; + break; + case R_SPARC_DISP16: + *(short *) reloc_addr = (symbol_addr) - (Elf32_Addr) reloc_addr; + break; +#endif - return (unsigned int) new_addr; -} + case R_SPARC_DISP32: + *reloc_addr = symbol_addr - (unsigned int) reloc_addr; + break; + + case R_SPARC_LO10: + if (!symbol_addr) + symbol_addr = tpnt->loadaddr + rpnt->r_addend; + else + symbol_addr += rpnt->r_addend; + *reloc_addr = (*reloc_addr & ~0x3ff)|(symbol_addr & 0x3ff); + break; + + case R_SPARC_GLOB_DAT: + case R_SPARC_32: + *reloc_addr = symbol_addr; + break; + + case R_SPARC_JMP_SLOT: +/* +value = symbol_addr; +value += reloc->r_addend; +disp = value - reloc_addr; +reloc_addr[1] = OPCODE_JMP_G1 | (value & 0x3ff); +reloc_addr[0] = OPCODE_SETHI_G1 | (value >> 10); + reloc_addr[1] = OPCODE_JMP_G1 | ((symbol_addr-(Elf32_Addr)reloc_addr) & 0x3ff); + reloc_addr[0] = OPCODE_SETHI_G1 | ((symbol_addr-(Elf32_Addr)reloc_addr) >> 10); +*/ + reloc_addr[1] = 0x03000000 | ((symbol_addr >> 10) & 0x3fffff); + reloc_addr[2] = 0x81c06000 | (symbol_addr & 0x3ff); + break; + + case R_SPARC_RELATIVE: + *reloc_addr += tpnt->loadaddr + rpnt->r_addend; + break; + + case R_SPARC_WDISP30: + *reloc_addr = (*reloc_addr & 0xc0000000)| + ((symbol_addr - (unsigned int) reloc_addr) >> 2); + break; + + case R_SPARC_HI22: + if (!symbol_addr) + symbol_addr = tpnt->loadaddr + rpnt->r_addend; + else + symbol_addr += rpnt->r_addend; + *reloc_addr = (*reloc_addr & 0xffc00000) | (symbol_addr >> 10); + break; + + case R_SPARC_COPY: + if (symbol_addr) { +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_move) + _dl_dprintf(_dl_debug_file, + "\t%s move %d bytes from %x to %x\n", + symname, sym->st_size, + symbol_addr, reloc_addr); +#endif + + _dl_memcpy((char *)reloc_addr, + (char *)symbol_addr, + sym->st_size); + } else + _dl_dprintf(_dl_debug_file, "no symbol_addr to copy !?\n"); + break; + default: + return -1; /* Calls _dl_exit(1). */ + } -void _dl_parse_lazy_relocation_information(struct dyn_elf *arg_rpnt, - unsigned long rel_addr, unsigned long rel_size) -{ - int i; - char * strtab; - int reloc_type; - int symtab_index; - Elf32_Sym * symtab; - Elf32_Rela * rpnt; - unsigned int * reloc_addr; - struct elf_resolve * tpnt = arg_rpnt->dyn; - - /* Now parse the relocation information */ - rpnt = (Elf32_Rela *)rel_addr; - - symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB]; - strtab = ( char *)tpnt->dynamic_info[DT_STRTAB]; - - for(i=0; i< rel_size; i += sizeof(Elf32_Rela), rpnt++){ - reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); - reloc_type = ELF32_R_TYPE(rpnt->r_info); - symtab_index = ELF32_R_SYM(rpnt->r_info); - - switch(reloc_type){ - case R_SPARC_NONE: - break; - case R_SPARC_JMP_SLOT: - break; - default: - _dl_dprintf(2, "%s: (LAZY) can't handle reloc type ", _dl_progname); #if defined (__SUPPORT_LD_DEBUG__) - _dl_dprintf(2, "%s ", _dl_reltypes_tab[reloc_type]); + if (_dl_debug_reloc && _dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", + old_val, *reloc_addr, reloc_addr); #endif - if(symtab_index) _dl_dprintf(2, "'%s'\n", - strtab + symtab[symtab_index].st_name); - _dl_exit(33); - }; - }; + + return 0; } -int _dl_parse_relocation_information(struct dyn_elf *arg_rpnt, - unsigned long rel_addr, unsigned long rel_size) +static int +_dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope, + ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab) { - int i; - char * strtab; - int reloc_type; - int goof = 0; - Elf32_Sym * symtab; - Elf32_Rela * rpnt; - unsigned int * reloc_addr; - unsigned int symbol_addr; - int symtab_index; - struct elf_resolve * tpnt = arg_rpnt->dyn; - /* Now parse the relocation information */ - - rpnt = (Elf32_Rela *)rel_addr; - - symtab = (Elf32_Sym *)tpnt->dynamic_info[DT_SYMTAB]; - strtab = ( char *)tpnt->dynamic_info[DT_STRTAB]; - - for(i=0; i< rel_size; i+= sizeof(Elf32_Rela), rpnt++){ - reloc_addr = (int *) (tpnt->loadaddr + (int)rpnt->r_offset); - reloc_type = ELF32_R_TYPE(rpnt->r_info); - symtab_index = ELF32_R_SYM(rpnt->r_info); - symbol_addr = 0; - - if(symtab_index) { - - symbol_addr = (unsigned int) - _dl_find_hash(strtab + symtab[symtab_index].st_name, - tpnt->symbol_scope, tpnt, elf_machine_type_class(reloc_type)); - - if(!symbol_addr && - ELF32_ST_BIND(symtab [symtab_index].st_info) != STB_WEAK) { - _dl_dprintf (2, "%s: can't resolve symbol '%s'\n", - _dl_progname, strtab + symtab[symtab_index].st_name); - _dl_exit (1); - }; - }; - switch(reloc_type){ - case R_SPARC_NONE: - break; - case R_SPARC_32: - *reloc_addr = symbol_addr + rpnt->r_addend; - break; - case R_SPARC_DISP32: - *reloc_addr = symbol_addr + rpnt->r_addend - (unsigned int) reloc_addr; - break; - case R_SPARC_GLOB_DAT: - *reloc_addr = symbol_addr + rpnt->r_addend; - break; - case R_SPARC_JMP_SLOT: - reloc_addr[1] = 0x03000000 | ((symbol_addr >> 10) & 0x3fffff); - reloc_addr[2] = 0x81c06000 | (symbol_addr & 0x3ff); - break; - case R_SPARC_RELATIVE: - *reloc_addr += (unsigned int) tpnt->loadaddr + rpnt->r_addend; - break; - case R_SPARC_HI22: - if (!symbol_addr) - symbol_addr = tpnt->loadaddr + rpnt->r_addend; - else - symbol_addr += rpnt->r_addend; - *reloc_addr = (*reloc_addr & 0xffc00000)|(symbol_addr >> 10); - break; - case R_SPARC_LO10: - if (!symbol_addr) - symbol_addr = tpnt->loadaddr + rpnt->r_addend; - else - symbol_addr += rpnt->r_addend; - *reloc_addr = (*reloc_addr & ~0x3ff)|(symbol_addr & 0x3ff); - break; - case R_SPARC_WDISP30: - *reloc_addr = (*reloc_addr & 0xc0000000)| - ((symbol_addr - (unsigned int) reloc_addr) >> 2); - break; - case R_SPARC_COPY: - _dl_memcpy((void *) reloc_addr, (void *) symbol_addr, symtab[symtab_index].st_size); - break; - default: - _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname); + int reloc_type; + int symtab_index; + ElfW(Addr) *reloc_addr; +#if defined (__SUPPORT_LD_DEBUG__) + ElfW(Addr) old_val; +#endif + + (void)scope; + symtab_index = ELF_R_SYM(rpnt->r_info); + (void)strtab; + + reloc_addr = (ElfW(Addr)*)(tpnt->loadaddr + rpnt->r_offset); + reloc_type = ELF_R_TYPE(rpnt->r_info); + #if defined (__SUPPORT_LD_DEBUG__) - _dl_dprintf(2, "%s ", _dl_reltypes_tab[reloc_type]); + old_val = *reloc_addr; #endif - if (symtab_index) - _dl_dprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name); - _dl_exit(34); - }; - }; - return goof; + switch (reloc_type) { + case R_SPARC_NONE: + break; + case R_SPARC_JMP_SLOT: + break; + default: + _dl_exit(1); + } + +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_reloc && _dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\tpatched_lazy: %x ==> %x @ %x\n", + old_val, *reloc_addr, reloc_addr); +#endif + + return 0; +} + +void +_dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, + unsigned long rel_addr, + unsigned long rel_size) +{ +// (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc); + _dl_parse_relocation_information(rpnt, rel_addr, rel_size); +} + +int +_dl_parse_relocation_information(struct dyn_elf *rpnt, + unsigned long rel_addr, + unsigned long rel_size) +{ + return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc); } |