1107572Sgrehan/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */ 2107572Sgrehan 3107572Sgrehan/*- 4107572Sgrehan * Copyright (C) 1998 Tsubai Masanari 5107572Sgrehan * All rights reserved. 6107572Sgrehan * 7107572Sgrehan * Redistribution and use in source and binary forms, with or without 8107572Sgrehan * modification, are permitted provided that the following conditions 9107572Sgrehan * are met: 10107572Sgrehan * 1. Redistributions of source code must retain the above copyright 11107572Sgrehan * notice, this list of conditions and the following disclaimer. 12107572Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 13107572Sgrehan * notice, this list of conditions and the following disclaimer in the 14107572Sgrehan * documentation and/or other materials provided with the distribution. 15107572Sgrehan * 3. The name of the author may not be used to endorse or promote products 16107572Sgrehan * derived from this software without specific prior written permission. 17107572Sgrehan * 18107572Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19107572Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20107572Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21107572Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22107572Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23107572Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24107572Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25107572Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26107572Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27107572Sgrehan * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28107572Sgrehan * 29107572Sgrehan * $FreeBSD$ 30107572Sgrehan */ 31107572Sgrehan 32107572Sgrehan#include <sys/param.h> 33107572Sgrehan#include <sys/mman.h> 34107572Sgrehan 35107572Sgrehan#include <errno.h> 36107572Sgrehan#include <stdio.h> 37107572Sgrehan#include <stdlib.h> 38107572Sgrehan#include <string.h> 39107572Sgrehan#include <unistd.h> 40253750Savg#include <machine/cpu.h> 41234592Snwhitehorn#include <machine/atomic.h> 42173745Sjb#include <machine/md_var.h> 43107572Sgrehan 44107572Sgrehan#include "debug.h" 45107572Sgrehan#include "rtld.h" 46107572Sgrehan 47107572Sgrehan#define _ppc_ha(x) ((((u_int32_t)(x) & 0x8000) ? \ 48107572Sgrehan ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16) 49107572Sgrehan#define _ppc_la(x) ((u_int32_t)(x) & 0xffff) 50107572Sgrehan 51204211Snwhitehorn#define min(a,b) (((a) < (b)) ? (a) : (b)) 52204211Snwhitehorn#define max(a,b) (((a) > (b)) ? (a) : (b)) 53204211Snwhitehorn 54204211Snwhitehorn#define PLT_EXTENDED_BEGIN (1 << 13) 55204211Snwhitehorn#define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \ 56204211Snwhitehorn (N - PLT_EXTENDED_BEGIN)*2 : 0)) 57204211Snwhitehorn 58107572Sgrehan/* 59107572Sgrehan * Process the R_PPC_COPY relocations 60107572Sgrehan */ 61107572Sgrehanint 62107572Sgrehando_copy_relocations(Obj_Entry *dstobj) 63107572Sgrehan{ 64107572Sgrehan const Elf_Rela *relalim; 65107572Sgrehan const Elf_Rela *rela; 66107572Sgrehan 67115396Skan /* 68107572Sgrehan * COPY relocs are invalid outside of the main program 69107572Sgrehan */ 70115396Skan assert(dstobj->mainprog); 71107572Sgrehan 72115396Skan relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + 73107572Sgrehan dstobj->relasize); 74107572Sgrehan for (rela = dstobj->rela; rela < relalim; rela++) { 75107572Sgrehan void *dstaddr; 76107572Sgrehan const Elf_Sym *dstsym; 77107572Sgrehan const char *name; 78107572Sgrehan size_t size; 79107572Sgrehan const void *srcaddr; 80107572Sgrehan const Elf_Sym *srcsym = NULL; 81216695Skib const Obj_Entry *srcobj, *defobj; 82216695Skib SymLook req; 83216695Skib int res; 84107572Sgrehan 85107572Sgrehan if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) { 86107572Sgrehan continue; 87107572Sgrehan } 88107572Sgrehan 89107572Sgrehan dstaddr = (void *) (dstobj->relocbase + rela->r_offset); 90107572Sgrehan dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); 91107572Sgrehan name = dstobj->strtab + dstsym->st_name; 92107572Sgrehan size = dstsym->st_size; 93216695Skib symlook_init(&req, name); 94216695Skib req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); 95233231Skib req.flags = SYMLOOK_EARLY; 96115396Skan 97115396Skan for (srcobj = dstobj->next; srcobj != NULL; 98107572Sgrehan srcobj = srcobj->next) { 99216695Skib res = symlook_obj(&req, srcobj); 100216695Skib if (res == 0) { 101216695Skib srcsym = req.sym_out; 102216695Skib defobj = req.defobj_out; 103107572Sgrehan break; 104107572Sgrehan } 105107572Sgrehan } 106107572Sgrehan 107107572Sgrehan if (srcobj == NULL) { 108107572Sgrehan _rtld_error("Undefined symbol \"%s\" " 109107572Sgrehan " referenced from COPY" 110107572Sgrehan " relocation in %s", name, dstobj->path); 111107572Sgrehan return (-1); 112107572Sgrehan } 113115396Skan 114216695Skib srcaddr = (const void *) (defobj->relocbase+srcsym->st_value); 115115396Skan memcpy(dstaddr, srcaddr, size); 116107572Sgrehan dbg("copy_reloc: src=%p,dst=%p,size=%d\n",srcaddr,dstaddr,size); 117107572Sgrehan } 118115396Skan 119107572Sgrehan return (0); 120107572Sgrehan} 121107572Sgrehan 122107572Sgrehan 123107572Sgrehan/* 124107572Sgrehan * Perform early relocation of the run-time linker image 125107572Sgrehan */ 126107572Sgrehanvoid 127107572Sgrehanreloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 128107572Sgrehan{ 129107572Sgrehan const Elf_Rela *rela = 0, *relalim; 130107572Sgrehan Elf_Addr relasz = 0; 131107572Sgrehan Elf_Addr *where; 132107572Sgrehan 133107572Sgrehan /* 134107572Sgrehan * Extract the rela/relasz values from the dynamic section 135107572Sgrehan */ 136107572Sgrehan for (; dynp->d_tag != DT_NULL; dynp++) { 137107572Sgrehan switch (dynp->d_tag) { 138107572Sgrehan case DT_RELA: 139107572Sgrehan rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr); 140107572Sgrehan break; 141107572Sgrehan case DT_RELASZ: 142107572Sgrehan relasz = dynp->d_un.d_val; 143107572Sgrehan break; 144107572Sgrehan } 145107572Sgrehan } 146107572Sgrehan 147107572Sgrehan /* 148115396Skan * Relocate these values 149107572Sgrehan */ 150107572Sgrehan relalim = (const Elf_Rela *)((caddr_t)rela + relasz); 151107572Sgrehan for (; rela < relalim; rela++) { 152107572Sgrehan where = (Elf_Addr *)(relocbase + rela->r_offset); 153107572Sgrehan *where = (Elf_Addr)(relocbase + rela->r_addend); 154107572Sgrehan } 155107572Sgrehan} 156107572Sgrehan 157107572Sgrehan 158107572Sgrehan/* 159115396Skan * Relocate a non-PLT object with addend. 160107572Sgrehan */ 161107572Sgrehanstatic int 162107572Sgrehanreloc_nonplt_object(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela, 163233231Skib SymCache *cache, int flags, RtldLockState *lockstate) 164107572Sgrehan{ 165107572Sgrehan Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 166107572Sgrehan const Elf_Sym *def; 167107572Sgrehan const Obj_Entry *defobj; 168107572Sgrehan Elf_Addr tmp; 169107572Sgrehan 170107572Sgrehan switch (ELF_R_TYPE(rela->r_info)) { 171115396Skan 172107572Sgrehan case R_PPC_NONE: 173107572Sgrehan break; 174107572Sgrehan 175107572Sgrehan case R_PPC_ADDR32: /* word32 S + A */ 176107572Sgrehan case R_PPC_GLOB_DAT: /* word32 S + A */ 177107572Sgrehan def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 178233231Skib flags, cache, lockstate); 179107572Sgrehan if (def == NULL) { 180107572Sgrehan return (-1); 181107572Sgrehan } 182107572Sgrehan 183107572Sgrehan tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 184107572Sgrehan rela->r_addend); 185107572Sgrehan 186107572Sgrehan /* Don't issue write if unnecessary; avoid COW page fault */ 187107572Sgrehan if (*where != tmp) { 188107572Sgrehan *where = tmp; 189107572Sgrehan } 190107572Sgrehan break; 191107572Sgrehan 192107572Sgrehan case R_PPC_RELATIVE: /* word32 B + A */ 193107572Sgrehan tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); 194115396Skan 195107572Sgrehan /* As above, don't issue write unnecessarily */ 196107572Sgrehan if (*where != tmp) { 197107572Sgrehan *where = tmp; 198107572Sgrehan } 199107572Sgrehan break; 200107572Sgrehan 201107572Sgrehan case R_PPC_COPY: 202107572Sgrehan /* 203107572Sgrehan * These are deferred until all other relocations 204107572Sgrehan * have been done. All we do here is make sure 205107572Sgrehan * that the COPY relocation is not in a shared 206107572Sgrehan * library. They are allowed only in executable 207107572Sgrehan * files. 208107572Sgrehan */ 209107572Sgrehan if (!obj->mainprog) { 210107572Sgrehan _rtld_error("%s: Unexpected R_COPY " 211107572Sgrehan " relocation in shared library", 212107572Sgrehan obj->path); 213107572Sgrehan return (-1); 214115396Skan } 215107572Sgrehan break; 216107572Sgrehan 217107572Sgrehan case R_PPC_JMP_SLOT: 218107572Sgrehan /* 219107572Sgrehan * These will be handled by the plt/jmpslot routines 220107572Sgrehan */ 221107572Sgrehan break; 222107572Sgrehan 223137122Sssouhlal case R_PPC_DTPMOD32: 224137122Sssouhlal def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 225233231Skib flags, cache, lockstate); 226137122Sssouhlal 227137122Sssouhlal if (def == NULL) 228137122Sssouhlal return (-1); 229137122Sssouhlal 230137122Sssouhlal *where = (Elf_Addr) defobj->tlsindex; 231137122Sssouhlal 232137122Sssouhlal break; 233137122Sssouhlal 234137122Sssouhlal case R_PPC_TPREL32: 235137122Sssouhlal def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 236233231Skib flags, cache, lockstate); 237137122Sssouhlal 238137122Sssouhlal if (def == NULL) 239137122Sssouhlal return (-1); 240137122Sssouhlal 241137122Sssouhlal /* 242137122Sssouhlal * We lazily allocate offsets for static TLS as we 243137122Sssouhlal * see the first relocation that references the 244137122Sssouhlal * TLS block. This allows us to support (small 245137122Sssouhlal * amounts of) static TLS in dynamically loaded 246137122Sssouhlal * modules. If we run out of space, we generate an 247137122Sssouhlal * error. 248137122Sssouhlal */ 249137122Sssouhlal if (!defobj->tls_done) { 250137122Sssouhlal if (!allocate_tls_offset((Obj_Entry*) defobj)) { 251137122Sssouhlal _rtld_error("%s: No space available for static " 252137122Sssouhlal "Thread Local Storage", obj->path); 253137122Sssouhlal return (-1); 254137122Sssouhlal } 255137122Sssouhlal } 256137122Sssouhlal 257137122Sssouhlal *(Elf_Addr **)where = *where * sizeof(Elf_Addr) 258137122Sssouhlal + (Elf_Addr *)(def->st_value + rela->r_addend 259161799Smarcel + defobj->tlsoffset - TLS_TP_OFFSET); 260137122Sssouhlal 261137122Sssouhlal break; 262137122Sssouhlal 263137122Sssouhlal case R_PPC_DTPREL32: 264137122Sssouhlal def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 265233231Skib flags, cache, lockstate); 266137122Sssouhlal 267137122Sssouhlal if (def == NULL) 268137122Sssouhlal return (-1); 269137122Sssouhlal 270137122Sssouhlal *where += (Elf_Addr)(def->st_value + rela->r_addend 271137122Sssouhlal - TLS_DTV_OFFSET); 272137122Sssouhlal 273137122Sssouhlal break; 274137122Sssouhlal 275107572Sgrehan default: 276107572Sgrehan _rtld_error("%s: Unsupported relocation type %d" 277107572Sgrehan " in non-PLT relocations\n", obj->path, 278115396Skan ELF_R_TYPE(rela->r_info)); 279107572Sgrehan return (-1); 280107572Sgrehan } 281115396Skan return (0); 282107572Sgrehan} 283107572Sgrehan 284107572Sgrehan 285107572Sgrehan/* 286107572Sgrehan * Process non-PLT relocations 287107572Sgrehan */ 288107572Sgrehanint 289233231Skibreloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, 290233231Skib RtldLockState *lockstate) 291107572Sgrehan{ 292107572Sgrehan const Elf_Rela *relalim; 293107572Sgrehan const Elf_Rela *rela; 294107572Sgrehan SymCache *cache; 295107572Sgrehan int r = -1; 296107572Sgrehan 297107572Sgrehan /* 298107572Sgrehan * The dynamic loader may be called from a thread, we have 299107572Sgrehan * limited amounts of stack available so we cannot use alloca(). 300107572Sgrehan */ 301171462Smarcel if (obj != obj_rtld) { 302234841Skib cache = calloc(obj->dynsymcount, sizeof(SymCache)); 303208256Srdivacky /* No need to check for NULL here */ 304171462Smarcel } else 305107572Sgrehan cache = NULL; 306107572Sgrehan 307107572Sgrehan /* 308107572Sgrehan * From the SVR4 PPC ABI: 309115396Skan * "The PowerPC family uses only the Elf32_Rela relocation 310107572Sgrehan * entries with explicit addends." 311107572Sgrehan */ 312107572Sgrehan relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); 313107572Sgrehan for (rela = obj->rela; rela < relalim; rela++) { 314233231Skib if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags, 315233231Skib lockstate) < 0) 316107572Sgrehan goto done; 317107572Sgrehan } 318107572Sgrehan r = 0; 319107572Sgrehandone: 320208256Srdivacky if (cache != NULL) 321208256Srdivacky free(cache); 322228646Snwhitehorn 323228646Snwhitehorn /* Synchronize icache for text seg in case we made any changes */ 324228646Snwhitehorn __syncicache(obj->mapbase, obj->textsize); 325228646Snwhitehorn 326107572Sgrehan return (r); 327107572Sgrehan} 328107572Sgrehan 329107572Sgrehan/* 330107572Sgrehan * Initialise a PLT slot to the resolving trampoline 331107572Sgrehan */ 332107572Sgrehanstatic int 333107572Sgrehanreloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) 334107572Sgrehan{ 335107572Sgrehan Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 336204211Snwhitehorn Elf_Addr *pltresolve, *pltlongresolve, *jmptab; 337107572Sgrehan Elf_Addr distance; 338204211Snwhitehorn int N = obj->pltrelasize / sizeof(Elf_Rela); 339107572Sgrehan int reloff; 340107572Sgrehan 341107572Sgrehan reloff = rela - obj->pltrela; 342107572Sgrehan 343204211Snwhitehorn if (reloff < 0) 344107572Sgrehan return (-1); 345107572Sgrehan 346204211Snwhitehorn pltlongresolve = obj->pltgot + 5; 347204211Snwhitehorn pltresolve = pltlongresolve + 5; 348107572Sgrehan 349107572Sgrehan distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1); 350107572Sgrehan 351115396Skan dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x", 352107572Sgrehan (void *)where, (void *)pltresolve, reloff, distance); 353107572Sgrehan 354204211Snwhitehorn if (reloff < PLT_EXTENDED_BEGIN) { 355204211Snwhitehorn /* li r11,reloff */ 356204211Snwhitehorn /* b pltresolve */ 357204211Snwhitehorn where[0] = 0x39600000 | reloff; 358204211Snwhitehorn where[1] = 0x48000000 | (distance & 0x03fffffc); 359204211Snwhitehorn } else { 360204211Snwhitehorn jmptab = obj->pltgot + JMPTAB_BASE(N); 361204211Snwhitehorn jmptab[reloff] = (u_int)pltlongresolve; 362107572Sgrehan 363204211Snwhitehorn /* lis r11,jmptab[reloff]@ha */ 364204211Snwhitehorn /* lwzu r12,jmptab[reloff]@l(r11) */ 365204211Snwhitehorn /* mtctr r12 */ 366204211Snwhitehorn /* bctr */ 367204211Snwhitehorn where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]); 368204211Snwhitehorn where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]); 369204211Snwhitehorn where[2] = 0x7d8903a6; 370204211Snwhitehorn where[3] = 0x4e800420; 371204211Snwhitehorn } 372204211Snwhitehorn 373204211Snwhitehorn 374107572Sgrehan /* 375228635Snwhitehorn * The icache will be sync'd in reloc_plt, which is called 376107572Sgrehan * after all the slots have been updated 377107572Sgrehan */ 378107572Sgrehan 379107572Sgrehan return (0); 380107572Sgrehan} 381107572Sgrehan 382107572Sgrehan 383107572Sgrehan/* 384107572Sgrehan * Process the PLT relocations. 385107572Sgrehan */ 386107572Sgrehanint 387107572Sgrehanreloc_plt(Obj_Entry *obj) 388107572Sgrehan{ 389107572Sgrehan const Elf_Rela *relalim; 390107572Sgrehan const Elf_Rela *rela; 391228635Snwhitehorn int N = obj->pltrelasize / sizeof(Elf_Rela); 392107572Sgrehan 393107572Sgrehan if (obj->pltrelasize != 0) { 394107572Sgrehan 395115396Skan relalim = (const Elf_Rela *)((char *)obj->pltrela + 396107572Sgrehan obj->pltrelasize); 397107572Sgrehan for (rela = obj->pltrela; rela < relalim; rela++) { 398107572Sgrehan assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); 399107572Sgrehan 400107572Sgrehan if (reloc_plt_object(obj, rela) < 0) { 401107572Sgrehan return (-1); 402107572Sgrehan } 403107572Sgrehan } 404107572Sgrehan } 405107572Sgrehan 406228635Snwhitehorn /* 407228635Snwhitehorn * Sync the icache for the byte range represented by the 408228635Snwhitehorn * trampoline routines and call slots. 409228635Snwhitehorn */ 410228635Snwhitehorn if (obj->pltgot != NULL) 411228635Snwhitehorn __syncicache(obj->pltgot, JMPTAB_BASE(N)*4); 412228635Snwhitehorn 413107572Sgrehan return (0); 414107572Sgrehan} 415107572Sgrehan 416107572Sgrehan 417107572Sgrehan/* 418107572Sgrehan * LD_BIND_NOW was set - force relocation for all jump slots 419107572Sgrehan */ 420107572Sgrehanint 421233231Skibreloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) 422107572Sgrehan{ 423107572Sgrehan const Obj_Entry *defobj; 424107572Sgrehan const Elf_Rela *relalim; 425107572Sgrehan const Elf_Rela *rela; 426107572Sgrehan const Elf_Sym *def; 427107572Sgrehan Elf_Addr *where; 428107572Sgrehan Elf_Addr target; 429107572Sgrehan 430107572Sgrehan relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); 431107572Sgrehan for (rela = obj->pltrela; rela < relalim; rela++) { 432107572Sgrehan assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); 433107572Sgrehan where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 434107572Sgrehan def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 435233231Skib SYMLOOK_IN_PLT | flags, NULL, lockstate); 436107572Sgrehan if (def == NULL) { 437107572Sgrehan dbg("reloc_jmpslots: sym not found"); 438107572Sgrehan return (-1); 439107572Sgrehan } 440107572Sgrehan 441107572Sgrehan target = (Elf_Addr)(defobj->relocbase + def->st_value); 442107572Sgrehan 443107572Sgrehan#if 0 444107572Sgrehan /* PG XXX */ 445107572Sgrehan dbg("\"%s\" in \"%s\" --> %p in \"%s\"", 446107572Sgrehan defobj->strtab + def->st_name, basename(obj->path), 447107572Sgrehan (void *)target, basename(defobj->path)); 448107572Sgrehan#endif 449107572Sgrehan 450115396Skan reloc_jmpslot(where, target, defobj, obj, 451107572Sgrehan (const Elf_Rel *) rela); 452107572Sgrehan } 453107572Sgrehan 454107572Sgrehan obj->jmpslots_done = true; 455107572Sgrehan 456107572Sgrehan return (0); 457107572Sgrehan} 458107572Sgrehan 459107572Sgrehan 460107572Sgrehan/* 461107572Sgrehan * Update the value of a PLT jump slot. Branch directly to the target if 462107572Sgrehan * it is within +/- 32Mb, otherwise go indirectly via the pltcall 463107572Sgrehan * trampoline call and jump table. 464107572Sgrehan */ 465107572SgrehanElf_Addr 466107572Sgrehanreloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, 467107572Sgrehan const Obj_Entry *obj, const Elf_Rel *rel) 468107572Sgrehan{ 469107572Sgrehan Elf_Addr offset; 470107572Sgrehan const Elf_Rela *rela = (const Elf_Rela *) rel; 471107572Sgrehan 472115396Skan dbg(" reloc_jmpslot: where=%p, target=%p", 473107572Sgrehan (void *)wherep, (void *)target); 474107572Sgrehan 475107572Sgrehan /* 476107572Sgrehan * At the PLT entry pointed at by `wherep', construct 477107572Sgrehan * a direct transfer to the now fully resolved function 478107572Sgrehan * address. 479107572Sgrehan */ 480107572Sgrehan offset = target - (Elf_Addr)wherep; 481107572Sgrehan 482107572Sgrehan if (abs(offset) < 32*1024*1024) { /* inside 32MB? */ 483107572Sgrehan /* b value # branch directly */ 484107572Sgrehan *wherep = 0x48000000 | (offset & 0x03fffffc); 485107572Sgrehan __syncicache(wherep, 4); 486107572Sgrehan } else { 487107572Sgrehan Elf_Addr *pltcall, *jmptab; 488107572Sgrehan int distance; 489107572Sgrehan int N = obj->pltrelasize / sizeof(Elf_Rela); 490107572Sgrehan int reloff = rela - obj->pltrela; 491107572Sgrehan 492204211Snwhitehorn if (reloff < 0) 493107572Sgrehan return (-1); 494107572Sgrehan 495107572Sgrehan pltcall = obj->pltgot; 496107572Sgrehan 497204211Snwhitehorn dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n", 498107572Sgrehan reloff, N); 499107572Sgrehan 500204211Snwhitehorn jmptab = obj->pltgot + JMPTAB_BASE(N); 501107572Sgrehan jmptab[reloff] = target; 502234592Snwhitehorn mb(); /* Order jmptab update before next changes */ 503107572Sgrehan 504204211Snwhitehorn if (reloff < PLT_EXTENDED_BEGIN) { 505204211Snwhitehorn /* for extended PLT entries, we keep the old code */ 506107572Sgrehan 507204211Snwhitehorn distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1); 508204211Snwhitehorn 509204211Snwhitehorn /* li r11,reloff */ 510204211Snwhitehorn /* b pltcall # use indirect pltcall routine */ 511213406Snwhitehorn 512213406Snwhitehorn /* first instruction same as before */ 513204211Snwhitehorn wherep[1] = 0x48000000 | (distance & 0x03fffffc); 514204211Snwhitehorn __syncicache(wherep, 8); 515204211Snwhitehorn } 516107572Sgrehan } 517107572Sgrehan 518107572Sgrehan return (target); 519107572Sgrehan} 520107572Sgrehan 521228435Skibint 522228435Skibreloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) 523228435Skib{ 524107572Sgrehan 525228435Skib /* XXX not implemented */ 526228435Skib return (0); 527228435Skib} 528228435Skib 529228435Skibint 530233231Skibreloc_gnu_ifunc(Obj_Entry *obj, int flags, 531233231Skib struct Struct_RtldLockState *lockstate) 532228435Skib{ 533228435Skib 534228435Skib /* XXX not implemented */ 535228435Skib return (0); 536228435Skib} 537228435Skib 538107572Sgrehan/* 539107572Sgrehan * Setup the plt glue routines. 540107572Sgrehan */ 541204211Snwhitehorn#define PLTCALL_SIZE 20 542204211Snwhitehorn#define PLTLONGRESOLVE_SIZE 20 543204211Snwhitehorn#define PLTRESOLVE_SIZE 24 544107572Sgrehan 545107572Sgrehanvoid 546107572Sgrehaninit_pltgot(Obj_Entry *obj) 547107572Sgrehan{ 548204211Snwhitehorn Elf_Word *pltcall, *pltresolve, *pltlongresolve; 549107572Sgrehan Elf_Word *jmptab; 550107572Sgrehan int N = obj->pltrelasize / sizeof(Elf_Rela); 551107572Sgrehan 552107572Sgrehan pltcall = obj->pltgot; 553107572Sgrehan 554107572Sgrehan if (pltcall == NULL) { 555107572Sgrehan return; 556107572Sgrehan } 557107572Sgrehan 558107572Sgrehan /* 559107572Sgrehan * From the SVR4 PPC ABI: 560107572Sgrehan * 561107572Sgrehan * 'The first 18 words (72 bytes) of the PLT are reserved for 562107572Sgrehan * use by the dynamic linker. 563107572Sgrehan * ... 564115396Skan * 'If the executable or shared object requires N procedure 565115396Skan * linkage table entries, the link editor shall reserve 3*N 566115396Skan * words (12*N bytes) following the 18 reserved words. The 567115396Skan * first 2*N of these words are the procedure linkage table 568115396Skan * entries themselves. The static linker directs calls to bytes 569115396Skan * (72 + (i-1)*8), for i between 1 and N inclusive. The remaining 570107572Sgrehan * N words (4*N bytes) are reserved for use by the dynamic linker.' 571107572Sgrehan */ 572107572Sgrehan 573107572Sgrehan /* 574107572Sgrehan * Copy the absolute-call assembler stub into the first part of 575107572Sgrehan * the reserved PLT area. 576107572Sgrehan */ 577107572Sgrehan memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE); 578107572Sgrehan 579107572Sgrehan /* 580107572Sgrehan * Determine the address of the jumptable, which is the dyn-linker 581107572Sgrehan * reserved area after the call cells. Write the absolute address 582107572Sgrehan * of the jumptable into the absolute-call assembler code so it 583107572Sgrehan * can determine this address. 584107572Sgrehan */ 585204211Snwhitehorn jmptab = obj->pltgot + JMPTAB_BASE(N); 586107572Sgrehan pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */ 587107572Sgrehan pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */ 588107572Sgrehan 589107572Sgrehan /* 590204211Snwhitehorn * Skip down 20 bytes into the initial reserved area and copy 591107572Sgrehan * in the standard resolving assembler call. Into this assembler, 592107572Sgrehan * insert the absolute address of the _rtld_bind_start routine 593107572Sgrehan * and the address of the relocation object. 594204211Snwhitehorn * 595204211Snwhitehorn * We place pltlongresolve first, so it can fix up its arguments 596204211Snwhitehorn * and then fall through to the regular PLT resolver. 597107572Sgrehan */ 598204211Snwhitehorn pltlongresolve = obj->pltgot + 5; 599107572Sgrehan 600204211Snwhitehorn memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve, 601204211Snwhitehorn PLTLONGRESOLVE_SIZE); 602204211Snwhitehorn pltlongresolve[0] |= _ppc_ha(jmptab); /* lis 12,jmptab@ha */ 603204211Snwhitehorn pltlongresolve[1] |= _ppc_la(jmptab); /* addi 12,12,jmptab@l */ 604204211Snwhitehorn 605204211Snwhitehorn pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t); 606107572Sgrehan memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE); 607107572Sgrehan pltresolve[0] |= _ppc_ha(_rtld_bind_start); 608107572Sgrehan pltresolve[1] |= _ppc_la(_rtld_bind_start); 609107572Sgrehan pltresolve[3] |= _ppc_ha(obj); 610107572Sgrehan pltresolve[4] |= _ppc_la(obj); 611107572Sgrehan 612107572Sgrehan /* 613228635Snwhitehorn * The icache will be sync'd in reloc_plt, which is called 614228635Snwhitehorn * after all the slots have been updated 615107572Sgrehan */ 616107572Sgrehan} 617133133Sdfr 618133133Sdfrvoid 619133133Sdfrallocate_initial_tls(Obj_Entry *list) 620133133Sdfr{ 621133133Sdfr register Elf_Addr **tp __asm__("r2"); 622137122Sssouhlal Elf_Addr **_tp; 623133133Sdfr 624133133Sdfr /* 625133133Sdfr * Fix the size of the static TLS block by using the maximum 626133133Sdfr * offset allocated so far and adding a bit for dynamic modules to 627133133Sdfr * use. 628133133Sdfr */ 629133133Sdfr 630133133Sdfr tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; 631133133Sdfr 632161799Smarcel _tp = (Elf_Addr **) ((char *) allocate_tls(list, NULL, TLS_TCB_SIZE, 8) 633137122Sssouhlal + TLS_TP_OFFSET + TLS_TCB_SIZE); 634137122Sssouhlal 635137122Sssouhlal /* 636137122Sssouhlal * XXX gcc seems to ignore 'tp = _tp;' 637137122Sssouhlal */ 638137122Sssouhlal 639137122Sssouhlal __asm __volatile("mr %0,%1" : "=r"(tp) : "r"(_tp)); 640133133Sdfr} 641133133Sdfr 642133133Sdfrvoid* 643133133Sdfr__tls_get_addr(tls_index* ti) 644133133Sdfr{ 645133133Sdfr register Elf_Addr **tp __asm__("r2"); 646133133Sdfr char *p; 647133133Sdfr 648137122Sssouhlal p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET 649137122Sssouhlal - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset); 650137122Sssouhlal 651137122Sssouhlal return (p + TLS_DTV_OFFSET); 652133133Sdfr} 653