diff options
author | Artur Grabowski <art@cvs.openbsd.org> | 2002-07-27 16:56:02 +0000 |
---|---|---|
committer | Artur Grabowski <art@cvs.openbsd.org> | 2002-07-27 16:56:02 +0000 |
commit | b2058d917d398cfb5416d0cc8c658ceea3a10aef (patch) | |
tree | 01f0016a48a99498ccb22105bd3345c0611f3e79 /libexec/ld.so/sparc | |
parent | ea208f4d3faf66cdbf9308f962bbb0d72c6c7fc7 (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.c | 322 |
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; } |