diff options
Diffstat (limited to 'libexec/rtld-elf/alpha/reloc.c')
-rw-r--r-- | libexec/rtld-elf/alpha/reloc.c | 204 |
1 files changed, 161 insertions, 43 deletions
diff --git a/libexec/rtld-elf/alpha/reloc.c b/libexec/rtld-elf/alpha/reloc.c index e5837b8..743b1c2 100644 --- a/libexec/rtld-elf/alpha/reloc.c +++ b/libexec/rtld-elf/alpha/reloc.c @@ -1,5 +1,5 @@ /*- - * Copyright 1996-1998 John D. Polstra. + * Copyright 1996, 1997, 1998, 1999 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: reloc.c,v 1.3 1998/09/11 18:30:55 dfr Exp $ + * $Id: reloc.c,v 1.4 1999/04/09 00:28:43 jdp Exp $ */ /* @@ -168,67 +168,185 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld) int reloc_plt(Obj_Entry *obj, bool bind_now) { + /* All PLT relocations are the same kind: either Elf_Rel or Elf_Rela. */ + if (obj->pltrelsize != 0) { const Elf_Rel *rellim; const Elf_Rel *rel; - const Elf_Rela *relalim; - const Elf_Rela *rela; - /* Process the PLT relocations without addend if there are any. */ - rellim = (const Elf_Rel *) ((caddr_t) obj->pltrel + obj->pltrelsize); - if (bind_now) { - /* Fully resolve procedure addresses now */ - for (rel = obj->pltrel; obj->pltrel != NULL && rel < rellim; - rel++) { - Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rel->r_offset); + rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); + for (rel = obj->pltrel; rel < rellim; rel++) { + Elf_Addr *where; + + assert(ELF_R_TYPE(rel->r_info) == R_ALPHA_JMP_SLOT); + + /* Relocate the GOT slot pointing into the PLT. */ + where = (Elf_Addr *)(obj->relocbase + rel->r_offset); + *where += (Elf_Addr)obj->relocbase; + + if (bind_now) { /* Fully resolve the procedure address. */ const Elf_Sym *def; const Obj_Entry *defobj; - assert(ELF_R_TYPE(rel->r_info) == R_ALPHA_JMP_SLOT); - def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true); if (def == NULL) - return -1; - - *where = (Elf_Addr) (defobj->relocbase + def->st_value); - } - } else { /* Just relocate the GOT slots pointing into the PLT */ - for (rel = obj->pltrel; obj->pltrel != NULL && rel < rellim; - rel++) { - Elf_Addr *where = (Elf_Addr *) - (obj->relocbase + rel->r_offset); - *where += (Elf_Addr) obj->relocbase; + return -1; + reloc_jmpslot(where, + (Elf_Addr)(defobj->relocbase + def->st_value)); } } + } else { + const Elf_Rela *relalim; + const Elf_Rela *rela; + + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + Elf_Addr *where; + + assert(ELF_R_TYPE(rela->r_info) == R_ALPHA_JMP_SLOT); + + /* Relocate the GOT slot pointing into the PLT. */ + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + *where += (Elf_Addr)obj->relocbase; - /* Process the PLT relocations with addend if there are any. */ - relalim = (const Elf_Rela *) ((caddr_t) obj->pltrela + - obj->pltrelasize); - if (bind_now) { - /* Fully resolve procedure addresses now */ - for (rela = obj->pltrela; obj->pltrela != NULL && rela < relalim; - rela++) { - Elf_Addr *where = (Elf_Addr *) (obj->relocbase + - rela->r_offset); + if (bind_now) { /* Fully resolve the procedure address. */ const Elf_Sym *def; const Obj_Entry *defobj; - assert(ELF_R_TYPE(rela->r_info) == R_ALPHA_JMP_SLOT); - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true); if (def == NULL) - return -1; + return -1; + reloc_jmpslot(where, + (Elf_Addr)(defobj->relocbase + def->st_value)); + } + } + } + return 0; +} - *where = (Elf_Addr) (defobj->relocbase + def->st_value); +/* Fixup the jump slot at "where" to transfer control to "target". */ +void +reloc_jmpslot(Elf_Addr *where, Elf_Addr target) +{ + Elf_Addr stubaddr; + + dbg(" reloc_jmpslot: where=%p, target=%p", (void *)where, (void *)target); + stubaddr = *where; + if (stubaddr != target) { + int64_t delta; + u_int32_t inst[3]; + int instct; + Elf_Addr pc; + int64_t idisp; + u_int32_t *stubptr; + + /* Point this GOT entry directly at the target. */ + *where = target; + + /* + * There may be multiple GOT tables, each with an entry + * pointing to the stub in the PLT. But we can only find and + * fix up the first GOT entry. So we must rewrite the stub as + * well, to perform a call to the target if it is executed. + * + * When the stub gets control, register pv ($27) contains its + * address. We adjust its value so that it points to the + * target, and then jump indirect through it. + * + * Each PLT entry has room for 3 instructions. If the + * adjustment amount fits in a signed 32-bit integer, we can + * simply add it to register pv. Otherwise we must load the + * GOT entry itself into the pv register. + */ + delta = target - stubaddr; + dbg(" stubaddr=%p, where-stubaddr=%ld, delta=%ld", (void *)stubaddr, + (long)where - (long)stubaddr, (long)delta); + instct = 0; + if ((int32_t)delta == delta) { + /* + * We can adjust pv with a LDA, LDAH sequence. + * + * First build an LDA instruction to adjust the low 16 bits. + */ + inst[instct++] = 0x08 << 26 | 27 << 21 | 27 << 16 | + (delta & 0xffff); + dbg(" LDA $27,%d($27)", (int16_t)delta); + /* + * Adjust the delta to account for the effects of the LDA, + * including sign-extension. + */ + delta -= (int16_t)delta; + if (delta != 0) { + /* Build an LDAH instruction to adjust the high 16 bits. */ + inst[instct++] = 0x09 << 26 | 27 << 21 | 27 << 16 | + (delta >> 16 & 0xffff); + dbg(" LDAH $27,%d($27)", (int16_t)(delta >> 16)); + } + } else { + int64_t dhigh; + + /* We must load the GOT entry from memory. */ + delta = (Elf_Addr)where - stubaddr; + /* + * If the GOT entry is too far away from the PLT entry, + * then punt. This PLT entry will have to be looked up + * manually for all GOT entries except the first one. + * The program will still run, albeit very slowly. It's + * extremely unlikely that this case could ever arise in + * practice, but we might as well handle it correctly if + * it does. + */ + if ((int32_t)delta != delta) { + dbg(" PLT stub too far from GOT to relocate"); + return; } - } else { /* Just relocate the GOT slots pointing into the PLT */ - for (rela = obj->pltrela; obj->pltrela != NULL && rela < relalim; - rela++) { - Elf_Addr *where = (Elf_Addr *) - (obj->relocbase + rela->r_offset); - *where += (Elf_Addr) obj->relocbase; + dhigh = delta - (int16_t)delta; + if (dhigh != 0) { + /* Build an LDAH instruction to adjust the high 16 bits. */ + inst[instct++] = 0x09 << 26 | 27 << 21 | 27 << 16 | + (dhigh >> 16 & 0xffff); + dbg(" LDAH $27,%d($27)", (int16_t)(dhigh >> 16)); } + /* Build an LDQ to load the GOT entry. */ + inst[instct++] = 0x29 << 26 | 27 << 21 | 27 << 16 | + (delta & 0xffff); + dbg(" LDQ $27,%d($27)", (int16_t)delta); } - return 0; + + /* + * Build a JMP or BR instruction to jump to the target. If + * the instruction displacement fits in a sign-extended 21-bit + * field, we can use the more efficient BR instruction. + * Otherwise we have to jump indirect through the pv register. + */ + pc = stubaddr + 4 * (instct + 1); + idisp = (int64_t)(target - pc) >> 2; + if (-0x100000 <= idisp && idisp < 0x100000) { + inst[instct++] = 0x30 << 26 | 31 << 21 | (idisp & 0x1fffff); + dbg(" BR $31,%p", (void *)target); + } else { + inst[instct++] = 0x1a << 26 | 31 << 21 | 27 << 16 | + (idisp & 0x3fff); + dbg(" JMP $31,($27),%d", (int)(idisp & 0x3fff)); + } + + /* + * Fill in the tail of the PLT entry first for reentrancy. + * Until we have overwritten the first instruction (an + * unconditional branch), the remaining instructions have no + * effect. + */ + stubptr = (u_int32_t *)stubaddr; + while (instct > 1) { + instct--; + stubptr[instct] = inst[instct]; + } + /* + * Commit the tail of the instruction sequence to memory + * before overwriting the first instruction. + */ + __asm__ __volatile__("wmb" : : : "memory"); + stubptr[0] = inst[0]; + } } /* Process an R_ALPHA_COPY relocation. */ |