diff options
Diffstat (limited to 'ldso')
-rw-r--r-- | ldso/ldso/metag/dl-debug.h | 27 | ||||
-rw-r--r-- | ldso/ldso/metag/dl-startup.h | 63 | ||||
-rw-r--r-- | ldso/ldso/metag/dl-syscalls.h | 6 | ||||
-rw-r--r-- | ldso/ldso/metag/dl-sysdep.h | 98 | ||||
-rw-r--r-- | ldso/ldso/metag/elfinterp.c | 299 | ||||
-rw-r--r-- | ldso/ldso/metag/resolve.S | 51 |
6 files changed, 544 insertions, 0 deletions
diff --git a/ldso/ldso/metag/dl-debug.h b/ldso/ldso/metag/dl-debug.h new file mode 100644 index 000000000..5981d7c73 --- /dev/null +++ b/ldso/ldso/metag/dl-debug.h @@ -0,0 +1,27 @@ +/* + * Meta ELF shared library loader support. + * + * Program to load an elf binary on a linux system, and run it. + * References to symbols in sharable libraries can be resolved + * by either an ELF sharable library or a linux style of shared + * library. + * + * Copyright (C) 2013, Imagination Technologies Ltd. + * + * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ + +static const char *_dl_reltypes_tab[] = { + [0] "R_METAG_HIADDR16", "R_METAG_LOADDR16", "R_METAG_ADDR32", + [3] "R_METAG_NONE", "R_METAG_RELBRANCH", "R_METAG_GETSETOFF", + [6] "R_METAG_REG32OP1", "R_METAG_REG32OP2", "R_METAG_REG32OP3", + [9] "R_METAG_REG16OP1", "R_METAG_REG16OP2", "R_METAG_REG16OP3", + [12] "R_METAG_REG32OP4", "R_METAG_HIOG", "R_METAG_LOOG", + [30] "R_METAG_VTINHERIT", "R_METAG_VTENTRY", + [32] "R_METAG_HI16_GOTOFF", "R_METAG_LO16_GOTOFF", + [34] "R_METAG_GETSET_GOTOFF", "R_METAG_GETSET_GOT", + [36] "R_METAG_HI16_GOTPC", "R_METAG_LO16_GOTPC", + [38] "R_METAG_HI16_PLT", "R_METAG_LO16_PLT", + [40] "R_METAG_RELBRANCH_PLT", "R_METAG_GOTOFF", + [42] "R_METAG_PLT", "R_METAG_COPY", "R_METAG_JMP_SLOT", +}; diff --git a/ldso/ldso/metag/dl-startup.h b/ldso/ldso/metag/dl-startup.h new file mode 100644 index 000000000..8dbf747e1 --- /dev/null +++ b/ldso/ldso/metag/dl-startup.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 Imagination Technologies Ltd. + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ + +/* + * This code fixes the stack pointer so that the dynamic linker + * can find argc, argv and auxvt (Auxillary Vector Table). + */ + +__asm__ ( +" .text\n" +" .global __start\n" +" .type __start,@function\n" +" .hidden __start\n" +"_start:\n" +"__start:\n" +" MSETL [A0StP++],D0Ar4,D0Ar2\n" +" MOV D1Ar1,D0Ar2\n" +" CALLR D1RtP,__dl_start\n" +" GETL D0Ar2,D1Ar1,[A0StP+#-(1*8)]\n" +" GETL D0Ar4,D1Ar3,[A0StP+#-(2*8)]\n" +" SUB A0StP,A0StP,#(2*8)\n" +" MOV PC,D0Re0\n" +" .size __start,.-__start\n" +" .previous\n" +); + + +/* + * Get a pointer to the argv array. On many platforms this can be just + * the address if the first argument, on other platforms we need to + * do something a little more subtle here. + */ + +#define GET_ARGV(ARGVP, ARGS) ARGVP = (((unsigned long *) ARGS)) + + +/* Handle relocation of the symbols in the dynamic loader. */ +static inline +void PERFORM_BOOTSTRAP_RELOC(ELF_RELOC *rpnt, unsigned long *reloc_addr, + unsigned long symbol_addr, unsigned long load_addr, Elf32_Sym *symtab) +{ + switch (ELF32_R_TYPE(rpnt->r_info)) { + case R_METAG_GLOB_DAT: + case R_METAG_JMP_SLOT: + case R_METAG_ADDR32: + *reloc_addr = symbol_addr; + break; + case R_METAG_RELATIVE: + *reloc_addr = load_addr + rpnt->r_addend; + break; + case R_METAG_RELBRANCH: + *reloc_addr = symbol_addr + rpnt->r_addend - *reloc_addr - 4; + break; + case R_METAG_NONE: + break; + default: + _dl_exit(1); + break; + } +} diff --git a/ldso/ldso/metag/dl-syscalls.h b/ldso/ldso/metag/dl-syscalls.h new file mode 100644 index 000000000..70ceab10e --- /dev/null +++ b/ldso/ldso/metag/dl-syscalls.h @@ -0,0 +1,6 @@ +/* stub for arch-specific syscall issues + * + * Copyright (C) 2013, Imagination Technologies Ltd. + * + * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ diff --git a/ldso/ldso/metag/dl-sysdep.h b/ldso/ldso/metag/dl-sysdep.h new file mode 100644 index 000000000..55e8d1f66 --- /dev/null +++ b/ldso/ldso/metag/dl-sysdep.h @@ -0,0 +1,98 @@ +/* + * Meta can never use Elf32_Rel relocations. + * + * Copyright (C) 2013, Imagination Technologies Ltd. + * + * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ + +#define ELF_USES_RELOCA + +#include <elf.h> + +/* Initialization sequence for the GOT. */ +#define INIT_GOT(GOT_BASE,MODULE) \ +{ \ + GOT_BASE[1] = (unsigned long) MODULE; \ + GOT_BASE[2] = (unsigned long) _dl_linux_resolve; \ +} + +/* Maximum unsigned GOT [GS]ETD offset size, ie. 2^(11+2). */ +#define GOT_REG_OFFSET 0x2000 + +/* Defined some magic numbers that this ld.so should accept. */ +#define MAGIC1 EM_METAG +#undef MAGIC2 +#define ELF_TARGET "META" + +/* Need bootstrap relocations */ +#define ARCH_NEEDS_BOOTSTRAP_RELOCS + +struct elf_resolve; +extern unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry); + +/* Variable page size. */ +#define ADDR_ALIGN (_dl_pagesize - 1) +#define PAGE_ALIGN (~ADDR_ALIGN) +#define OFFS_ALIGN (PAGE_ALIGN & ~(1ul << (sizeof(_dl_pagesize) * 8 - 1))) + +/* The union of reloc-type-classes where the reloc TYPE is a member. + + TYPE is in the class ELF_RTYPE_CLASS_NOCOPY if it should not be allowed + to resolve to one of the main executable's symbols, as for a COPY + reloc. */ +#define elf_machine_type_class(type) \ + (((((type) == R_METAG_JMP_SLOT)) * ELF_RTYPE_CLASS_PLT) \ + | (((type) == R_METAG_COPY) * ELF_RTYPE_CLASS_COPY)) + +static inline Elf32_Addr +elf_machine_dynamic(Elf32_Ehdr *header) +{ + Elf32_Addr *got; + + __asm__ ("MOV %0,A1LbP" : "=r" (got)); + + if (header->e_ident[EI_ABIVERSION] >= 1) { + /* GOT register offset was introduced with ABI v1 */ + got = (Elf32_Addr*)((void*)got - GOT_REG_OFFSET); + } + return *got; +} + +#define DL_BOOT_COMPUTE_GOT(GOT) \ + ((GOT) = elf_machine_dynamic(header)) + +static inline Elf32_Addr +elf_machine_load_address(void) +{ + Elf32_Addr addr; + __asm__ ("MOV D1Ar1,A1LbP\n" + "ADDT D1Ar1,D1Ar1,#HI(__dl_start@GOTOFF)\n" + "ADD D1Ar1,D1Ar1,#LO(__dl_start@GOTOFF)\n" + "ADDT D0Ar2,D0Ar2,#HI(__dl_start_addr@GOTOFF)\n" + "ADD D0Ar2,D0Ar2,#LO(__dl_start_addr@GOTOFF)\n" + "GETD D0Ar2,[D0Ar2]\n" + "SUB %0,D1Ar1,D0Ar2\n" + ".section .data\n" + "__dl_start_addr: .long __dl_start\n" + ".previous\n" + : "=d" (addr) : : "D1Ar1", "D0Ar2"); + return addr; +} + +static inline void +elf_machine_relative(Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + Elf32_Rela *rpnt = (void *)rel_addr; + + --rpnt; + do { + Elf32_Addr *const reloc_addr = + (void *)(load_off + (++rpnt)->r_offset); + + *reloc_addr = load_off + rpnt->r_addend; + } while (--relative_count); +} + +#define DL_MALLOC_ALIGN 8 diff --git a/ldso/ldso/metag/elfinterp.c b/ldso/ldso/metag/elfinterp.c new file mode 100644 index 000000000..78434167b --- /dev/null +++ b/ldso/ldso/metag/elfinterp.c @@ -0,0 +1,299 @@ +/* + * Meta ELF shared library loader support. + * + * Program to load an elf binary on a linux system, and run it. + * References to symbols in sharable libraries can be resolved + * by either an ELF sharable library or a linux style of shared + * library. + * + * Copyright (C) 2013, Imagination Technologies Ltd. + * + * Licensed under LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + */ + +#include "ldso.h" + +/* Defined in resolve.S. */ +extern int _dl_linux_resolve(void); + +static inline unsigned long __get_unaligned_reloc(unsigned long *addr) +{ + char *rel_addr = (char *)addr; + unsigned long val; + + val = *rel_addr++ & 0xff; + val |= (*rel_addr++ << 8) & 0x0000ff00; + val |= (*rel_addr++ << 16) & 0x00ff0000; + val |= (*rel_addr++ << 24) & 0xff000000; + + return val; +} + +static inline void __put_unaligned_reloc(unsigned long *addr, + unsigned long val) +{ + char *rel_addr = (char *)addr; + + *rel_addr++ = (val & 0x000000ff); + *rel_addr++ = ((val & 0x0000ff00) >> 8); + *rel_addr++ = ((val & 0x00ff0000) >> 16); + *rel_addr++ = ((val & 0xff000000) >> 24); +} + +unsigned long +_dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry) +{ + int reloc_type; + int symtab_index; + char *strtab; + char *symname; + char *new_addr; + char *rel_addr; + char **got_addr; + Elf32_Sym *symtab; + ELF_RELOC *this_reloc; + unsigned long instr_addr; + + rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL]; + + this_reloc = (ELF_RELOC *)(intptr_t)(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 *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *)tpnt->dynamic_info[DT_STRTAB]; + symname = strtab + symtab[symtab_index].st_name; + + if (unlikely(reloc_type != R_METAG_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 = ((unsigned long)this_reloc->r_offset + + (unsigned long)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, NULL); + 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 (_dl_debug_bindings) { + _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname); + if (_dl_debug_detail) + _dl_dprintf(_dl_debug_file, + "\n\tpatched: %x ==> %x @ %x\n", + *got_addr, new_addr, got_addr); + } + if (!_dl_debug_nofixups) { + *got_addr = new_addr; + } +#else + *got_addr = new_addr; +#endif + + 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, Elf32_Sym *symtab, char *strtab)) +{ + int symtab_index; + unsigned int i; + char *strtab; + Elf32_Sym *symtab; + ELF_RELOC *rpnt; + + /* Parse the relocation information. */ + rpnt = (ELF_RELOC *)(intptr_t)rel_addr; + rel_size /= sizeof(ELF_RELOC); + + symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB]; + strtab = (char *)tpnt->dynamic_info[DT_STRTAB]; + + for (i = 0; i < rel_size; i++, rpnt++) { + int res; + + symtab_index = ELF32_R_SYM(rpnt->r_info); + + debug_sym(symtab, strtab, symtab_index); + debug_reloc(symtab, strtab, rpnt); + + /* Pass over to actual relocation function. */ + 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 = ELF32_R_TYPE(rpnt->r_info); + +#if defined (__SUPPORT_LD_DEBUG__) + _dl_dprintf(2, "can't handle reloc type %s\n", + _dl_reltypes(reloc_type)); +#else + _dl_dprintf(2, "can't handle reloc type %x\n", + reloc_type); +#endif + _dl_exit(-res); + } else if (unlikely(res > 0)) { + _dl_dprintf(2, "can't resolve symbol\n"); + return res; + } + } + + return 0; +} + +static int +_dl_do_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope, + ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab) +{ + int reloc_type; + int symtab_index; + char *symname = NULL; + unsigned long *reloc_addr; + unsigned long symbol_addr; +#if defined (__SUPPORT_LD_DEBUG__) + unsigned long old_val; +#endif + + reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + symtab_index = ELF32_R_SYM(rpnt->r_info); + symbol_addr = 0; + symname = strtab + symtab[symtab_index].st_name; + + if (symtab_index) { + if (symtab[symtab_index].st_shndx != SHN_UNDEF && + ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL) { + symbol_addr = (unsigned long)tpnt->loadaddr; + } else { + symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt, + elf_machine_type_class(reloc_type), NULL); + } + + if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK)) { + _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname); + _dl_exit(1); + }; + + symbol_addr += rpnt->r_addend; + } + +#if defined (__SUPPORT_LD_DEBUG__) + if (reloc_type != R_METAG_NONE) + old_val = __get_unaligned_reloc(reloc_addr); +#endif + + switch (reloc_type) { + case R_METAG_NONE: + break; + case R_METAG_GLOB_DAT: + case R_METAG_JMP_SLOT: + case R_METAG_ADDR32: + __put_unaligned_reloc(reloc_addr, symbol_addr); + break; + case R_METAG_COPY: +#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, symtab[symtab_index].st_size, + symbol_addr, reloc_addr); +#endif + + _dl_memcpy((char *)reloc_addr, + (char *)symbol_addr, + symtab[symtab_index].st_size); + break; + case R_METAG_RELATIVE: + __put_unaligned_reloc(reloc_addr, + (unsigned long)tpnt->loadaddr + + rpnt->r_addend); + break; + default: + return -1; /* Calls _dl_exit(1). */ + } + +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_reloc && _dl_debug_detail && + (reloc_type != R_METAG_NONE)) { + unsigned long new_val = __get_unaligned_reloc(reloc_addr); + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", + old_val, new_val, reloc_addr); + } +#endif + + return 0; +} + +static int +_dl_do_lazy_reloc(struct elf_resolve *tpnt, struct dyn_elf *scope, + ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab) +{ + int reloc_type; + unsigned long *reloc_addr; +#if defined (__SUPPORT_LD_DEBUG__) + unsigned long old_val; +#endif + + reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset); + reloc_type = ELF32_R_TYPE(rpnt->r_info); + +#if defined (__SUPPORT_LD_DEBUG__) + old_val = *reloc_addr; +#endif + + switch (reloc_type) { + case R_METAG_NONE: + break; + case R_METAG_JMP_SLOT: + *reloc_addr += (unsigned long)tpnt->loadaddr; + break; + default: + return -1; /* Calls _dl_exit(1). */ + } + +#if defined (__SUPPORT_LD_DEBUG__) + if (_dl_debug_reloc && _dl_debug_detail) + _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", + old_val, *reloc_addr, reloc_addr); +#endif + + return 0; +} + +/* External interface to the generic part of the dynamic linker. */ + +void +_dl_parse_lazy_relocation_information(struct dyn_elf *rpnt, + unsigned long rel_addr, + unsigned long rel_size) +{ + _dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc); +} + +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); +} diff --git a/ldso/ldso/metag/resolve.S b/ldso/ldso/metag/resolve.S new file mode 100644 index 000000000..8f23a340a --- /dev/null +++ b/ldso/ldso/metag/resolve.S @@ -0,0 +1,51 @@ +/* + * Meta dynamic resolver + * + * Copyright (C) 2013 Imagination Technologies Ltd. + * + * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball. + * + * This function is _not_ called directly. It is jumped to (so no return + * address is on the stack) when attempting to use a symbol that has not yet + * been resolved. The first time a jump symbol (such as a function call inside + * a shared library) is used (before it gets resolved) it will jump here to + * _dl_linux_resolve. When we get called the stack looks like this: + * reloc_entry + * tpnt + * + * This function saves all the registers then makes the function call + * _dl_linux_resolver(tpnt, reloc_entry). _dl_linux_resolver() figures out + * where the jump symbol is _really_ supposed to have jumped to and returns + * that to us. Once we have that, we overwrite tpnt with this fixed up + * address. We then clean up after ourselves, put all the registers back how we + * found them, then we jump to the fixed up address, which is where the jump + * symbol that got us here really wanted to jump to in the first place. + * -Erik Andersen + */ + + .text + .global __dl_linux_resolve + .type __dl_linux_resolve,@function + +__dl_linux_resolve: + !! Save registers on the stack. Do we need to save any more here? + MSETL [A0StP++],D0Ar6,D0Ar4,D0Ar2,D0FrT + SETL [A0StP++],A0FrP,A1LbP + !! Get the args for _dl_linux_resolver off the stack + GETL D0Re0,D1Re0,[A0StP+#-(6*8)] + GETD D1Ar1,[D0Re0] + MOV D0Ar2,D1Re0 + !! Multiply plt_index by sizeof(Elf32_Rela) + MULW D0Ar2,D0Ar2,#12 + !! Call the resolver + CALLR D1RtP,__dl_linux_resolver + !! Restore the registers from the stack + SUB A0.2,A0StP,#(1*8) + GETL A0FrP,A1LbP,[A0.2] + SUB A0.2,A0.2,#(4*8) + MGETL D0Ar6,D0Ar4,D0Ar2,D0FrT,[A0.2] + !! Also take into account args pushed by PLT + SUB A0StP,A0StP,#(6*8) + !! Jump to the resolved address + MOV PC,D0Re0 + .size __dl_linux_resolve, .-__dl_linux_resolve |