summaryrefslogtreecommitdiff
path: root/libexec/ld.so/sparc
diff options
context:
space:
mode:
authorArtur Grabowski <art@cvs.openbsd.org>2002-07-27 16:56:02 +0000
committerArtur Grabowski <art@cvs.openbsd.org>2002-07-27 16:56:02 +0000
commitb2058d917d398cfb5416d0cc8c658ceea3a10aef (patch)
tree01f0016a48a99498ccb22105bd3345c0611f3e79 /libexec/ld.so/sparc
parentea208f4d3faf66cdbf9308f962bbb0d72c6c7fc7 (diff)
Commit work in progress before I get drunk tonight.
Code from sparc64 and NetBSD. Basically we can link the most, lazy linking works (!), but something else is screwed.
Diffstat (limited to 'libexec/ld.so/sparc')
-rw-r--r--libexec/ld.so/sparc/rtld_machine.c322
1 files changed, 314 insertions, 8 deletions
diff --git a/libexec/ld.so/sparc/rtld_machine.c b/libexec/ld.so/sparc/rtld_machine.c
index 546e8c40fe7..2ff19519864 100644
--- a/libexec/ld.so/sparc/rtld_machine.c
+++ b/libexec/ld.so/sparc/rtld_machine.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtld_machine.c,v 1.1 2002/07/27 13:19:26 art Exp $ */
+/* $OpenBSD: rtld_machine.c,v 1.2 2002/07/27 16:56:01 art Exp $ */
/*
* Copyright (c) 1999 Dale Rahn
@@ -57,26 +57,332 @@ _dl_bcopy(const void *src, void *dest, int size)
pdest[i] = psrc[i];
}
+/*
+ * The following table holds for each relocation type:
+ * - the width in bits of the memory location the relocation
+ * applies to (not currently used)
+ * - the number of bits the relocation value must be shifted to the
+ * right (i.e. discard least significant bits) to fit into
+ * the appropriate field in the instruction word.
+ * - flags indicating whether
+ * * the relocation involves a symbol
+ * * the relocation is relative to the current position
+ * * the relocation is for a GOT entry
+ * * the relocation is relative to the load address
+ *
+ */
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ((s) & 0xff) /* right shift */
+static int reloc_target_flags[] = {
+ 0, /* NONE */
+ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* RELOC_8 */
+ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* RELOC_16 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* RELOC_32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* HI22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 13 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LO10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */
+ _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */
+ _RF_SZ(32) | _RF_RS(0), /* COPY */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_DAT */
+ _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */
+ _RF_A| _RF_B| _RF_SZ(32) | _RF_RS(0), /* RELATIVE */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* UA_32 */
+
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* PLT32 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* HIPLT22 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* PCPLT22 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 10 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 11 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 64 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* HH22 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* HM10 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* LM22 */
+ _RF_S|_RF_A|_RF_P|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* WDISP16 */
+ _RF_S|_RF_A|_RF_P|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* WDISP19 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 7 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 5 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 6 */
+};
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+
+static int reloc_target_bitmask[] = {
+#define _BM(x) (~(-(1ULL << (x))))
+ 0, /* NONE */
+ _BM(8), _BM(16), _BM(32), /* RELOC_8, _16, _32 */
+ _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */
+ _BM(30), _BM(22), /* WDISP30, WDISP22 */
+ _BM(22), _BM(22), /* HI22, _22 */
+ _BM(13), _BM(10), /* RELOC_13, _LO10 */
+ _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */
+ _BM(10), _BM(22), /* _PC10, _PC22 */
+ _BM(30), 0, /* _WPLT30, _COPY */
+ -1, -1, -1, /* _GLOB_DAT, JMP_SLOT, _RELATIVE */
+ _BM(32), _BM(32), /* _UA32, PLT32 */
+ _BM(22), _BM(10), /* _HIPLT22, LOPLT10 */
+ _BM(32), _BM(22), _BM(10), /* _PCPLT32, _PCPLT22, _PCPLT10 */
+ _BM(10), _BM(11), -1, /* _10, _11, _64 */
+ _BM(10), _BM(22), /* _OLO10, _HH22 */
+ _BM(10), _BM(22), /* _HM10, _LM22 */
+ _BM(16), _BM(19), /* _WDISP16, _WDISP19 */
+ -1, /* GLOB_JMP */
+ _BM(7), _BM(5), _BM(6) /* _7, _5, _6 */
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+void _dl_reloc_plt(Elf_Word *where, Elf_Addr value);
+
int
_dl_md_reloc(elf_object_t *object, int rel, int relasz)
{
- return (0);
+ long i;
+ long numrela;
+ long fails = 0;
+ Elf_Addr loff;
+ Elf_RelA *relas;
+ struct load_list *llist;
+
+ loff = object->load_offs;
+ numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
+ relas = (Elf_RelA *)(object->Dyn.info[rel]);
+
+ if (relas == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ */
+ if ((rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+
+ for (i = 0; i < numrela; i++, relas++) {
+ Elf_Addr *where, value, ooff, mask;
+ Elf_Word type;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+
+ type = ELF_R_TYPE(relas->r_info);
+
+ if (type == R_TYPE(NONE))
+ continue;
+
+ if (type == R_TYPE(JMP_SLOT) && rel != DT_JMPREL)
+ continue;
+
+ where = (Elf_Addr *)(relas->r_offset + loff);
+
+ if (RELOC_USE_ADDEND(type))
+ value = relas->r_addend;
+ else
+ value = 0;
+
+ sym = NULL;
+ symn = NULL;
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(relas->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ if (sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ value += loff;
+ } else {
+ this = NULL;
+ ooff = _dl_find_symbol(symn, _dl_objects,
+ &this, 0, 1);
+ if (this == NULL) {
+resolve_failed:
+ _dl_printf("%s: %s: can't resolve "
+ "reference '%s'\n",
+ _dl_progname, object->load_name,
+ symn);
+ fails++;
+ continue;
+ }
+ value += (Elf_Addr)(ooff + this->st_value);
+#ifdef notyet
+/*
+ * XXX Hmm, we should change the API of _dl_find_symbol and do this in there,
+ * XXX or maybe make a wrapper.
+ */
+ if (this->st_size != sym->st_size &&
+ sym->st_size != 0) {
+ _dl_printf("%s: %s : WARNING: "
+ "symbol(%s) size mismatch ",
+ _dl_progname, object->load_name,
+ symn);
+ _dl_printf("relink your program\n");
+ }
+#endif
+ }
+ }
+
+ if (type == R_TYPE(JMP_SLOT)) {
+ _dl_reloc_plt((Elf_Word *)where, value);
+ continue;
+ }
+
+ if (type == R_TYPE(COPY)) {
+ void *dstaddr = where;
+ const void *srcaddr;
+ const Elf_Sym *dstsym = sym, *srcsym = NULL;
+ size_t size = dstsym->st_size;
+ Elf_Addr soff;
+
+ soff = _dl_find_symbol(symn, object->next, &srcsym,
+ 0, 2);
+ if (srcsym == NULL)
+ goto resolve_failed;
+
+ srcaddr = (void *)(soff + srcsym->st_value);
+ _dl_bcopy(srcaddr, dstaddr, size);
+ continue;
+ }
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+ if (RELOC_BASE_RELATIVE(type))
+ value += loff + *where;
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ /* We ignore alignment restrictions here */
+ *where &= ~mask;
+ *where |= value;
+
+ }
+
+ /* reprotect the unprotected segments */
+ if ((rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+
+ return (fails);
+}
+
+void
+_dl_reloc_plt(Elf_Word *where, Elf_Addr value)
+{
+ /*
+ * At the PLT entry pointed at by `where', we now construct
+ * a direct transfer to the now fully resolved function
+ * address. The resulting code in the jump slot is:
+ *
+ * sethi %hi(roffset), %g1
+ * sethi %hi(addr), %g1
+ * jmp %g1+%lo(addr)
+ *
+ * We write the third instruction first, since that leaves the
+ * previous `b,a' at the second word in place. Hence the whole
+ * PLT slot can be atomically change to the new sequence by
+ * writing the `sethi' instruction at word 2.
+ */
+#define SETHI 0x03000000
+#define JMP 0x81c06000
+#define NOP 0x01000000
+ where[2] = JMP | (value & 0x000003ff);
+ where[1] = SETHI | ((value >> 10) & 0x003fffff);
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
}
+
/*
* Resolve a symbol at run-time.
*/
void *
-_dl_bind(elf_object_t *object, int index)
+_dl_bind(elf_object_t *object, Elf_Word reloff)
{
- return (NULL);
-}
+ Elf_RelA *rela;
+ Elf_Addr *addr, ooff;
+ const Elf_Sym *sym, *this;
+ const char *symn;
-void
-_dl_install_plt(Elf_Word *pltgot, Elf_Addr proc)
-{
+ rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rela->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+DL_DEB(("_dl_bind %s\n", symn));
+
+ addr = (Elf_Addr *)(object->load_offs + rela->r_offset);
+ ooff = _dl_find_symbol(symn, _dl_objects, &this, 0, 1);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ _dl_reloc_plt(addr, ooff + this->st_value);
+
+ return (void *)ooff + this->st_value;
}
void
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
+ Elf_Addr *pltgot;
+ extern void _dl_bind_start(void); /* XXX */
+
+ pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+
+ if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) {
+ _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ return;
+ }
+
+ /*
+ * PLTGOT is the PLT on the sparc.
+ * The first entry holds the call the dynamic linker.
+ * We construct a `call' sequence that transfers
+ * to `_rtld_bind_start()'.
+ * The second entry holds the object identification.
+ * Note: each PLT entry is three words long.
+ */
+#define SAVE 0x9de3bfc0 /* i.e. `save %sp,-64,%sp' */
+#define CALL 0x40000000
+#define NOP 0x01000000
+ pltgot[0] = SAVE;
+ pltgot[1] = CALL |
+ ((Elf_Addr)&_dl_bind_start -
+ (Elf_Addr)&pltgot[1]) >> 2;
+ pltgot[2] = NOP;
+ pltgot[3] = (Elf_Addr) object;
}