alpha_reloc.c revision 1.43
138032Speter/* $NetBSD: alpha_reloc.c,v 1.43 2017/08/10 19:03:26 joerg Exp $ */ 2285303Sgshapiro 364562Sgshapiro/* 438032Speter * Copyright (c) 2001 Wasabi Systems, Inc. 538032Speter * All rights reserved. 638032Speter * 738032Speter * Written by Jason R. Thorpe for Wasabi Systems, Inc. 838032Speter * 938032Speter * Redistribution and use in source and binary forms, with or without 1038032Speter * modification, are permitted provided that the following conditions 1138032Speter * are met: 1238032Speter * 1. Redistributions of source code must retain the above copyright 1338032Speter * notice, this list of conditions and the following disclaimer. 1464562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 15147078Sgshapiro * notice, this list of conditions and the following disclaimer in the 1664562Sgshapiro * documentation and/or other materials provided with the distribution. 17266692Sgshapiro * 3. All advertising materials mentioning features or use of this software 1838032Speter * must display the following acknowledgement: 1990792Sgshapiro * This product includes software developed for the NetBSD Project by 2038032Speter * Wasabi Systems, Inc. 2190792Sgshapiro * 4. The name of Wasabi Systems, Inc. may not be used to endorse 2290792Sgshapiro * or promote products derived from this software without specific prior 2390792Sgshapiro * written permission. 24120256Sgshapiro * 2590792Sgshapiro * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26125820Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27125820Sgshapiro * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28125820Sgshapiro * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29125820Sgshapiro * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30125820Sgshapiro * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31125820Sgshapiro * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32120256Sgshapiro * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33125820Sgshapiro * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34125820Sgshapiro * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35125820Sgshapiro * POSSIBILITY OF SUCH DAMAGE. 36125820Sgshapiro */ 3790792Sgshapiro 3890792Sgshapiro/* 39120256Sgshapiro * Copyright 1996, 1997, 1998, 1999 John D. Polstra. 40120256Sgshapiro * All rights reserved. 41132943Sgshapiro * 42132943Sgshapiro * Redistribution and use in source and binary forms, with or without 43132943Sgshapiro * modification, are permitted provided that the following conditions 4490792Sgshapiro * are met: 4590792Sgshapiro * 1. Redistributions of source code must retain the above copyright 46132943Sgshapiro * notice, this list of conditions and the following disclaimer. 47132943Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 4890792Sgshapiro * notice, this list of conditions and the following disclaimer in the 4990792Sgshapiro * documentation and/or other materials provided with the distribution. 5064562Sgshapiro * 5190792Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 5290792Sgshapiro * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 5338032Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 5438032Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 5538032Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 5638032Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 5738032Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 5838032Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 5938032Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 6038032Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 6138032Speter */ 6238032Speter 6338032Speter#include <sys/cdefs.h> 6490792Sgshapiro#ifndef lint 6590792Sgshapiro__RCSID("$NetBSD: alpha_reloc.c,v 1.43 2017/08/10 19:03:26 joerg Exp $"); 6690792Sgshapiro#endif /* not lint */ 6790792Sgshapiro 6838032Speter#include <sys/types.h> 6938032Speter#include <sys/tls.h> 7038032Speter#include <string.h> 7138032Speter 7238032Speter#include "rtld.h" 7390792Sgshapiro#include "debug.h" 7490792Sgshapiro 75120256Sgshapiro#ifdef RTLD_DEBUG_ALPHA 7638032Speter#define adbg(x) xprintf x 77120256Sgshapiro#else 78120256Sgshapiro#define adbg(x) /* nothing */ 79120256Sgshapiro#endif 80120256Sgshapiro 81120256Sgshapirovoid _rtld_bind_start(void); 82120256Sgshapirovoid _rtld_bind_start_old(void); 83120256Sgshapirovoid _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); 84120256Sgshapirocaddr_t _rtld_bind(const Obj_Entry *, Elf_Addr); 85120256Sgshapirostatic inline int _rtld_relocate_plt_object(const Obj_Entry *, 86120256Sgshapiro const Elf_Rela *, Elf_Addr *); 87120256Sgshapiro 88120256Sgshapirovoid 89120256Sgshapiro_rtld_setup_pltgot(const Obj_Entry *obj) 90120256Sgshapiro{ 9190792Sgshapiro uint32_t word0; 9294334Sgshapiro 9394334Sgshapiro /* 9490792Sgshapiro * The PLTGOT on the Alpha looks like this: 9590792Sgshapiro * 96111823Sgshapiro * PLT HEADER 9790792Sgshapiro * . 9890792Sgshapiro * . 32 bytes 9990792Sgshapiro * . 10090792Sgshapiro * PLT ENTRY #0 10190792Sgshapiro * . 10290792Sgshapiro * . 12 bytes 10390792Sgshapiro * . 10490792Sgshapiro * PLT ENTRY #1 10590792Sgshapiro * . 10690792Sgshapiro * . 12 bytes 10790792Sgshapiro * . 10890792Sgshapiro * etc. 10990792Sgshapiro * 11090792Sgshapiro * The old-format entries look like (displacements filled in 11190792Sgshapiro * by the linker): 11290792Sgshapiro * 11390792Sgshapiro * ldah $28, 0($31) # 0x279f0000 11490792Sgshapiro * lda $28, 0($28) # 0x239c0000 11590792Sgshapiro * br $31, plt0 # 0xc3e00000 11690792Sgshapiro * 11790792Sgshapiro * The new-format entries look like: 11890792Sgshapiro * 11990792Sgshapiro * br $28, plt0 # 0xc3800000 12090792Sgshapiro * # 0x00000000 12190792Sgshapiro * # 0x00000000 12290792Sgshapiro * 12390792Sgshapiro * What we do is fetch the first PLT entry and check to 12490792Sgshapiro * see the first word of it matches the first word of the 12590792Sgshapiro * old format. If so, we use a binding routine that can 12690792Sgshapiro * handle the old format, otherwise we use a binding routine 12790792Sgshapiro * that handles the new format. 12890792Sgshapiro * 12990792Sgshapiro * Note that this is done on a per-object basis, we can mix 130203004Sgshapiro * and match shared objects build with both the old and new 13190792Sgshapiro * linker. 13290792Sgshapiro */ 13390792Sgshapiro word0 = *(uint32_t *)(((char *) obj->pltgot) + 32); 13490792Sgshapiro if ((word0 & 0xffff0000) == 0x279f0000) { 13590792Sgshapiro /* Old PLT entry format. */ 13694334Sgshapiro adbg(("ALPHA: object %p has old PLT format\n", obj)); 13764562Sgshapiro obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start_old; 13890792Sgshapiro obj->pltgot[3] = (Elf_Addr) obj; 13990792Sgshapiro } else { 14090792Sgshapiro /* New PLT entry format. */ 14190792Sgshapiro adbg(("ALPHA: object %p has new PLT format\n", obj)); 14290792Sgshapiro obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; 143168515Sgshapiro obj->pltgot[3] = (Elf_Addr) obj; 144168515Sgshapiro } 145168515Sgshapiro 146168515Sgshapiro __asm volatile("imb"); 147168515Sgshapiro} 148168515Sgshapiro 149168515Sgshapiro/* 15064562Sgshapiro * It is possible for the compiler to emit relocations for unaligned data. 15164562Sgshapiro * We handle this situation with these inlines. 15264562Sgshapiro */ 15364562Sgshapiro#define RELOC_ALIGNED_P(x) \ 15464562Sgshapiro (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) 155110560Sgshapiro 15690792Sgshapirostatic inline Elf_Addr 15790792Sgshapiroload_ptr(void *where) 15890792Sgshapiro{ 15990792Sgshapiro Elf_Addr res; 16090792Sgshapiro 16138032Speter memcpy(&res, where, sizeof(res)); 16290792Sgshapiro 16390792Sgshapiro return (res); 16490792Sgshapiro} 16590792Sgshapiro 16690792Sgshapirostatic inline void 16790792Sgshapirostore_ptr(void *where, Elf_Addr val) 16880785Sgshapiro{ 16990792Sgshapiro 17090792Sgshapiro memcpy(where, &val, sizeof(val)); 17190792Sgshapiro} 17290792Sgshapiro 17390792Sgshapirovoid 17490792Sgshapiro_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 17590792Sgshapiro{ 17690792Sgshapiro const Elf_Rela *rela = 0, *relalim; 17790792Sgshapiro Elf_Addr relasz = 0; 17890792Sgshapiro Elf_Addr *where; 17990792Sgshapiro 18090792Sgshapiro for (; dynp->d_tag != DT_NULL; dynp++) { 18190792Sgshapiro switch (dynp->d_tag) { 18290792Sgshapiro case DT_RELA: 18390792Sgshapiro rela = (const Elf_Rela *)(relocbase + dynp->d_un.d_ptr); 18490792Sgshapiro break; 185168515Sgshapiro case DT_RELASZ: 18690792Sgshapiro relasz = dynp->d_un.d_val; 18790792Sgshapiro break; 18890792Sgshapiro } 18990792Sgshapiro } 19090792Sgshapiro relalim = (const Elf_Rela *)((const uint8_t *)rela + relasz); 19190792Sgshapiro for (; rela < relalim; rela++) { 19290792Sgshapiro where = (Elf_Addr *)(relocbase + rela->r_offset); 19390792Sgshapiro /* XXX For some reason I see a few GLOB_DAT relocs here. */ 19490792Sgshapiro *where += (Elf_Addr)relocbase; 19590792Sgshapiro } 19690792Sgshapiro} 19790792Sgshapiro 19890792Sgshapiroint 199223067Sgshapiro_rtld_relocate_nonplt_objects(Obj_Entry *obj) 20090792Sgshapiro{ 20190792Sgshapiro const Elf_Rela *rela; 20290792Sgshapiro Elf_Addr target = -1; 20390792Sgshapiro const Elf_Sym *def = NULL; 20490792Sgshapiro const Obj_Entry *defobj = NULL; 20590792Sgshapiro unsigned long last_symnum = ULONG_MAX; 20690792Sgshapiro 20790792Sgshapiro for (rela = obj->rela; rela < obj->relalim; rela++) { 20890792Sgshapiro Elf_Addr *where; 20990792Sgshapiro Elf_Addr tmp; 21090792Sgshapiro unsigned long symnum; 21190792Sgshapiro 21290792Sgshapiro where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 21390792Sgshapiro 21490792Sgshapiro switch (ELF_R_TYPE(rela->r_info)) { 21590792Sgshapiro case R_TYPE(REFQUAD): 21690792Sgshapiro case R_TYPE(GLOB_DAT): 21790792Sgshapiro case R_TYPE(TPREL64): 21890792Sgshapiro case R_TYPE(DTPMOD64): 21990792Sgshapiro case R_TYPE(DTPREL64): 220110560Sgshapiro symnum = ELF_R_SYM(rela->r_info); 22190792Sgshapiro if (last_symnum != symnum) { 22290792Sgshapiro last_symnum = symnum; 22390792Sgshapiro def = _rtld_find_symdef(symnum, obj, &defobj, 22490792Sgshapiro false); 22590792Sgshapiro if (def == NULL) 22690792Sgshapiro return -1; 22790792Sgshapiro } 22890792Sgshapiro break; 22990792Sgshapiro 23090792Sgshapiro default: 23190792Sgshapiro break; 23290792Sgshapiro } 23390792Sgshapiro 23490792Sgshapiro switch (ELF_R_TYPE(rela->r_info)) { 23590792Sgshapiro case R_TYPE(NONE): 23690792Sgshapiro break; 23790792Sgshapiro 23890792Sgshapiro case R_TYPE(REFQUAD): 23990792Sgshapiro case R_TYPE(GLOB_DAT): 24090792Sgshapiro target = (Elf_Addr)(defobj->relocbase + 24190792Sgshapiro def->st_value); 242112810Sgshapiro 24390792Sgshapiro tmp = target + rela->r_addend; 24490792Sgshapiro if (__predict_true(RELOC_ALIGNED_P(where))) { 24590792Sgshapiro if (*where != tmp) 24690792Sgshapiro *where = tmp; 24790792Sgshapiro } else { 24890792Sgshapiro if (load_ptr(where) != tmp) 24990792Sgshapiro store_ptr(where, tmp); 25090792Sgshapiro } 25190792Sgshapiro rdbg(("REFQUAD/GLOB_DAT %s in %s --> %p in %s", 25290792Sgshapiro obj->strtab + obj->symtab[symnum].st_name, 25390792Sgshapiro obj->path, (void *)tmp, defobj->path)); 25490792Sgshapiro break; 25590792Sgshapiro 25690792Sgshapiro case R_TYPE(RELATIVE): 25790792Sgshapiro if (__predict_true(RELOC_ALIGNED_P(where))) 25890792Sgshapiro *where += (Elf_Addr)obj->relocbase; 25990792Sgshapiro else 26090792Sgshapiro store_ptr(where, 26190792Sgshapiro load_ptr(where) + (Elf_Addr)obj->relocbase); 26290792Sgshapiro rdbg(("RELATIVE in %s --> %p", obj->path, 26390792Sgshapiro (void *)*where)); 26490792Sgshapiro break; 26590792Sgshapiro 26690792Sgshapiro case R_TYPE(COPY): 26790792Sgshapiro /* 26890792Sgshapiro * These are deferred until all other relocations have 26990792Sgshapiro * been done. All we do here is make sure that the 27090792Sgshapiro * COPY relocation is not in a shared library. They 27190792Sgshapiro * are allowed only in executable files. 27290792Sgshapiro */ 27390792Sgshapiro if (obj->isdynamic) { 27490792Sgshapiro _rtld_error( 27590792Sgshapiro "%s: Unexpected R_COPY relocation in shared library", 27690792Sgshapiro obj->path); 27790792Sgshapiro return -1; 278110560Sgshapiro } 27990792Sgshapiro rdbg(("COPY (avoid in main)")); 28090792Sgshapiro break; 28190792Sgshapiro 28290792Sgshapiro case R_TYPE(TPREL64): 28390792Sgshapiro if (!defobj->tls_done && 28490792Sgshapiro _rtld_tls_offset_allocate(obj)) 28590792Sgshapiro return -1; 28690792Sgshapiro 28790792Sgshapiro tmp = (Elf64_Addr)(def->st_value + 28890792Sgshapiro sizeof(struct tls_tcb) + defobj->tlsoffset + 289110560Sgshapiro rela->r_addend); 29090792Sgshapiro 29180785Sgshapiro if (__predict_true(RELOC_ALIGNED_P(where))) 29280785Sgshapiro *where = tmp; 29380785Sgshapiro else 29480785Sgshapiro store_ptr(where, tmp); 29580785Sgshapiro 29680785Sgshapiro rdbg(("TPREL64 %s in %s --> %p", 29790792Sgshapiro obj->strtab + obj->symtab[symnum].st_name, 29880785Sgshapiro obj->path, (void *)*where)); 29980785Sgshapiro 300285303Sgshapiro break; 30180785Sgshapiro 30280785Sgshapiro case R_TYPE(DTPMOD64): 30380785Sgshapiro tmp = (Elf64_Addr)defobj->tlsindex; 30480785Sgshapiro if (__predict_true(RELOC_ALIGNED_P(where))) 30598841Sgshapiro *where = tmp; 30680785Sgshapiro else 30780785Sgshapiro store_ptr(where, tmp); 308132943Sgshapiro 30980785Sgshapiro rdbg(("DTPMOD64 %s in %s --> %p", 31090792Sgshapiro obj->strtab + obj->symtab[symnum].st_name, 31180785Sgshapiro obj->path, (void *)*where)); 31280785Sgshapiro 31380785Sgshapiro break; 31480785Sgshapiro 31590792Sgshapiro case R_TYPE(DTPREL64): 316285303Sgshapiro tmp = (Elf64_Addr)(def->st_value + rela->r_addend); 31780785Sgshapiro if (__predict_true(RELOC_ALIGNED_P(where))) 31890792Sgshapiro *where = tmp; 31980785Sgshapiro else 32080785Sgshapiro store_ptr(where, tmp); 32180785Sgshapiro 32280785Sgshapiro rdbg(("DTPREL64 %s in %s --> %p", 32390792Sgshapiro obj->strtab + obj->symtab[symnum].st_name, 32438032Speter obj->path, (void *)*where)); 32538032Speter 32638032Speter break; 32738032Speter 32890792Sgshapiro default: 32990792Sgshapiro rdbg(("sym = %lu, type = %lu, offset = %p, " 33038032Speter "addend = %p, contents = %p, symbol = %s", 33138032Speter (u_long)ELF_R_SYM(rela->r_info), 33238032Speter (u_long)ELF_R_TYPE(rela->r_info), 33338032Speter (void *)rela->r_offset, (void *)rela->r_addend, 33438032Speter (void *)load_ptr(where), 33590792Sgshapiro obj->strtab + obj->symtab[symnum].st_name)); 33638032Speter _rtld_error("%s: Unsupported relocation type %ld " 33738032Speter "in non-PLT relocations", 33838032Speter obj->path, (u_long) ELF_R_TYPE(rela->r_info)); 33938032Speter return -1; 34090792Sgshapiro } 34138032Speter } 34238032Speter return 0; 34390792Sgshapiro} 34438032Speter 34590792Sgshapiroint 34638032Speter_rtld_relocate_plt_lazy(Obj_Entry *obj) 34738032Speter{ 34864562Sgshapiro const Elf_Rela *rela; 34938032Speter 35038032Speter if (!obj->relocbase) 35138032Speter return 0; 35238032Speter 35338032Speter for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { 35490792Sgshapiro Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 35564562Sgshapiro 35690792Sgshapiro assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); 35738032Speter 35838032Speter /* Just relocate the GOT slots pointing into the PLT */ 35938032Speter *where += (Elf_Addr)obj->relocbase; 36038032Speter rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); 36138032Speter } 36238032Speter 363125820Sgshapiro return 0; 364125820Sgshapiro} 365125820Sgshapiro 366125820Sgshapirostatic inline int 367125820Sgshapiro_rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, 368125820Sgshapiro Elf_Addr *tp) 369125820Sgshapiro{ 370125820Sgshapiro Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 371125820Sgshapiro Elf_Addr new_value; 372125820Sgshapiro const Elf_Sym *def; 373125820Sgshapiro const Obj_Entry *defobj; 374125820Sgshapiro Elf_Addr stubaddr; 37538032Speter unsigned long info = rela->r_info; 376168515Sgshapiro 37738032Speter assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT)); 378120256Sgshapiro 379120256Sgshapiro def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); 380120256Sgshapiro if (__predict_false(def == NULL)) 381120256Sgshapiro return -1; 382120256Sgshapiro if (__predict_false(def == &_rtld_sym_zero)) 383120256Sgshapiro return 0; 384120256Sgshapiro 38538032Speter if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { 386168515Sgshapiro if (tp == NULL) 387125820Sgshapiro return 0; 388120256Sgshapiro new_value = _rtld_resolve_ifunc(defobj, def); 389125820Sgshapiro } else { 390120256Sgshapiro new_value = (Elf_Addr)(defobj->relocbase + def->st_value); 391125820Sgshapiro } 392120256Sgshapiro rdbg(("bind now/fixup in %s --> old=%p new=%p", 393132943Sgshapiro defobj->strtab + def->st_name, (void *)*where, (void *)new_value)); 394120256Sgshapiro 395120256Sgshapiro if ((stubaddr = *where) != new_value) { 396120256Sgshapiro int64_t delta, idisp; 397120256Sgshapiro uint32_t insn[3], *stubptr; 398120256Sgshapiro int insncnt; 399120256Sgshapiro Elf_Addr pc; 400285303Sgshapiro 401285303Sgshapiro /* Point this GOT entry at the target. */ 402120256Sgshapiro *where = new_value; 403120256Sgshapiro 404120256Sgshapiro /* 405147078Sgshapiro * Alpha shared objects may have multiple GOTs, each 406120256Sgshapiro * of which may point to this entry in the PLT. But, 407120256Sgshapiro * we only have a reference to the first GOT entry which 40890792Sgshapiro * points to this PLT entry. In order to avoid having to 40938032Speter * re-bind this call every time a non-first GOT entry is 41038032Speter * used, we will attempt to patch up the PLT entry to 41138032Speter * reference the target, rather than the binder. 41238032Speter * 41338032Speter * When the PLT stub gets control, PV contains the address 41464562Sgshapiro * of the PLT entry. Each PLT entry has room for 3 insns. 41538032Speter * If the displacement of the target from PV fits in a signed 416125820Sgshapiro * 32-bit integer, we can simply add it to PV. Otherwise, 41764562Sgshapiro * we must load the GOT entry itself into PV. 41864562Sgshapiro * 41964562Sgshapiro * Note if the shared object uses the old PLT format, then 42064562Sgshapiro * we cannot patch up the PLT safely, and so we skip it 42164562Sgshapiro * in that case[*]. 42264562Sgshapiro * 423285303Sgshapiro * [*] Actually, if we're not doing lazy-binding, then 424285303Sgshapiro * we *can* (and do) patch up this PLT entry; the PLTGOT 42590792Sgshapiro * thunk won't yet point to any binder entry point, and 42664562Sgshapiro * so this test will fail as it would for the new PLT 427125820Sgshapiro * entry format. 428125820Sgshapiro */ 429125820Sgshapiro if (obj->pltgot[2] == (Elf_Addr) &_rtld_bind_start_old) { 430125820Sgshapiro rdbg((" old PLT format")); 43138032Speter goto out; 43264562Sgshapiro } 43338032Speter 434125820Sgshapiro delta = new_value - stubaddr; 435125820Sgshapiro rdbg((" stubaddr=%p, where-stubaddr=%ld, delta=%ld", 436125820Sgshapiro (void *)stubaddr, (long)where - (long)stubaddr, 437125820Sgshapiro (long)delta)); 43864562Sgshapiro insncnt = 0; 43938032Speter if ((int32_t)delta == delta) { 440125820Sgshapiro /* 441125820Sgshapiro * We can adjust PV with an LDA, LDAH sequence. 442125820Sgshapiro * 44338032Speter * First, build an LDA insn to adjust the low 16 44464562Sgshapiro * bits. 44590792Sgshapiro */ 44664562Sgshapiro insn[insncnt++] = 0x08 << 26 | 27 << 21 | 27 << 16 | 44764562Sgshapiro (delta & 0xffff); 44864562Sgshapiro rdbg((" LDA $27,%d($27)", (int16_t)delta)); 44964562Sgshapiro /* 45064562Sgshapiro * Adjust the delta to account for the effects of 45138032Speter * the LDA, including sign-extension. 45238032Speter */ 45338032Speter delta -= (int16_t)delta; 45438032Speter if (delta != 0) { 45538032Speter /* 45664562Sgshapiro * Build an LDAH instruction to adjust the 45738032Speter * high 16 bits. 45838032Speter */ 45964562Sgshapiro insn[insncnt++] = 0x09 << 26 | 27 << 21 | 46038032Speter 27 << 16 | ((delta >> 16) & 0xffff); 46190792Sgshapiro rdbg((" LDAH $27,%d($27)", 462120256Sgshapiro (int16_t)(delta >> 16))); 46390792Sgshapiro } 46438032Speter } else { 46564562Sgshapiro int64_t dhigh; 46664562Sgshapiro 46790792Sgshapiro /* We must load the GOT entry. */ 46864562Sgshapiro delta = (Elf_Addr)where - stubaddr; 469285303Sgshapiro 470285303Sgshapiro /* 47138032Speter * If the GOT entry is too far away from the PLT 47238032Speter * entry, then we can't patch up the PLT entry. 47338032Speter * This PLT entry will have to be bound for each 47438032Speter * GOT entry except for the first one. This program 47590792Sgshapiro * will still run, albeit very slowly. It is very 47690792Sgshapiro * unlikely that this case will ever happen in 47790792Sgshapiro * practice. 47890792Sgshapiro */ 47938032Speter if ((int32_t)delta != delta) { 48038032Speter rdbg((" PLT stub too far from GOT to relocate")); 48190792Sgshapiro goto out; 48238032Speter } 48338032Speter dhigh = delta - (int16_t)delta; 48438032Speter if (dhigh != 0) { 48538032Speter /* 48690792Sgshapiro * Build an LDAH instruction to adjust the 487132943Sgshapiro * high 16 bits. 48838032Speter */ 48938032Speter insn[insncnt++] = 0x09 << 26 | 27 << 21 | 49038032Speter 27 << 16 | ((dhigh >> 16) & 0xffff); 49190792Sgshapiro rdbg((" LDAH $27,%d($27)", 49290792Sgshapiro (int16_t)(dhigh >> 16))); 49390792Sgshapiro } 49438032Speter /* Build an LDQ to load the GOT entry. */ 49590792Sgshapiro insn[insncnt++] = 0x29 << 26 | 27 << 21 | 49638032Speter 27 << 16 | (delta & 0xffff); 49790792Sgshapiro rdbg((" LDQ $27,%d($27)", 49890792Sgshapiro (int16_t)delta)); 49938032Speter } 50038032Speter 50138032Speter /* 50238032Speter * Now, build a JMP or BR insn to jump to the target. If 50338032Speter * the displacement fits in a sign-extended 21-bit field, 50438032Speter * we can use the more efficient BR insn. Otherwise, we 505168515Sgshapiro * have to jump indirect through PV. 50664562Sgshapiro */ 50738032Speter pc = stubaddr + (4 * (insncnt + 1)); 50890792Sgshapiro idisp = (int64_t)(new_value - pc) >> 2; 50990792Sgshapiro if (-0x100000 <= idisp && idisp < 0x100000) { 510132943Sgshapiro insn[insncnt++] = 0x30 << 26 | 31 << 21 | 51190792Sgshapiro (idisp & 0x1fffff); 51290792Sgshapiro rdbg((" BR $31,%p", (void *)new_value)); 51390792Sgshapiro } else { 514285303Sgshapiro insn[insncnt++] = 0x1a << 26 | 31 << 21 | 515285303Sgshapiro 27 << 16 | (idisp & 0x3fff); 51690792Sgshapiro rdbg((" JMP $31,($27),%d", 51790792Sgshapiro (int)(idisp & 0x3fff))); 51890792Sgshapiro } 51990792Sgshapiro 52090792Sgshapiro /* 52190792Sgshapiro * Fill in the tail of the PLT entry first, for reentrancy. 52290792Sgshapiro * Until we have overwritten the first insn (an unconditional 52390792Sgshapiro * branch), the remaining insns have no effect. 52490792Sgshapiro */ 52590792Sgshapiro stubptr = (uint32_t *)stubaddr; 52690792Sgshapiro while (insncnt > 1) { 52790792Sgshapiro insncnt--; 52890792Sgshapiro stubptr[insncnt] = insn[insncnt]; 52990792Sgshapiro } 53090792Sgshapiro /* 53190792Sgshapiro * Commit the tail of the insn sequence to memory 53290792Sgshapiro * before overwriting the first insn. 53390792Sgshapiro */ 53490792Sgshapiro __asm volatile("wmb" ::: "memory"); 53564562Sgshapiro stubptr[0] = insn[0]; 53664562Sgshapiro /* 53764562Sgshapiro * I-stream will be sync'd when we either return from 53864562Sgshapiro * the binder (lazy bind case) or when the PLTGOT thunk 53990792Sgshapiro * is patched up (bind-now case). 54090792Sgshapiro */ 54138032Speter } 54238032Speterout: 54390792Sgshapiro if (tp) 54490792Sgshapiro *tp = new_value; 54564562Sgshapiro 54664562Sgshapiro return 0; 54790792Sgshapiro} 54890792Sgshapiro 549120256Sgshapirocaddr_t 550120256Sgshapiro_rtld_bind(const Obj_Entry *obj, Elf_Addr reloff) 55190792Sgshapiro{ 55290792Sgshapiro const Elf_Rela *rela = 55390792Sgshapiro (const Elf_Rela *)((const uint8_t *)obj->pltrela + reloff); 554120256Sgshapiro Elf_Addr result = 0; /* XXX gcc */ 55590792Sgshapiro int err; 556285303Sgshapiro 557285303Sgshapiro _rtld_shared_enter(); 55864562Sgshapiro err = _rtld_relocate_plt_object(obj, rela, &result); 55938032Speter if (err) 56038032Speter _rtld_die(); 56138032Speter _rtld_shared_exit(); 56238032Speter 56390792Sgshapiro return (caddr_t)result; 56438032Speter} 56538032Speter 566168515Sgshapiroint 56738032Speter_rtld_relocate_plt_objects(const Obj_Entry *obj) 56838032Speter{ 56938032Speter const Elf_Rela *rela; 57090792Sgshapiro 57190792Sgshapiro for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) 572132943Sgshapiro if (_rtld_relocate_plt_object(obj, rela, NULL) < 0) 57390792Sgshapiro return -1; 57490792Sgshapiro 57590792Sgshapiro return 0; 57690792Sgshapiro} 57790792Sgshapiro