summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoakim Tjernlund <joakim.tjernlund@transmode.se>2005-03-14 13:25:07 +0000
committerJoakim Tjernlund <joakim.tjernlund@transmode.se>2005-03-14 13:25:07 +0000
commit7d137fcf818e9a157a9f7ed9df61896f6cf97490 (patch)
tree4cd0201de5e99f8f7fc3e40ac97fe9e44411d29f
parent3b67c539e3f14a7acf59608d6f7a37b710033a5c (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__
-rw-r--r--ldso/ldso/arm/dl-sysdep.h37
-rw-r--r--ldso/ldso/cris/dl-sysdep.h46
-rw-r--r--ldso/ldso/dl-elf.c11
-rw-r--r--ldso/ldso/dl-startup.c24
-rw-r--r--ldso/ldso/i386/dl-sysdep.h41
-rw-r--r--ldso/ldso/m68k/dl-sysdep.h37
-rw-r--r--ldso/ldso/mips/dl-sysdep.h51
-rw-r--r--ldso/ldso/powerpc/dl-sysdep.h77
-rw-r--r--ldso/ldso/powerpc/elfinterp.c14
-rw-r--r--ldso/ldso/sh/dl-sysdep.h78
-rw-r--r--ldso/ldso/sh64/dl-sysdep.h78
-rw-r--r--ldso/ldso/sparc/dl-sysdep.h60
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);
+}