diff options
author | Joakim Tjernlund <joakim.tjernlund@transmode.se> | 2005-03-14 13:25:07 +0000 |
---|---|---|
committer | Joakim Tjernlund <joakim.tjernlund@transmode.se> | 2005-03-14 13:25:07 +0000 |
commit | 7d137fcf818e9a157a9f7ed9df61896f6cf97490 (patch) | |
tree | 4cd0201de5e99f8f7fc3e40ac97fe9e44411d29f /ldso | |
parent | 3b67c539e3f14a7acf59608d6f7a37b710033a5c (diff) |
Generalize optimized relative reloc procesing.
Add elf_machine_dynamic() and elf_machine_load_address() for
all archs. elf_machine_dynamic() replaces the #ifdef mess to
get at the GOT. elf_machine_load_address() is needed to execute
ldso directly, this is not complete yet.
I probably broke one or two archs(only tested PPC) so please
try and report problems. For a report to be useful you need
to enable __SUPPORT_LD_DEBUG_EARLY__ and __SUPPORT_LD_DEBUG__
Diffstat (limited to 'ldso')
-rw-r--r-- | ldso/ldso/arm/dl-sysdep.h | 37 | ||||
-rw-r--r-- | ldso/ldso/cris/dl-sysdep.h | 46 | ||||
-rw-r--r-- | ldso/ldso/dl-elf.c | 11 | ||||
-rw-r--r-- | ldso/ldso/dl-startup.c | 24 | ||||
-rw-r--r-- | ldso/ldso/i386/dl-sysdep.h | 41 | ||||
-rw-r--r-- | ldso/ldso/m68k/dl-sysdep.h | 37 | ||||
-rw-r--r-- | ldso/ldso/mips/dl-sysdep.h | 51 | ||||
-rw-r--r-- | ldso/ldso/powerpc/dl-sysdep.h | 77 | ||||
-rw-r--r-- | ldso/ldso/powerpc/elfinterp.c | 14 | ||||
-rw-r--r-- | ldso/ldso/sh/dl-sysdep.h | 78 | ||||
-rw-r--r-- | ldso/ldso/sh64/dl-sysdep.h | 78 | ||||
-rw-r--r-- | ldso/ldso/sparc/dl-sysdep.h | 60 |
12 files changed, 526 insertions, 28 deletions
diff --git a/ldso/ldso/arm/dl-sysdep.h b/ldso/ldso/arm/dl-sysdep.h index 1c5c91ab0..773000f58 100644 --- a/ldso/ldso/arm/dl-sysdep.h +++ b/ldso/ldso/arm/dl-sysdep.h @@ -7,7 +7,7 @@ /* Define this if the system uses RELOCA. */ #undef ELF_USES_RELOCA - +#include <elf.h> /* Initialization sequence for the GOT. */ #define INIT_GOT(GOT_BASE,MODULE) \ { \ @@ -66,3 +66,38 @@ unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_entry); #define elf_machine_type_class(type) \ ((((type) == R_ARM_JUMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_ARM_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_dynamic (void) +{ + register Elf32_Addr *got asm ("r10"); + return *got; +} + + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_load_address (void) +{ + extern void __dl_boot asm ("_dl_boot"); + Elf32_Addr got_addr = (Elf32_Addr) &__dl_boot; + Elf32_Addr pcrel_addr; + asm ("adr %0, _dl_boot" : "=r" (pcrel_addr)); + return pcrel_addr - got_addr; +} + +static inline void +elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + Elf32_Rel * rpnt = (void *) (rel_addr + load_off); + --rpnt; + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + + *reloc_addr = load_off; + } while (--relative_count); +} diff --git a/ldso/ldso/cris/dl-sysdep.h b/ldso/ldso/cris/dl-sysdep.h index ab089072f..d12644a2d 100644 --- a/ldso/ldso/cris/dl-sysdep.h +++ b/ldso/ldso/cris/dl-sysdep.h @@ -1,6 +1,6 @@ /* CRIS can never use Elf32_Rel relocations. */ #define ELF_USES_RELOCA - +#include <elf.h> /* Initialization sequence for the GOT. */ #define INIT_GOT(GOT_BASE,MODULE) \ { \ @@ -75,3 +75,47 @@ cris_mod(unsigned long m, unsigned long p) ((((((type) == R_CRIS_JUMP_SLOT)) \ || ((type) == R_CRIS_GLOB_DAT)) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_CRIS_COPY) * ELF_RTYPE_CLASS_COPY)) + +static inline Elf32_Addr +elf_machine_dynamic (void) +{ + /* Don't just set this to an asm variable "r0" since that's not logical + (like, the variable is uninitialized and the register is fixed) and + may make GCC trip over itself doing register allocation. Yes, I'm + paranoid. Why do you ask? */ + Elf32_Addr *got; + + __asm__ ("move.d $r0,%0" : "=rm" (got)); + return *got; +} + +/* Return the run-time load address of the shared object. We do it like + m68k and i386, by taking an arbitrary local symbol, forcing a GOT entry + for it, and peeking into the GOT table, which is set to the link-time + file-relative symbol value (regardless of whether the target is REL or + RELA). We subtract this link-time file-relative value from the "local" + value we calculate from GOT position and GOT offset. FIXME: Perhaps + there's some other symbol we could use, that we don't *have* to force a + GOT entry for. */ + +static inline Elf32_Addr +elf_machine_load_address (void) +{ + Elf32_Addr gotaddr_diff; + __asm__ ("sub.d [$r0+_dl_boot:GOT16],$r0,%0\n\t" + "add.d _dl_boot:GOTOFF,%0" : "=r" (gotaddr_diff)); + return gotaddr_diff; +} + +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 + load_off); + --rpnt; + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + + *reloc_addr = load_off + rpnt->r_addend; + } while (--relative_count); +} diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index e1cc005a0..bcf83346e 100644 --- a/ldso/ldso/dl-elf.c +++ b/ldso/ldso/dl-elf.c @@ -726,7 +726,7 @@ int _dl_fixup(struct dyn_elf *rpnt, int now_flag) { int goof = 0; struct elf_resolve *tpnt; - unsigned long reloc_size; + Elf32_Word reloc_size, reloc_addr, relative_count; if (rpnt->next) goof += _dl_fixup(rpnt->next, now_flag); @@ -757,8 +757,15 @@ int _dl_fixup(struct dyn_elf *rpnt, int now_flag) if (tpnt->dynamic_info[DT_RELOC_TABLE_ADDR] && !(tpnt->init_flag & RELOCS_DONE)) { tpnt->init_flag |= RELOCS_DONE; + reloc_addr = tpnt->dynamic_info[DT_RELOC_TABLE_ADDR]; + relative_count = tpnt->dynamic_info[DT_RELCONT_IDX]; + if (relative_count) { /* Optimize the XX_RELATIVE relocations if possible */ + reloc_size -= relative_count * sizeof(ELF_RELOC); + elf_machine_relative (tpnt->loadaddr, reloc_addr, relative_count); + reloc_addr += relative_count * sizeof(ELF_RELOC); + } goof += _dl_parse_relocation_information(rpnt, - tpnt->dynamic_info[DT_RELOC_TABLE_ADDR], + reloc_addr, reloc_size); } if (tpnt->dynamic_info[DT_BIND_NOW]) diff --git a/ldso/ldso/dl-startup.c b/ldso/ldso/dl-startup.c index caff8d1a7..cdf9641b0 100644 --- a/ldso/ldso/dl-startup.c +++ b/ldso/ldso/dl-startup.c @@ -114,7 +114,7 @@ DL_BOOT(unsigned long args) unsigned int argc; char **argv, **envp; unsigned long load_addr; - unsigned long *got; + Elf32_Addr got; unsigned long *aux_dat; int goof = 0; ElfW(Ehdr) *header; @@ -167,6 +167,8 @@ DL_BOOT(unsigned long args) /* locate the ELF header. We need this done as soon as possible * (esp since SEND_STDERR() needs this on some platforms... */ + if (!auxvt[AT_BASE].a_un.a_val) + auxvt[AT_BASE].a_un.a_val = elf_machine_load_address(); load_addr = auxvt[AT_BASE].a_un.a_val; header = (ElfW(Ehdr) *) auxvt[AT_BASE].a_un.a_ptr; @@ -194,6 +196,7 @@ DL_BOOT(unsigned long args) * we can take advantage of the magic offset register, if we * happen to know what that is for this architecture. If not, * we can always read stuff out of the ELF file to find it... */ +#if 0 /* to be deleted */ #if defined(__i386__) __asm__("\tmovl %%ebx,%0\n\t":"=a"(got)); #elif defined(__m68k__) @@ -270,12 +273,15 @@ found_got: /* Now, finally, fix up the location of the dynamic stuff */ dpnt = (Elf32_Dyn *) (*got + load_addr); +#endif + got = elf_machine_dynamic(); + dpnt = (Elf32_Dyn *) (got + load_addr); #ifdef __SUPPORT_LD_DEBUG_EARLY__ SEND_STDERR("First Dynamic section entry="); SEND_ADDRESS_STDERR(dpnt, 1); #endif _dl_memset(tpnt, 0, sizeof(struct elf_resolve)); - + tpnt->loadaddr = load_addr; /* OK, that was easy. Next scan the DYNAMIC section of the image. We are only doing ourself right now - we will have to do the rest later */ #ifdef __SUPPORT_LD_DEBUG_EARLY__ @@ -342,12 +348,13 @@ found_got: goof = 0; for (indx = 0; indx < INDX_MAX; indx++) { unsigned int i; - ELF_RELOC *rpnt; unsigned long *reloc_addr; unsigned long symbol_addr; int symtab_index; - unsigned long rel_addr, rel_size; Elf32_Sym *sym; + ELF_RELOC *rpnt; + unsigned long rel_addr, rel_size; + Elf32_Word relative_count = tpnt->dynamic_info[DT_RELCONT_IDX]; rel_addr = (indx ? tpnt->dynamic_info[DT_JMPREL] : tpnt-> dynamic_info[DT_RELOC_TABLE_ADDR]); @@ -358,6 +365,15 @@ found_got: continue; /* Now parse the relocation information */ + /* Since ldso is linked with -Bsymbolic, all relocs will be RELATIVE(for those archs that have + RELATIVE relocs) which means that the for(..) loop below has noting to do and can be deleted. + Possibly one should add a HAVE_RELATIVE_RELOCS directive and #ifdef away some code. */ + if (!indx && relative_count) { + rel_size -= relative_count * sizeof(ELF_RELOC); + elf_machine_relative (load_addr, rel_addr, relative_count); + rel_addr += relative_count * sizeof(ELF_RELOC);; + } + rpnt = (ELF_RELOC *) (rel_addr + load_addr); for (i = 0; i < rel_size; i += sizeof(ELF_RELOC), rpnt++) { reloc_addr = (unsigned long *) (load_addr + (unsigned long) rpnt->r_offset); diff --git a/ldso/ldso/i386/dl-sysdep.h b/ldso/ldso/i386/dl-sysdep.h index a534110e7..b0edee369 100644 --- a/ldso/ldso/i386/dl-sysdep.h +++ b/ldso/ldso/i386/dl-sysdep.h @@ -7,7 +7,7 @@ /* Define this if the system uses RELOCA. */ #undef ELF_USES_RELOCA - +#include <elf.h> /* Initialization sequence for the GOT. */ #define INIT_GOT(GOT_BASE,MODULE) \ do { \ @@ -40,3 +40,42 @@ extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_ent #define elf_machine_type_class(type) \ ((((type) == R_386_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_386_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_dynamic (void) +{ + register Elf32_Addr *got asm ("%ebx"); + return *got; +} + + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_load_address (void) +{ + /* It doesn't matter what variable this is, the reference never makes + it to assembly. We need a dummy reference to some global variable + via the GOT to make sure the compiler initialized %ebx in time. */ + extern int _dl_argc; + Elf32_Addr addr; + asm ("leal _dl_boot@GOTOFF(%%ebx), %0\n" + "subl _dl_boot@GOT(%%ebx), %0" + : "=r" (addr) : "m" (_dl_argc) : "cc"); + return addr; +} + +static inline void +elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + Elf32_Rel * rpnt = (void *) (rel_addr + load_off); + --rpnt; + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + + *reloc_addr = load_off; + } while (--relative_count); +} diff --git a/ldso/ldso/m68k/dl-sysdep.h b/ldso/ldso/m68k/dl-sysdep.h index 1eff443e7..0670fd3d0 100644 --- a/ldso/ldso/m68k/dl-sysdep.h +++ b/ldso/ldso/m68k/dl-sysdep.h @@ -4,7 +4,7 @@ /* Define this if the system uses RELOCA. */ #define ELF_USES_RELOCA - +#include <elf.h> /* Initialization sequence for a GOT. */ #define INIT_GOT(GOT_BASE,MODULE) \ { \ @@ -37,3 +37,38 @@ extern unsigned int _dl_linux_resolver (int, int, struct elf_resolve *, int); #define elf_machine_type_class(type) \ ((((type) == R_68K_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_68K_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. */ +static inline Elf32_Addr +elf_machine_dynamic (void) +{ + register Elf32_Addr *got asm ("%a5"); + return *got; +} + + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr +elf_machine_load_address (void) +{ + Elf32_Addr addr; + asm ("lea _dl_boot(%%pc), %0\n\t" + "sub.l _dl_boot@GOT.w(%%a5), %0" + : "=a" (addr)); + 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 + load_off); + --rpnt; + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + + *reloc_addr = load_off + rpnt->r_addend; + } while (--relative_count); +} diff --git a/ldso/ldso/mips/dl-sysdep.h b/ldso/ldso/mips/dl-sysdep.h index c3c5a6976..3f4672182 100644 --- a/ldso/ldso/mips/dl-sysdep.h +++ b/ldso/ldso/mips/dl-sysdep.h @@ -7,7 +7,7 @@ /* Define this if the system uses RELOCA. */ #undef ELF_USES_RELOCA - +#include <elf.h> #define ARCH_NUM 3 #define DT_MIPS_GOTSYM_IDX (DT_NUM + OS_NUM) #define DT_MIPS_LOCAL_GOTNO_IDX (DT_NUM + OS_NUM +1) @@ -71,3 +71,52 @@ void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt, int lazy) #define elf_machine_type_class(type) ELF_RTYPE_CLASS_PLT /* MIPS does not have COPY relocs */ #define DL_NO_COPY_RELOCS + +#define OFFSET_GP_GOT 0x7ff0 + +static inline ElfW(Addr) * +elf_mips_got_from_gpreg (ElfW(Addr) gpreg) +{ + /* FIXME: the offset of gp from GOT may be system-dependent. */ + return (ElfW(Addr) *) (gpreg - OFFSET_GP_GOT); +} + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. We assume its $gp points to the primary GOT. */ +static inline ElfW(Addr) +elf_machine_dynamic (void) +{ + register ElfW(Addr) gp __asm__ ("$28"); + return *elf_mips_got_from_gpreg (gp); +} + +#define STRINGXP(X) __STRING(X) +#define STRINGXV(X) STRINGV_(X) +#define STRINGV_(...) # __VA_ARGS__ +#define PTR_LA la +#define PTR_SUBU subu + +/* Return the run-time load address of the shared object. */ +static inline ElfW(Addr) +elf_machine_load_address (void) +{ + ElfW(Addr) addr; + asm (" .set noreorder\n" + " " STRINGXP (PTR_LA) " %0, 0f\n" + " bltzal $0, 0f\n" + " nop\n" + "0: " STRINGXP (PTR_SUBU) " %0, $31, %0\n" + " .set reorder\n" + : "=r" (addr) + : /* No inputs */ + : "$31"); + return addr; +} + +static inline void +elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + /* No REALTIVE relocs in MIPS? */ +} diff --git a/ldso/ldso/powerpc/dl-sysdep.h b/ldso/ldso/powerpc/dl-sysdep.h index 6bbdb8c47..b09ada5c3 100644 --- a/ldso/ldso/powerpc/dl-sysdep.h +++ b/ldso/ldso/powerpc/dl-sysdep.h @@ -7,7 +7,7 @@ * Define this if the system uses RELOCA. */ #define ELF_USES_RELOCA - +#include <elf.h> /* * Initialization sequence for a GOT. */ @@ -91,3 +91,78 @@ void _dl_init_got(unsigned long *lpnt,struct elf_resolve *tpnt); /* The SVR4 ABI specifies that the JMPREL relocs must be inside the DT_RELA table. */ #define ELF_MACHINE_PLTREL_OVERLAP 1 + +/* Return the link-time address of _DYNAMIC, stored as + the first value in the GOT. */ +static inline Elf32_Addr +elf_machine_dynamic (void) +{ + Elf32_Addr *got; + asm (" bl _GLOBAL_OFFSET_TABLE_-4@local" + : "=l"(got)); + return *got; +} + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr +elf_machine_load_address (void) +{ + unsigned int *got; + unsigned int *branchaddr; + + /* This is much harder than you'd expect. Possibly I'm missing something. + The 'obvious' way: + + Apparently, "bcl 20,31,$+4" is what should be used to load LR + with the address of the next instruction. + I think this is so that machines that do bl/blr pairing don't + get confused. + + asm ("bcl 20,31,0f ;" + "0: mflr 0 ;" + "lis %0,0b@ha;" + "addi %0,%0,0b@l;" + "subf %0,%0,0" + : "=b" (addr) : : "r0", "lr"); + + doesn't work, because the linker doesn't have to (and in fact doesn't) + update the @ha and @l references; the loader (which runs after this + code) will do that. + + Instead, we use the following trick: + + The linker puts the _link-time_ address of _DYNAMIC at the first + word in the GOT. We could branch to that address, if we wanted, + by using an @local reloc; the linker works this out, so it's safe + to use now. We can't, of course, actually branch there, because + we'd cause an illegal instruction exception; so we need to compute + the address ourselves. That gives us the following code: */ + + /* Get address of the 'b _DYNAMIC@local'... */ + asm ("bl 0f ;" + "b _DYNAMIC@local;" + "0:" + : "=l"(branchaddr)); + + /* ... and the address of the GOT. */ + asm (" bl _GLOBAL_OFFSET_TABLE_-4@local" + : "=l"(got)); + + /* So now work out the difference between where the branch actually points, + and the offset of that location in memory from the start of the file. */ + return ((Elf32_Addr)branchaddr - *got + + ((int)(*branchaddr << 6 & 0xffffff00) >> 6)); +} + +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 + load_off); + --rpnt; + do { /* PowerPC handles pre increment/decrement better */ + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + + *reloc_addr = load_off + rpnt->r_addend; + } while (--relative_count); +} diff --git a/ldso/ldso/powerpc/elfinterp.c b/ldso/ldso/powerpc/elfinterp.c index d3dd269fe..e156aa355 100644 --- a/ldso/ldso/powerpc/elfinterp.c +++ b/ldso/ldso/powerpc/elfinterp.c @@ -421,7 +421,7 @@ _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope, ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)) { - unsigned int i, relative_count; + unsigned int i; char *strtab; Elf32_Sym *symtab; ELF_RELOC *rpnt; @@ -434,18 +434,6 @@ _dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope, symtab = (Elf32_Sym *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr); strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr); - relative_count = tpnt->dynamic_info[DT_RELCONT_IDX]; - if (relative_count) { /* Optimize the R_PPC_RELATIVE relocations if possible */ - Elf32_Addr loadaddr = tpnt->loadaddr; - rel_size -= relative_count; - --rpnt; - do { /* PowerPC handles pre increment/decrement better */ - Elf32_Addr *const reloc_addr = (void *) (loadaddr + (++rpnt)->r_offset); - - *reloc_addr = loadaddr + rpnt->r_addend; - } while (--relative_count); - } - for (i = 0; i < rel_size; i++, rpnt++) { int res; diff --git a/ldso/ldso/sh/dl-sysdep.h b/ldso/ldso/sh/dl-sysdep.h index 134e901c7..b332cc1ac 100644 --- a/ldso/ldso/sh/dl-sysdep.h +++ b/ldso/ldso/sh/dl-sysdep.h @@ -5,7 +5,7 @@ /* Define this if the system uses RELOCA. */ #define ELF_USES_RELOCA - +#include <elf.h> /* * Initialization sequence for a GOT. */ @@ -96,3 +96,79 @@ _dl_urem(unsigned int n, unsigned int base) #define elf_machine_type_class(type) \ ((((type) == R_SH_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_SH_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_dynamic (void) +{ + register Elf32_Addr *got; + asm ("mov r12,%0" :"=r" (got)); + return *got; +} + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_load_address (void) +{ + Elf32_Addr addr; + asm ("mov.l 1f,r0\n\ + mov.l 3f,r2\n\ + add r12,r2\n\ + mov.l @(r0,r12),r0\n\ + bra 2f\n\ + sub r0,r2\n\ + .align 2\n\ + 1: .long _dl_boot@GOT\n\ + 3: .long _dl_boot@GOTOFF\n\ + 2: mov r2,%0" + : "=r" (addr) : : "r0", "r1", "r2"); + return addr; +} + +#define COPY_UNALIGNED_WORD(swp, twp, align) \ + { \ + void *__s = (swp), *__t = (twp); \ + unsigned char *__s1 = __s, *__t1 = __t; \ + unsigned short *__s2 = __s, *__t2 = __t; \ + unsigned long *__s4 = __s, *__t4 = __t; \ + switch ((align)) \ + { \ + case 0: \ + *__t4 = *__s4; \ + break; \ + case 2: \ + *__t2++ = *__s2++; \ + *__t2 = *__s2; \ + break; \ + default: \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1 = *__s1; \ + break; \ + } \ + } + +static inline void +elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + Elf32_Addr value; + Elf32_Rela * rpnt = (void *) (rel_addr + load_off); + + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + rpnt->r_offset); + + if (rpnt->r_addend) + value = load_off + rpnt->r_addend; + else { + COPY_UNALIGNED_WORD (reloc_addr, &value, (int) reloc_addr & 3); + value += load_off; + } + COPY_UNALIGNED_WORD (&value, reloc_addr, (int) reloc_addr & 3); + rpnt++; + } while (--relative_count); +#undef COPY_UNALIGNED_WORD +} diff --git a/ldso/ldso/sh64/dl-sysdep.h b/ldso/ldso/sh64/dl-sysdep.h index 98a21aedb..9e35e6029 100644 --- a/ldso/ldso/sh64/dl-sysdep.h +++ b/ldso/ldso/sh64/dl-sysdep.h @@ -6,7 +6,7 @@ /* Define this if the system uses RELOCA. */ #define ELF_USES_RELOCA - +#include <elf.h> /* * Initialization sequence for a GOT. */ @@ -40,3 +40,79 @@ extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_ent #define elf_machine_type_class(type) \ ((((type) == R_SH_JMP_SLOT) * ELF_RTYPE_CLASS_PLT) \ | (((type) == R_SH_COPY) * ELF_RTYPE_CLASS_COPY)) + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_dynamic (void) +{ + register Elf32_Addr *got; + asm ("mov r12,%0" :"=r" (got)); + return *got; +} + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr __attribute__ ((unused)) +elf_machine_load_address (void) +{ + Elf32_Addr addr; + asm ("mov.l 1f,r0\n\ + mov.l 3f,r2\n\ + add r12,r2\n\ + mov.l @(r0,r12),r0\n\ + bra 2f\n\ + sub r0,r2\n\ + .align 2\n\ + 1: .long _dl_boot@GOT\n\ + 3: .long _dl_boot@GOTOFF\n\ + 2: mov r2,%0" + : "=r" (addr) : : "r0", "r1", "r2"); + return addr; +} + +#define COPY_UNALIGNED_WORD(swp, twp, align) \ + { \ + void *__s = (swp), *__t = (twp); \ + unsigned char *__s1 = __s, *__t1 = __t; \ + unsigned short *__s2 = __s, *__t2 = __t; \ + unsigned long *__s4 = __s, *__t4 = __t; \ + switch ((align)) \ + { \ + case 0: \ + *__t4 = *__s4; \ + break; \ + case 2: \ + *__t2++ = *__s2++; \ + *__t2 = *__s2; \ + break; \ + default: \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1 = *__s1; \ + break; \ + } \ + } + +static inline void +elf_machine_relative (Elf32_Addr load_off, const Elf32_Addr rel_addr, + Elf32_Word relative_count) +{ + Elf32_Addr value; + Elf32_Rela * rpnt = (void *) (rel_addr + load_off); + + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + rpnt->r_offset); + + if (rpnt->r_addend) + value = load_off + rpnt->r_addend; + else { + COPY_UNALIGNED_WORD (reloc_addr, &value, (int) reloc_addr & 3); + value += load_off; + } + COPY_UNALIGNED_WORD (&value, reloc_addr, (int) reloc_addr & 3); + rpnt++; + } while (--relative_count); +#undef COPY_UNALIGNED_WORD +} diff --git a/ldso/ldso/sparc/dl-sysdep.h b/ldso/ldso/sparc/dl-sysdep.h index d48120e2d..7559a80d2 100644 --- a/ldso/ldso/sparc/dl-sysdep.h +++ b/ldso/ldso/sparc/dl-sysdep.h @@ -7,7 +7,7 @@ /* Define this if the system uses RELOCA. */ #define ELF_USES_RELOCA - +#include <elf.h> /* * Initialization sequence for a GOT. For the Sparc, this points to the * PLT, and we need to initialize a couple of the slots. The PLT should @@ -110,3 +110,61 @@ sparc_mod(unsigned long m, unsigned long p) /* The SPARC overlaps DT_RELA and DT_PLTREL. */ #define ELF_MACHINE_PLTREL_OVERLAP 1 + +/* We have to do this because elf_machine_{dynamic,load_address} can be + invoked from functions that have no GOT references, and thus the compiler + has no obligation to load the PIC register. */ +#define LOAD_PIC_REG(PIC_REG) \ +do { register Elf32_Addr pc __asm("o7"); \ + __asm("sethi %%hi(_GLOBAL_OFFSET_TABLE_-4), %1\n\t" \ + "call 1f\n\t" \ + "add %1, %%lo(_GLOBAL_OFFSET_TABLE_+4), %1\n" \ + "1:\tadd %1, %0, %1" \ + : "=r" (pc), "=r" (PIC_REG)); \ +} while (0) + +/* Return the link-time address of _DYNAMIC. Conveniently, this is the + first element of the GOT. This must be inlined in a function which + uses global data. */ +static inline Elf32_Addr +elf_machine_dynamic (void) +{ + register Elf32_Addr *got asm ("%l7"); + + LOAD_PIC_REG (got); + + return *got; +} + +/* Return the run-time load address of the shared object. */ +static inline Elf32_Addr +elf_machine_load_address (void) +{ + register Elf32_Addr *pc __asm ("%o7"), *got __asm ("%l7"); + + __asm ("sethi %%hi(_GLOBAL_OFFSET_TABLE_-4), %1\n\t" + "call 1f\n\t" + " add %1, %%lo(_GLOBAL_OFFSET_TABLE_+4), %1\n\t" + "call _DYNAMIC\n\t" + "call _GLOBAL_OFFSET_TABLE_\n" + "1:\tadd %1, %0, %1\n\t" : "=r" (pc), "=r" (got)); + + /* got is now l_addr + _GLOBAL_OFFSET_TABLE_ + *got is _DYNAMIC + pc[2]*4 is l_addr + _DYNAMIC - (long)pc - 8 + pc[3]*4 is l_addr + _GLOBAL_OFFSET_TABLE_ - (long)pc - 12 */ + return (Elf32_Addr) got - *got + (pc[2] - pc[3]) * 4 - 4; +} + +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 + load_off); + --rpnt; + do { + Elf32_Addr *const reloc_addr = (void *) (load_off + (++rpnt)->r_offset); + + *reloc_addr = load_off + rpnt->r_addend; + } while (--relative_count); +} |