1b9dea67fSPeter Grehan /* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */ 2b9dea67fSPeter Grehan 3b9dea67fSPeter Grehan /*- 4e6209940SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-NetBSD 5e6209940SPedro F. Giffuni * 6b9dea67fSPeter Grehan * Copyright (C) 1998 Tsubai Masanari 7b9dea67fSPeter Grehan * All rights reserved. 8b9dea67fSPeter Grehan * 9b9dea67fSPeter Grehan * Redistribution and use in source and binary forms, with or without 10b9dea67fSPeter Grehan * modification, are permitted provided that the following conditions 11b9dea67fSPeter Grehan * are met: 12b9dea67fSPeter Grehan * 1. Redistributions of source code must retain the above copyright 13b9dea67fSPeter Grehan * notice, this list of conditions and the following disclaimer. 14b9dea67fSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 15b9dea67fSPeter Grehan * notice, this list of conditions and the following disclaimer in the 16b9dea67fSPeter Grehan * documentation and/or other materials provided with the distribution. 17b9dea67fSPeter Grehan * 3. The name of the author may not be used to endorse or promote products 18b9dea67fSPeter Grehan * derived from this software without specific prior written permission. 19b9dea67fSPeter Grehan * 20b9dea67fSPeter Grehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21b9dea67fSPeter Grehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22b9dea67fSPeter Grehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23b9dea67fSPeter Grehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24b9dea67fSPeter Grehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25b9dea67fSPeter Grehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26b9dea67fSPeter Grehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27b9dea67fSPeter Grehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28b9dea67fSPeter Grehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29b9dea67fSPeter Grehan * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30b9dea67fSPeter Grehan * 31b9dea67fSPeter Grehan * $FreeBSD$ 32b9dea67fSPeter Grehan */ 33b9dea67fSPeter Grehan 34b9dea67fSPeter Grehan #include <sys/param.h> 35b9dea67fSPeter Grehan #include <sys/mman.h> 36b9dea67fSPeter Grehan 37b9dea67fSPeter Grehan #include <errno.h> 38b9dea67fSPeter Grehan #include <stdio.h> 39b9dea67fSPeter Grehan #include <stdlib.h> 40b9dea67fSPeter Grehan #include <string.h> 41b9dea67fSPeter Grehan #include <unistd.h> 42a29cc9a3SAndriy Gapon #include <machine/cpu.h> 43c606eab4SNathan Whitehorn #include <machine/atomic.h> 441566f9a7SJohn Birrell #include <machine/md_var.h> 45b9dea67fSPeter Grehan 46b9dea67fSPeter Grehan #include "debug.h" 47b9dea67fSPeter Grehan #include "rtld.h" 48b9dea67fSPeter Grehan 49b9dea67fSPeter Grehan #define _ppc_ha(x) ((((u_int32_t)(x) & 0x8000) ? \ 50b9dea67fSPeter Grehan ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16) 51b9dea67fSPeter Grehan #define _ppc_la(x) ((u_int32_t)(x) & 0xffff) 52b9dea67fSPeter Grehan 534f2730f7SNathan Whitehorn #define min(a,b) (((a) < (b)) ? (a) : (b)) 544f2730f7SNathan Whitehorn #define max(a,b) (((a) > (b)) ? (a) : (b)) 554f2730f7SNathan Whitehorn 564f2730f7SNathan Whitehorn #define PLT_EXTENDED_BEGIN (1 << 13) 574f2730f7SNathan Whitehorn #define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \ 584f2730f7SNathan Whitehorn (N - PLT_EXTENDED_BEGIN)*2 : 0)) 594f2730f7SNathan Whitehorn 60*f62da49bSJustin Hibbits void _rtld_bind_secureplt_start(void); 61*f62da49bSJustin Hibbits 62b9dea67fSPeter Grehan /* 63b9dea67fSPeter Grehan * Process the R_PPC_COPY relocations 64b9dea67fSPeter Grehan */ 65b9dea67fSPeter Grehan int 66b9dea67fSPeter Grehan do_copy_relocations(Obj_Entry *dstobj) 67b9dea67fSPeter Grehan { 68b9dea67fSPeter Grehan const Elf_Rela *relalim; 69b9dea67fSPeter Grehan const Elf_Rela *rela; 70b9dea67fSPeter Grehan 71b9dea67fSPeter Grehan /* 72b9dea67fSPeter Grehan * COPY relocs are invalid outside of the main program 73b9dea67fSPeter Grehan */ 74b9dea67fSPeter Grehan assert(dstobj->mainprog); 75b9dea67fSPeter Grehan 76903e0ffdSAlex Richardson relalim = (const Elf_Rela *)((const char *) dstobj->rela + 77b9dea67fSPeter Grehan dstobj->relasize); 78b9dea67fSPeter Grehan for (rela = dstobj->rela; rela < relalim; rela++) { 79b9dea67fSPeter Grehan void *dstaddr; 80b9dea67fSPeter Grehan const Elf_Sym *dstsym; 81b9dea67fSPeter Grehan const char *name; 82b9dea67fSPeter Grehan size_t size; 83b9dea67fSPeter Grehan const void *srcaddr; 84b9dea67fSPeter Grehan const Elf_Sym *srcsym = NULL; 858569deafSKonstantin Belousov const Obj_Entry *srcobj, *defobj; 868569deafSKonstantin Belousov SymLook req; 878569deafSKonstantin Belousov int res; 88b9dea67fSPeter Grehan 89b9dea67fSPeter Grehan if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) { 90b9dea67fSPeter Grehan continue; 91b9dea67fSPeter Grehan } 92b9dea67fSPeter Grehan 93b9dea67fSPeter Grehan dstaddr = (void *)(dstobj->relocbase + rela->r_offset); 94b9dea67fSPeter Grehan dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); 95b9dea67fSPeter Grehan name = dstobj->strtab + dstsym->st_name; 96b9dea67fSPeter Grehan size = dstsym->st_size; 978569deafSKonstantin Belousov symlook_init(&req, name); 988569deafSKonstantin Belousov req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); 99082f959aSKonstantin Belousov req.flags = SYMLOOK_EARLY; 100b9dea67fSPeter Grehan 1019fee0541SKonstantin Belousov for (srcobj = globallist_next(dstobj); srcobj != NULL; 1029fee0541SKonstantin Belousov srcobj = globallist_next(srcobj)) { 1038569deafSKonstantin Belousov res = symlook_obj(&req, srcobj); 1048569deafSKonstantin Belousov if (res == 0) { 1058569deafSKonstantin Belousov srcsym = req.sym_out; 1068569deafSKonstantin Belousov defobj = req.defobj_out; 107b9dea67fSPeter Grehan break; 108b9dea67fSPeter Grehan } 109b9dea67fSPeter Grehan } 110b9dea67fSPeter Grehan 111b9dea67fSPeter Grehan if (srcobj == NULL) { 112b9dea67fSPeter Grehan _rtld_error("Undefined symbol \"%s\" " 113b9dea67fSPeter Grehan " referenced from COPY" 114b9dea67fSPeter Grehan " relocation in %s", name, dstobj->path); 115b9dea67fSPeter Grehan return (-1); 116b9dea67fSPeter Grehan } 117b9dea67fSPeter Grehan 1188569deafSKonstantin Belousov srcaddr = (const void *)(defobj->relocbase+srcsym->st_value); 119b9dea67fSPeter Grehan memcpy(dstaddr, srcaddr, size); 120b9dea67fSPeter Grehan dbg("copy_reloc: src=%p,dst=%p,size=%d\n",srcaddr,dstaddr,size); 121b9dea67fSPeter Grehan } 122b9dea67fSPeter Grehan 123b9dea67fSPeter Grehan return (0); 124b9dea67fSPeter Grehan } 125b9dea67fSPeter Grehan 126b9dea67fSPeter Grehan 127b9dea67fSPeter Grehan /* 128b9dea67fSPeter Grehan * Perform early relocation of the run-time linker image 129b9dea67fSPeter Grehan */ 130b9dea67fSPeter Grehan void 131b9dea67fSPeter Grehan reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase) 132b9dea67fSPeter Grehan { 133a5d5e8ddSPedro F. Giffuni const Elf_Rela *rela = NULL, *relalim; 134b9dea67fSPeter Grehan Elf_Addr relasz = 0; 135b9dea67fSPeter Grehan Elf_Addr *where; 136b9dea67fSPeter Grehan 137b9dea67fSPeter Grehan /* 138b9dea67fSPeter Grehan * Extract the rela/relasz values from the dynamic section 139b9dea67fSPeter Grehan */ 140b9dea67fSPeter Grehan for (; dynp->d_tag != DT_NULL; dynp++) { 141b9dea67fSPeter Grehan switch (dynp->d_tag) { 142b9dea67fSPeter Grehan case DT_RELA: 143b9dea67fSPeter Grehan rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr); 144b9dea67fSPeter Grehan break; 145b9dea67fSPeter Grehan case DT_RELASZ: 146b9dea67fSPeter Grehan relasz = dynp->d_un.d_val; 147b9dea67fSPeter Grehan break; 148b9dea67fSPeter Grehan } 149b9dea67fSPeter Grehan } 150b9dea67fSPeter Grehan 151b9dea67fSPeter Grehan /* 152b9dea67fSPeter Grehan * Relocate these values 153b9dea67fSPeter Grehan */ 154903e0ffdSAlex Richardson relalim = (const Elf_Rela *)((const char *)rela + relasz); 155b9dea67fSPeter Grehan for (; rela < relalim; rela++) { 156b9dea67fSPeter Grehan where = (Elf_Addr *)(relocbase + rela->r_offset); 157b9dea67fSPeter Grehan *where = (Elf_Addr)(relocbase + rela->r_addend); 158b9dea67fSPeter Grehan } 159b9dea67fSPeter Grehan } 160b9dea67fSPeter Grehan 161b9dea67fSPeter Grehan 162b9dea67fSPeter Grehan /* 163b9dea67fSPeter Grehan * Relocate a non-PLT object with addend. 164b9dea67fSPeter Grehan */ 165b9dea67fSPeter Grehan static int 166903e0ffdSAlex Richardson reloc_nonplt_object(Obj_Entry *obj_rtld __unused, Obj_Entry *obj, 167903e0ffdSAlex Richardson const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate) 168b9dea67fSPeter Grehan { 169b9dea67fSPeter Grehan Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 170b9dea67fSPeter Grehan const Elf_Sym *def; 171b9dea67fSPeter Grehan const Obj_Entry *defobj; 172b9dea67fSPeter Grehan Elf_Addr tmp; 173b9dea67fSPeter Grehan 174b9dea67fSPeter Grehan switch (ELF_R_TYPE(rela->r_info)) { 175b9dea67fSPeter Grehan 176b9dea67fSPeter Grehan case R_PPC_NONE: 177b9dea67fSPeter Grehan break; 178b9dea67fSPeter Grehan 179b9dea67fSPeter Grehan case R_PPC_ADDR32: /* word32 S + A */ 180b9dea67fSPeter Grehan case R_PPC_GLOB_DAT: /* word32 S + A */ 181b9dea67fSPeter Grehan def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 182082f959aSKonstantin Belousov flags, cache, lockstate); 183b9dea67fSPeter Grehan if (def == NULL) { 184b9dea67fSPeter Grehan return (-1); 185b9dea67fSPeter Grehan } 186b9dea67fSPeter Grehan 187b9dea67fSPeter Grehan tmp = (Elf_Addr)(defobj->relocbase + def->st_value + 188b9dea67fSPeter Grehan rela->r_addend); 189b9dea67fSPeter Grehan 190b9dea67fSPeter Grehan /* Don't issue write if unnecessary; avoid COW page fault */ 191b9dea67fSPeter Grehan if (*where != tmp) { 192b9dea67fSPeter Grehan *where = tmp; 193b9dea67fSPeter Grehan } 194b9dea67fSPeter Grehan break; 195b9dea67fSPeter Grehan 196b9dea67fSPeter Grehan case R_PPC_RELATIVE: /* word32 B + A */ 197b9dea67fSPeter Grehan tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); 198b9dea67fSPeter Grehan 199b9dea67fSPeter Grehan /* As above, don't issue write unnecessarily */ 200b9dea67fSPeter Grehan if (*where != tmp) { 201b9dea67fSPeter Grehan *where = tmp; 202b9dea67fSPeter Grehan } 203b9dea67fSPeter Grehan break; 204b9dea67fSPeter Grehan 205b9dea67fSPeter Grehan case R_PPC_COPY: 206b9dea67fSPeter Grehan /* 207b9dea67fSPeter Grehan * These are deferred until all other relocations 208b9dea67fSPeter Grehan * have been done. All we do here is make sure 209b9dea67fSPeter Grehan * that the COPY relocation is not in a shared 210b9dea67fSPeter Grehan * library. They are allowed only in executable 211b9dea67fSPeter Grehan * files. 212b9dea67fSPeter Grehan */ 213b9dea67fSPeter Grehan if (!obj->mainprog) { 214b9dea67fSPeter Grehan _rtld_error("%s: Unexpected R_COPY " 215b9dea67fSPeter Grehan " relocation in shared library", 216b9dea67fSPeter Grehan obj->path); 217b9dea67fSPeter Grehan return (-1); 218b9dea67fSPeter Grehan } 219b9dea67fSPeter Grehan break; 220b9dea67fSPeter Grehan 221b9dea67fSPeter Grehan case R_PPC_JMP_SLOT: 222b9dea67fSPeter Grehan /* 223b9dea67fSPeter Grehan * These will be handled by the plt/jmpslot routines 224b9dea67fSPeter Grehan */ 225b9dea67fSPeter Grehan break; 226b9dea67fSPeter Grehan 2276c2a9753SSuleiman Souhlal case R_PPC_DTPMOD32: 2286c2a9753SSuleiman Souhlal def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 229082f959aSKonstantin Belousov flags, cache, lockstate); 2306c2a9753SSuleiman Souhlal 2316c2a9753SSuleiman Souhlal if (def == NULL) 2326c2a9753SSuleiman Souhlal return (-1); 2336c2a9753SSuleiman Souhlal 2346c2a9753SSuleiman Souhlal *where = (Elf_Addr) defobj->tlsindex; 2356c2a9753SSuleiman Souhlal 2366c2a9753SSuleiman Souhlal break; 2376c2a9753SSuleiman Souhlal 2386c2a9753SSuleiman Souhlal case R_PPC_TPREL32: 2396c2a9753SSuleiman Souhlal def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 240082f959aSKonstantin Belousov flags, cache, lockstate); 2416c2a9753SSuleiman Souhlal 2426c2a9753SSuleiman Souhlal if (def == NULL) 2436c2a9753SSuleiman Souhlal return (-1); 2446c2a9753SSuleiman Souhlal 2456c2a9753SSuleiman Souhlal /* 2466c2a9753SSuleiman Souhlal * We lazily allocate offsets for static TLS as we 2476c2a9753SSuleiman Souhlal * see the first relocation that references the 2486c2a9753SSuleiman Souhlal * TLS block. This allows us to support (small 2496c2a9753SSuleiman Souhlal * amounts of) static TLS in dynamically loaded 2506c2a9753SSuleiman Souhlal * modules. If we run out of space, we generate an 2516c2a9753SSuleiman Souhlal * error. 2526c2a9753SSuleiman Souhlal */ 2536c2a9753SSuleiman Souhlal if (!defobj->tls_done) { 254903e0ffdSAlex Richardson if (!allocate_tls_offset( 255903e0ffdSAlex Richardson __DECONST(Obj_Entry *, defobj))) { 2566c2a9753SSuleiman Souhlal _rtld_error("%s: No space available for static " 2576c2a9753SSuleiman Souhlal "Thread Local Storage", obj->path); 2586c2a9753SSuleiman Souhlal return (-1); 2596c2a9753SSuleiman Souhlal } 2606c2a9753SSuleiman Souhlal } 2616c2a9753SSuleiman Souhlal 2626c2a9753SSuleiman Souhlal *(Elf_Addr **)where = *where * sizeof(Elf_Addr) 2636c2a9753SSuleiman Souhlal + (Elf_Addr *)(def->st_value + rela->r_addend 264953cba36SAndreas Tobler + defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE); 2656c2a9753SSuleiman Souhlal 2666c2a9753SSuleiman Souhlal break; 2676c2a9753SSuleiman Souhlal 2686c2a9753SSuleiman Souhlal case R_PPC_DTPREL32: 2696c2a9753SSuleiman Souhlal def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 270082f959aSKonstantin Belousov flags, cache, lockstate); 2716c2a9753SSuleiman Souhlal 2726c2a9753SSuleiman Souhlal if (def == NULL) 2736c2a9753SSuleiman Souhlal return (-1); 2746c2a9753SSuleiman Souhlal 2756c2a9753SSuleiman Souhlal *where += (Elf_Addr)(def->st_value + rela->r_addend 2766c2a9753SSuleiman Souhlal - TLS_DTV_OFFSET); 2776c2a9753SSuleiman Souhlal 2786c2a9753SSuleiman Souhlal break; 2796c2a9753SSuleiman Souhlal 280b9dea67fSPeter Grehan default: 281b9dea67fSPeter Grehan _rtld_error("%s: Unsupported relocation type %d" 282b9dea67fSPeter Grehan " in non-PLT relocations\n", obj->path, 283b9dea67fSPeter Grehan ELF_R_TYPE(rela->r_info)); 284b9dea67fSPeter Grehan return (-1); 285b9dea67fSPeter Grehan } 286b9dea67fSPeter Grehan return (0); 287b9dea67fSPeter Grehan } 288b9dea67fSPeter Grehan 289b9dea67fSPeter Grehan 290b9dea67fSPeter Grehan /* 291b9dea67fSPeter Grehan * Process non-PLT relocations 292b9dea67fSPeter Grehan */ 293b9dea67fSPeter Grehan int 294082f959aSKonstantin Belousov reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, 295082f959aSKonstantin Belousov RtldLockState *lockstate) 296b9dea67fSPeter Grehan { 297b9dea67fSPeter Grehan const Elf_Rela *relalim; 298b9dea67fSPeter Grehan const Elf_Rela *rela; 299f846c80aSKonstantin Belousov const Elf_Phdr *phdr; 300b9dea67fSPeter Grehan SymCache *cache; 301b9dea67fSPeter Grehan int r = -1; 302b9dea67fSPeter Grehan 30314c35647SKonstantin Belousov if ((flags & SYMLOOK_IFUNC) != 0) 30414c35647SKonstantin Belousov /* XXX not implemented */ 30514c35647SKonstantin Belousov return (0); 30614c35647SKonstantin Belousov 307b9dea67fSPeter Grehan /* 308b9dea67fSPeter Grehan * The dynamic loader may be called from a thread, we have 309b9dea67fSPeter Grehan * limited amounts of stack available so we cannot use alloca(). 310b9dea67fSPeter Grehan */ 31199227f1eSMarcel Moolenaar if (obj != obj_rtld) { 312f6265192SKonstantin Belousov cache = calloc(obj->dynsymcount, sizeof(SymCache)); 3131dfdc15bSRoman Divacky /* No need to check for NULL here */ 31499227f1eSMarcel Moolenaar } else 31599227f1eSMarcel Moolenaar cache = NULL; 316b9dea67fSPeter Grehan 317b9dea67fSPeter Grehan /* 318b9dea67fSPeter Grehan * From the SVR4 PPC ABI: 319b9dea67fSPeter Grehan * "The PowerPC family uses only the Elf32_Rela relocation 320b9dea67fSPeter Grehan * entries with explicit addends." 321b9dea67fSPeter Grehan */ 322903e0ffdSAlex Richardson relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize); 323b9dea67fSPeter Grehan for (rela = obj->rela; rela < relalim; rela++) { 324082f959aSKonstantin Belousov if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags, 325082f959aSKonstantin Belousov lockstate) < 0) 326b9dea67fSPeter Grehan goto done; 327b9dea67fSPeter Grehan } 328b9dea67fSPeter Grehan r = 0; 329b9dea67fSPeter Grehan done: 3301dfdc15bSRoman Divacky if (cache != NULL) 3311dfdc15bSRoman Divacky free(cache); 3324b51c699SNathan Whitehorn 333f846c80aSKonstantin Belousov /* 334f846c80aSKonstantin Belousov * Synchronize icache for executable segments in case we made 335f846c80aSKonstantin Belousov * any changes. 336f846c80aSKonstantin Belousov */ 337f846c80aSKonstantin Belousov for (phdr = obj->phdr; 338f846c80aSKonstantin Belousov (const char *)phdr < (const char *)obj->phdr + obj->phsize; 339f846c80aSKonstantin Belousov phdr++) { 340f846c80aSKonstantin Belousov if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X) != 0) { 341f846c80aSKonstantin Belousov __syncicache(obj->relocbase + phdr->p_vaddr, 342f846c80aSKonstantin Belousov phdr->p_memsz); 343f846c80aSKonstantin Belousov } 344f846c80aSKonstantin Belousov } 3454b51c699SNathan Whitehorn 346b9dea67fSPeter Grehan return (r); 347b9dea67fSPeter Grehan } 348b9dea67fSPeter Grehan 349b9dea67fSPeter Grehan /* 350b9dea67fSPeter Grehan * Initialise a PLT slot to the resolving trampoline 351b9dea67fSPeter Grehan */ 352b9dea67fSPeter Grehan static int 353b9dea67fSPeter Grehan reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) 354b9dea67fSPeter Grehan { 355b9dea67fSPeter Grehan Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); 3564f2730f7SNathan Whitehorn Elf_Addr *pltresolve, *pltlongresolve, *jmptab; 357b9dea67fSPeter Grehan Elf_Addr distance; 3584f2730f7SNathan Whitehorn int N = obj->pltrelasize / sizeof(Elf_Rela); 359b9dea67fSPeter Grehan int reloff; 360b9dea67fSPeter Grehan 361b9dea67fSPeter Grehan reloff = rela - obj->pltrela; 362b9dea67fSPeter Grehan 3634f2730f7SNathan Whitehorn if (reloff < 0) 364b9dea67fSPeter Grehan return (-1); 365b9dea67fSPeter Grehan 366*f62da49bSJustin Hibbits if (obj->gotptr != NULL) { 367*f62da49bSJustin Hibbits *where += (Elf_Addr)obj->relocbase; 368*f62da49bSJustin Hibbits return (0); 369*f62da49bSJustin Hibbits } 370*f62da49bSJustin Hibbits 3714f2730f7SNathan Whitehorn pltlongresolve = obj->pltgot + 5; 3724f2730f7SNathan Whitehorn pltresolve = pltlongresolve + 5; 373b9dea67fSPeter Grehan 374b9dea67fSPeter Grehan distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1); 375b9dea67fSPeter Grehan 376b9dea67fSPeter Grehan dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x", 377b9dea67fSPeter Grehan (void *)where, (void *)pltresolve, reloff, distance); 378b9dea67fSPeter Grehan 3794f2730f7SNathan Whitehorn if (reloff < PLT_EXTENDED_BEGIN) { 380b9dea67fSPeter Grehan /* li r11,reloff */ 381b9dea67fSPeter Grehan /* b pltresolve */ 382b9dea67fSPeter Grehan where[0] = 0x39600000 | reloff; 383b9dea67fSPeter Grehan where[1] = 0x48000000 | (distance & 0x03fffffc); 3844f2730f7SNathan Whitehorn } else { 3854f2730f7SNathan Whitehorn jmptab = obj->pltgot + JMPTAB_BASE(N); 3864f2730f7SNathan Whitehorn jmptab[reloff] = (u_int)pltlongresolve; 3874f2730f7SNathan Whitehorn 3884f2730f7SNathan Whitehorn /* lis r11,jmptab[reloff]@ha */ 3894f2730f7SNathan Whitehorn /* lwzu r12,jmptab[reloff]@l(r11) */ 3904f2730f7SNathan Whitehorn /* mtctr r12 */ 3914f2730f7SNathan Whitehorn /* bctr */ 3924f2730f7SNathan Whitehorn where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]); 3934f2730f7SNathan Whitehorn where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]); 3944f2730f7SNathan Whitehorn where[2] = 0x7d8903a6; 3954f2730f7SNathan Whitehorn where[3] = 0x4e800420; 3964f2730f7SNathan Whitehorn } 3974f2730f7SNathan Whitehorn 398b9dea67fSPeter Grehan 399b9dea67fSPeter Grehan /* 40017bbcc52SNathan Whitehorn * The icache will be sync'd in reloc_plt, which is called 401b9dea67fSPeter Grehan * after all the slots have been updated 402b9dea67fSPeter Grehan */ 403b9dea67fSPeter Grehan 404b9dea67fSPeter Grehan return (0); 405b9dea67fSPeter Grehan } 406b9dea67fSPeter Grehan 407b9dea67fSPeter Grehan 408b9dea67fSPeter Grehan /* 409b9dea67fSPeter Grehan * Process the PLT relocations. 410b9dea67fSPeter Grehan */ 411b9dea67fSPeter Grehan int 4124849c3a5SMichal Meloun reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused) 413b9dea67fSPeter Grehan { 414b9dea67fSPeter Grehan const Elf_Rela *relalim; 415b9dea67fSPeter Grehan const Elf_Rela *rela; 41617bbcc52SNathan Whitehorn int N = obj->pltrelasize / sizeof(Elf_Rela); 417b9dea67fSPeter Grehan 418b9dea67fSPeter Grehan if (obj->pltrelasize != 0) { 419b9dea67fSPeter Grehan 420903e0ffdSAlex Richardson relalim = (const Elf_Rela *)((const char *)obj->pltrela + 421b9dea67fSPeter Grehan obj->pltrelasize); 422b9dea67fSPeter Grehan for (rela = obj->pltrela; rela < relalim; rela++) { 423b9dea67fSPeter Grehan assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); 424b9dea67fSPeter Grehan 425b9dea67fSPeter Grehan if (reloc_plt_object(obj, rela) < 0) { 426b9dea67fSPeter Grehan return (-1); 427b9dea67fSPeter Grehan } 428b9dea67fSPeter Grehan } 429b9dea67fSPeter Grehan } 430b9dea67fSPeter Grehan 43117bbcc52SNathan Whitehorn /* 43217bbcc52SNathan Whitehorn * Sync the icache for the byte range represented by the 43317bbcc52SNathan Whitehorn * trampoline routines and call slots. 43417bbcc52SNathan Whitehorn */ 435*f62da49bSJustin Hibbits if (obj->pltgot != NULL && obj->gotptr == NULL) 43617bbcc52SNathan Whitehorn __syncicache(obj->pltgot, JMPTAB_BASE(N)*4); 43717bbcc52SNathan Whitehorn 438b9dea67fSPeter Grehan return (0); 439b9dea67fSPeter Grehan } 440b9dea67fSPeter Grehan 441b9dea67fSPeter Grehan 442b9dea67fSPeter Grehan /* 443b9dea67fSPeter Grehan * LD_BIND_NOW was set - force relocation for all jump slots 444b9dea67fSPeter Grehan */ 445b9dea67fSPeter Grehan int 446082f959aSKonstantin Belousov reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) 447b9dea67fSPeter Grehan { 448b9dea67fSPeter Grehan const Obj_Entry *defobj; 449b9dea67fSPeter Grehan const Elf_Rela *relalim; 450b9dea67fSPeter Grehan const Elf_Rela *rela; 451b9dea67fSPeter Grehan const Elf_Sym *def; 452b9dea67fSPeter Grehan Elf_Addr *where; 453b9dea67fSPeter Grehan Elf_Addr target; 454b9dea67fSPeter Grehan 455903e0ffdSAlex Richardson relalim = (const Elf_Rela *)((const char *)obj->pltrela + 456903e0ffdSAlex Richardson obj->pltrelasize); 457b9dea67fSPeter Grehan for (rela = obj->pltrela; rela < relalim; rela++) { 458b9dea67fSPeter Grehan assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); 459b9dea67fSPeter Grehan where = (Elf_Addr *)(obj->relocbase + rela->r_offset); 460b9dea67fSPeter Grehan def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, 461082f959aSKonstantin Belousov SYMLOOK_IN_PLT | flags, NULL, lockstate); 462b9dea67fSPeter Grehan if (def == NULL) { 463b9dea67fSPeter Grehan dbg("reloc_jmpslots: sym not found"); 464b9dea67fSPeter Grehan return (-1); 465b9dea67fSPeter Grehan } 466b9dea67fSPeter Grehan 467b9dea67fSPeter Grehan target = (Elf_Addr)(defobj->relocbase + def->st_value); 468b9dea67fSPeter Grehan 469b9dea67fSPeter Grehan #if 0 470b9dea67fSPeter Grehan /* PG XXX */ 471b9dea67fSPeter Grehan dbg("\"%s\" in \"%s\" --> %p in \"%s\"", 472b9dea67fSPeter Grehan defobj->strtab + def->st_name, basename(obj->path), 473b9dea67fSPeter Grehan (void *)target, basename(defobj->path)); 474b9dea67fSPeter Grehan #endif 475b9dea67fSPeter Grehan 476b9dea67fSPeter Grehan reloc_jmpslot(where, target, defobj, obj, 477b9dea67fSPeter Grehan (const Elf_Rel *) rela); 478b9dea67fSPeter Grehan } 479b9dea67fSPeter Grehan 480b9dea67fSPeter Grehan obj->jmpslots_done = true; 481b9dea67fSPeter Grehan 482b9dea67fSPeter Grehan return (0); 483b9dea67fSPeter Grehan } 484b9dea67fSPeter Grehan 485b9dea67fSPeter Grehan 486b9dea67fSPeter Grehan /* 487b9dea67fSPeter Grehan * Update the value of a PLT jump slot. Branch directly to the target if 488b9dea67fSPeter Grehan * it is within +/- 32Mb, otherwise go indirectly via the pltcall 489b9dea67fSPeter Grehan * trampoline call and jump table. 490b9dea67fSPeter Grehan */ 491b9dea67fSPeter Grehan Elf_Addr 492903e0ffdSAlex Richardson reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, 493903e0ffdSAlex Richardson const Obj_Entry *defobj __unused, const Obj_Entry *obj, const Elf_Rel *rel) 494b9dea67fSPeter Grehan { 495b9dea67fSPeter Grehan Elf_Addr offset; 496b9dea67fSPeter Grehan const Elf_Rela *rela = (const Elf_Rela *) rel; 497b9dea67fSPeter Grehan 498b9dea67fSPeter Grehan dbg(" reloc_jmpslot: where=%p, target=%p", 499b9dea67fSPeter Grehan (void *)wherep, (void *)target); 500b9dea67fSPeter Grehan 501e35ddbe4SKonstantin Belousov if (ld_bind_not) 502e35ddbe4SKonstantin Belousov goto out; 503e35ddbe4SKonstantin Belousov 504b9dea67fSPeter Grehan /* 505b9dea67fSPeter Grehan * At the PLT entry pointed at by `wherep', construct 506b9dea67fSPeter Grehan * a direct transfer to the now fully resolved function 507b9dea67fSPeter Grehan * address. 508b9dea67fSPeter Grehan */ 509b9dea67fSPeter Grehan offset = target - (Elf_Addr)wherep; 510b9dea67fSPeter Grehan 511*f62da49bSJustin Hibbits if (obj->gotptr != NULL) { 512*f62da49bSJustin Hibbits assert(wherep >= (Elf_Word *)obj->pltgot); 513*f62da49bSJustin Hibbits assert(wherep < 514*f62da49bSJustin Hibbits (Elf_Word *)obj->pltgot + obj->pltrelasize); 515*f62da49bSJustin Hibbits *wherep = target; 516*f62da49bSJustin Hibbits goto out; 517*f62da49bSJustin Hibbits } 518*f62da49bSJustin Hibbits 519918428b8SDimitry Andric if (abs((int)offset) < 32*1024*1024) { /* inside 32MB? */ 520b9dea67fSPeter Grehan /* b value # branch directly */ 521b9dea67fSPeter Grehan *wherep = 0x48000000 | (offset & 0x03fffffc); 522b9dea67fSPeter Grehan __syncicache(wherep, 4); 523b9dea67fSPeter Grehan } else { 524b9dea67fSPeter Grehan Elf_Addr *pltcall, *jmptab; 525b9dea67fSPeter Grehan int distance; 526b9dea67fSPeter Grehan int N = obj->pltrelasize / sizeof(Elf_Rela); 527b9dea67fSPeter Grehan int reloff = rela - obj->pltrela; 528b9dea67fSPeter Grehan 5294f2730f7SNathan Whitehorn if (reloff < 0) 530b9dea67fSPeter Grehan return (-1); 531b9dea67fSPeter Grehan 532b9dea67fSPeter Grehan pltcall = obj->pltgot; 533b9dea67fSPeter Grehan 5344f2730f7SNathan Whitehorn dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n", 535b9dea67fSPeter Grehan reloff, N); 536b9dea67fSPeter Grehan 5374f2730f7SNathan Whitehorn jmptab = obj->pltgot + JMPTAB_BASE(N); 538b9dea67fSPeter Grehan jmptab[reloff] = target; 539c606eab4SNathan Whitehorn mb(); /* Order jmptab update before next changes */ 540b9dea67fSPeter Grehan 5414f2730f7SNathan Whitehorn if (reloff < PLT_EXTENDED_BEGIN) { 5424f2730f7SNathan Whitehorn /* for extended PLT entries, we keep the old code */ 5434f2730f7SNathan Whitehorn 544b9dea67fSPeter Grehan distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1); 545b9dea67fSPeter Grehan 546b9dea67fSPeter Grehan /* li r11,reloff */ 547b9dea67fSPeter Grehan /* b pltcall # use indirect pltcall routine */ 5489d00444dSNathan Whitehorn 5499d00444dSNathan Whitehorn /* first instruction same as before */ 550b9dea67fSPeter Grehan wherep[1] = 0x48000000 | (distance & 0x03fffffc); 551b9dea67fSPeter Grehan __syncicache(wherep, 8); 552b9dea67fSPeter Grehan } 5534f2730f7SNathan Whitehorn } 554b9dea67fSPeter Grehan 555e35ddbe4SKonstantin Belousov out: 556b9dea67fSPeter Grehan return (target); 557b9dea67fSPeter Grehan } 558b9dea67fSPeter Grehan 5596be4b697SKonstantin Belousov int 560903e0ffdSAlex Richardson reloc_iresolve(Obj_Entry *obj __unused, 561903e0ffdSAlex Richardson struct Struct_RtldLockState *lockstate __unused) 5626be4b697SKonstantin Belousov { 5636be4b697SKonstantin Belousov 5646be4b697SKonstantin Belousov /* XXX not implemented */ 5656be4b697SKonstantin Belousov return (0); 5666be4b697SKonstantin Belousov } 5676be4b697SKonstantin Belousov 5686be4b697SKonstantin Belousov int 569903e0ffdSAlex Richardson reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused, 570903e0ffdSAlex Richardson struct Struct_RtldLockState *lockstate __unused) 5716be4b697SKonstantin Belousov { 5726be4b697SKonstantin Belousov 5736be4b697SKonstantin Belousov /* XXX not implemented */ 5746be4b697SKonstantin Belousov return (0); 5756be4b697SKonstantin Belousov } 576b9dea67fSPeter Grehan 577b9dea67fSPeter Grehan /* 578b9dea67fSPeter Grehan * Setup the plt glue routines. 579b9dea67fSPeter Grehan */ 580b9dea67fSPeter Grehan #define PLTCALL_SIZE 20 5814f2730f7SNathan Whitehorn #define PLTLONGRESOLVE_SIZE 20 582b9dea67fSPeter Grehan #define PLTRESOLVE_SIZE 24 583b9dea67fSPeter Grehan 584b9dea67fSPeter Grehan void 585b9dea67fSPeter Grehan init_pltgot(Obj_Entry *obj) 586b9dea67fSPeter Grehan { 5874f2730f7SNathan Whitehorn Elf_Word *pltcall, *pltresolve, *pltlongresolve; 588b9dea67fSPeter Grehan Elf_Word *jmptab; 589b9dea67fSPeter Grehan int N = obj->pltrelasize / sizeof(Elf_Rela); 590b9dea67fSPeter Grehan 591b9dea67fSPeter Grehan pltcall = obj->pltgot; 592b9dea67fSPeter Grehan 593b9dea67fSPeter Grehan if (pltcall == NULL) { 594b9dea67fSPeter Grehan return; 595b9dea67fSPeter Grehan } 596b9dea67fSPeter Grehan 597*f62da49bSJustin Hibbits /* Handle Secure-PLT first, if applicable. */ 598*f62da49bSJustin Hibbits if (obj->gotptr != NULL) { 599*f62da49bSJustin Hibbits obj->gotptr[1] = (Elf_Addr)_rtld_bind_secureplt_start; 600*f62da49bSJustin Hibbits obj->gotptr[2] = (Elf_Addr)obj; 601*f62da49bSJustin Hibbits dbg("obj %s secure-plt gotptr=%p start=%p obj=%p", 602*f62da49bSJustin Hibbits obj->path, obj->gotptr, 603*f62da49bSJustin Hibbits (void *)obj->gotptr[1], (void *)obj->gotptr[2]); 604*f62da49bSJustin Hibbits return; 605*f62da49bSJustin Hibbits } 606*f62da49bSJustin Hibbits 607b9dea67fSPeter Grehan /* 608b9dea67fSPeter Grehan * From the SVR4 PPC ABI: 609b9dea67fSPeter Grehan * 610b9dea67fSPeter Grehan * 'The first 18 words (72 bytes) of the PLT are reserved for 611b9dea67fSPeter Grehan * use by the dynamic linker. 612b9dea67fSPeter Grehan * ... 613b9dea67fSPeter Grehan * 'If the executable or shared object requires N procedure 614b9dea67fSPeter Grehan * linkage table entries, the link editor shall reserve 3*N 615b9dea67fSPeter Grehan * words (12*N bytes) following the 18 reserved words. The 616b9dea67fSPeter Grehan * first 2*N of these words are the procedure linkage table 617b9dea67fSPeter Grehan * entries themselves. The static linker directs calls to bytes 618b9dea67fSPeter Grehan * (72 + (i-1)*8), for i between 1 and N inclusive. The remaining 619b9dea67fSPeter Grehan * N words (4*N bytes) are reserved for use by the dynamic linker.' 620b9dea67fSPeter Grehan */ 621b9dea67fSPeter Grehan 622b9dea67fSPeter Grehan /* 623b9dea67fSPeter Grehan * Copy the absolute-call assembler stub into the first part of 624b9dea67fSPeter Grehan * the reserved PLT area. 625b9dea67fSPeter Grehan */ 626b9dea67fSPeter Grehan memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE); 627b9dea67fSPeter Grehan 628b9dea67fSPeter Grehan /* 629b9dea67fSPeter Grehan * Determine the address of the jumptable, which is the dyn-linker 630b9dea67fSPeter Grehan * reserved area after the call cells. Write the absolute address 631b9dea67fSPeter Grehan * of the jumptable into the absolute-call assembler code so it 632b9dea67fSPeter Grehan * can determine this address. 633b9dea67fSPeter Grehan */ 6344f2730f7SNathan Whitehorn jmptab = obj->pltgot + JMPTAB_BASE(N); 635b9dea67fSPeter Grehan pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */ 636b9dea67fSPeter Grehan pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */ 637b9dea67fSPeter Grehan 638b9dea67fSPeter Grehan /* 6394f2730f7SNathan Whitehorn * Skip down 20 bytes into the initial reserved area and copy 640b9dea67fSPeter Grehan * in the standard resolving assembler call. Into this assembler, 641b9dea67fSPeter Grehan * insert the absolute address of the _rtld_bind_start routine 642b9dea67fSPeter Grehan * and the address of the relocation object. 6434f2730f7SNathan Whitehorn * 6444f2730f7SNathan Whitehorn * We place pltlongresolve first, so it can fix up its arguments 6454f2730f7SNathan Whitehorn * and then fall through to the regular PLT resolver. 646b9dea67fSPeter Grehan */ 6474f2730f7SNathan Whitehorn pltlongresolve = obj->pltgot + 5; 648b9dea67fSPeter Grehan 6494f2730f7SNathan Whitehorn memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve, 6504f2730f7SNathan Whitehorn PLTLONGRESOLVE_SIZE); 6514f2730f7SNathan Whitehorn pltlongresolve[0] |= _ppc_ha(jmptab); /* lis 12,jmptab@ha */ 6524f2730f7SNathan Whitehorn pltlongresolve[1] |= _ppc_la(jmptab); /* addi 12,12,jmptab@l */ 6534f2730f7SNathan Whitehorn 6544f2730f7SNathan Whitehorn pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t); 655b9dea67fSPeter Grehan memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE); 656b9dea67fSPeter Grehan pltresolve[0] |= _ppc_ha(_rtld_bind_start); 657b9dea67fSPeter Grehan pltresolve[1] |= _ppc_la(_rtld_bind_start); 658b9dea67fSPeter Grehan pltresolve[3] |= _ppc_ha(obj); 659b9dea67fSPeter Grehan pltresolve[4] |= _ppc_la(obj); 660b9dea67fSPeter Grehan 661b9dea67fSPeter Grehan /* 66217bbcc52SNathan Whitehorn * The icache will be sync'd in reloc_plt, which is called 66317bbcc52SNathan Whitehorn * after all the slots have been updated 664b9dea67fSPeter Grehan */ 665b9dea67fSPeter Grehan } 666fca32c74SDoug Rabson 667fca32c74SDoug Rabson void 668d27078f9SKonstantin Belousov ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) 6694352999eSKonstantin Belousov { 67041fc6f68SMarius Strobl 67141fc6f68SMarius Strobl } 67241fc6f68SMarius Strobl 67341fc6f68SMarius Strobl void 67441fc6f68SMarius Strobl pre_init(void) 67541fc6f68SMarius Strobl { 67641fc6f68SMarius Strobl 6774352999eSKonstantin Belousov } 6784352999eSKonstantin Belousov 6794352999eSKonstantin Belousov void 680fca32c74SDoug Rabson allocate_initial_tls(Obj_Entry *list) 681fca32c74SDoug Rabson { 6828c29a982SJustin Hibbits Elf_Addr **tp; 683fca32c74SDoug Rabson 684fca32c74SDoug Rabson /* 685fca32c74SDoug Rabson * Fix the size of the static TLS block by using the maximum 686fca32c74SDoug Rabson * offset allocated so far and adding a bit for dynamic modules to 687fca32c74SDoug Rabson * use. 688fca32c74SDoug Rabson */ 689fca32c74SDoug Rabson 690fca32c74SDoug Rabson tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; 691fca32c74SDoug Rabson 6928c29a982SJustin Hibbits tp = (Elf_Addr **)((char *) allocate_tls(list, NULL, TLS_TCB_SIZE, 8) 6936c2a9753SSuleiman Souhlal + TLS_TP_OFFSET + TLS_TCB_SIZE); 6946c2a9753SSuleiman Souhlal 6956c2a9753SSuleiman Souhlal /* 6966c2a9753SSuleiman Souhlal * XXX gcc seems to ignore 'tp = _tp;' 6976c2a9753SSuleiman Souhlal */ 6986c2a9753SSuleiman Souhlal 6998c29a982SJustin Hibbits __asm __volatile("mr 2,%0" :: "r"(tp)); 700fca32c74SDoug Rabson } 701fca32c74SDoug Rabson 702fca32c74SDoug Rabson void* 703fca32c74SDoug Rabson __tls_get_addr(tls_index* ti) 704fca32c74SDoug Rabson { 7058c29a982SJustin Hibbits register Elf_Addr **tp; 706fca32c74SDoug Rabson char *p; 707fca32c74SDoug Rabson 7088c29a982SJustin Hibbits __asm __volatile("mr %0,2" : "=r"(tp)); 7096c2a9753SSuleiman Souhlal p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET 7106c2a9753SSuleiman Souhlal - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset); 7116c2a9753SSuleiman Souhlal 7126c2a9753SSuleiman Souhlal return (p + TLS_DTV_OFFSET); 713fca32c74SDoug Rabson } 714