diff options
Diffstat (limited to 'ldso/ldso/sh64/dl-sysdep.h')
-rw-r--r-- | ldso/ldso/sh64/dl-sysdep.h | 153 |
1 files changed, 103 insertions, 50 deletions
diff --git a/ldso/ldso/sh64/dl-sysdep.h b/ldso/ldso/sh64/dl-sysdep.h index 852cb0d41..506e9ece2 100644 --- a/ldso/ldso/sh64/dl-sysdep.h +++ b/ldso/ldso/sh64/dl-sysdep.h @@ -44,74 +44,127 @@ extern unsigned long _dl_linux_resolver(struct elf_resolve * tpnt, int reloc_ent /* 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) +static inline Elf32_Addr elf_machine_dynamic(void) { register Elf32_Addr *got; - asm ("mov r12,%0" :"=r" (got)); + + /* + * The toolchain adds 32768 to the GOT address, we compensate for + * that in the movi/sub pair. + * + * XXX: If this is cleaned up in the toolchain, we can end up + * saving 2 instructions and subsequently free up r1 from the + * clobber list.. + */ + __asm__ ( + "movi\t(((datalabel _GLOBAL_OFFSET_TABLE_-(.LZZZ1-.)) >> 16) & 0xffff), r2\n\t" + "shori\t((datalabel _GLOBAL_OFFSET_TABLE_-(.LZZZ1-.)) & 0xffff), r2\n\t" + ".LZZZ1:\tptrel/u r2, tr0\n\t" + "movi\t32768, r1\n\t" + "gettr\ttr0, r2\n\t" + "sub\tr2, r1, %0\n\t" + : "=r" (got) + : /* no inputs */ + : "r1", "r2", "tr0" + ); + return *got; } /* Return the run-time load address of the shared object. */ -static inline Elf32_Addr __attribute__ ((unused)) -elf_machine_load_address (void) +static inline Elf32_Addr 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"); + + __asm__ ( + "movi\t(((datalabel _GLOBAL_OFFSET_TABLE_-(.LZZZ2-.)) >> 16) & 0xffff), r0\n\t" + "shori\t((datalabel _GLOBAL_OFFSET_TABLE_-(.LZZZ2-.)) & 0xffff), r0\n\t" + ".LZZZ2:\tptrel/u r0, tr0\n\t" + "movi\t(((_dl_start@GOTOFF) >> 16) & 0xffff), r2\n\t" + "shori\t((_dl_start@GOTOFF) & 0xffff), r2\n\t" + "gettr\ttr0, r0\n\t" + "add\tr2, r0, r2\n\t" + "movi\t(((_dl_start@GOT) >> 16) & 0xffff), r1\n\t" + "shori\t((_dl_start@GOT) & 0xffff), r1\n\t" + "ldx.l\tr1, r0, r1\n\t" + "sub\tr2, r1, %0\n\t" + : "=r" (addr) + : /* no inputs */ + : "r0", "r1", "r2", "tr0" + ); + 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; \ - } \ - } +/* + * XXX: As we don't need to worry about r25 clobbering, we could probably + * get away with inlining {st,ld}{x,}.l and friends here instead and + * forego gcc's idea of code generation. + */ +#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) +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; + Elf32_Addr value, word; + Elf32_Rela *rpnt = (void *)rel_addr; + int reloc_type = ELF32_R_TYPE(rpnt->r_info); do { - Elf32_Addr *const reloc_addr = (void *) (load_off + rpnt->r_offset); + Elf32_Addr *const reloc_addr = + (void *)(load_off + rpnt->r_offset); + int align = (int)reloc_addr & 3; - if (rpnt->r_addend) - value = load_off + rpnt->r_addend; - else { - COPY_UNALIGNED_WORD (reloc_addr, &value, (int) reloc_addr & 3); - value += load_off; + switch (reloc_type) { + case R_SH_RELATIVE_LOW16: + COPY_UNALIGNED_WORD(reloc_addr, &word, align); + word &= ~0x3fffc00; + value = (rpnt->r_addend + load_off); + word |= (value & 0xffff) << 10; + COPY_UNALIGNED_WORD(&word, reloc_addr, align); + break; + case R_SH_RELATIVE_MEDLOW16: + COPY_UNALIGNED_WORD(reloc_addr, &word, align); + word &= ~0x3fffc00; + value = (rpnt->r_addend + load_off) >> 16; + word |= (value & 0xffff) << 10; + COPY_UNALIGNED_WORD(&word, reloc_addr, align); + break; + default: + if (rpnt->r_addend) { + value = load_off + rpnt->r_addend; + } else { + COPY_UNALIGNED_WORD(reloc_addr, &value, align); + value += load_off; + } + + COPY_UNALIGNED_WORD(&value, reloc_addr, align); + break; } - COPY_UNALIGNED_WORD (&value, reloc_addr, (int) reloc_addr & 3); + rpnt++; } while (--relative_count); #undef COPY_UNALIGNED_WORD |