xref: /freebsd/libexec/rtld-elf/powerpc/reloc.c (revision 33658afd4e4d11cd71d92e52ca9da5381cdd829b)
1b9dea67fSPeter Grehan /*      $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $   */
2b9dea67fSPeter Grehan 
3b9dea67fSPeter Grehan /*-
4b61a5730SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 
32b9dea67fSPeter Grehan #include <sys/param.h>
33b9dea67fSPeter Grehan #include <sys/mman.h>
34b9dea67fSPeter Grehan 
35b9dea67fSPeter Grehan #include <errno.h>
36b9dea67fSPeter Grehan #include <stdio.h>
37b9dea67fSPeter Grehan #include <stdlib.h>
38b9dea67fSPeter Grehan #include <string.h>
39b9dea67fSPeter Grehan #include <unistd.h>
40a29cc9a3SAndriy Gapon #include <machine/cpu.h>
41c606eab4SNathan Whitehorn #include <machine/atomic.h>
421566f9a7SJohn Birrell #include <machine/md_var.h>
43b9dea67fSPeter Grehan 
44b9dea67fSPeter Grehan #include "debug.h"
45b9dea67fSPeter Grehan #include "rtld.h"
46b9dea67fSPeter Grehan 
47b9dea67fSPeter Grehan #define _ppc_ha(x) ((((u_int32_t)(x) & 0x8000) ? \
48b9dea67fSPeter Grehan                         ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
49b9dea67fSPeter Grehan #define _ppc_la(x) ((u_int32_t)(x) & 0xffff)
50b9dea67fSPeter Grehan 
514f2730f7SNathan Whitehorn #define min(a,b) (((a) < (b)) ? (a) : (b))
524f2730f7SNathan Whitehorn #define max(a,b) (((a) > (b)) ? (a) : (b))
534f2730f7SNathan Whitehorn 
544f2730f7SNathan Whitehorn #define PLT_EXTENDED_BEGIN	(1 << 13)
554f2730f7SNathan Whitehorn #define JMPTAB_BASE(N)		(18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \
564f2730f7SNathan Whitehorn 				    (N - PLT_EXTENDED_BEGIN)*2 : 0))
574f2730f7SNathan Whitehorn 
58f62da49bSJustin Hibbits void _rtld_bind_secureplt_start(void);
59f62da49bSJustin Hibbits 
601cd90a2cSAndrew Turner bool
arch_digest_dynamic(struct Struct_Obj_Entry * obj,const Elf_Dyn * dynp)611cd90a2cSAndrew Turner arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp)
621cd90a2cSAndrew Turner {
631cd90a2cSAndrew Turner 	if (dynp->d_tag == DT_PPC_GOT) {
641cd90a2cSAndrew Turner 		obj->gotptr = (Elf_Addr *)(obj->relocbase + dynp->d_un.d_ptr);
651cd90a2cSAndrew Turner 		return (true);
661cd90a2cSAndrew Turner 	}
671cd90a2cSAndrew Turner 
681cd90a2cSAndrew Turner 	return (false);
691cd90a2cSAndrew Turner }
701cd90a2cSAndrew Turner 
71b9dea67fSPeter Grehan /*
72b9dea67fSPeter Grehan  * Process the R_PPC_COPY relocations
73b9dea67fSPeter Grehan  */
74b9dea67fSPeter Grehan int
do_copy_relocations(Obj_Entry * dstobj)75b9dea67fSPeter Grehan do_copy_relocations(Obj_Entry *dstobj)
76b9dea67fSPeter Grehan {
77b9dea67fSPeter Grehan 	const Elf_Rela *relalim;
78b9dea67fSPeter Grehan 	const Elf_Rela *rela;
79b9dea67fSPeter Grehan 
80b9dea67fSPeter Grehan 	/*
81b9dea67fSPeter Grehan 	 * COPY relocs are invalid outside of the main program
82b9dea67fSPeter Grehan 	 */
83b9dea67fSPeter Grehan 	assert(dstobj->mainprog);
84b9dea67fSPeter Grehan 
85903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *) dstobj->rela +
86b9dea67fSPeter Grehan 	    dstobj->relasize);
87b9dea67fSPeter Grehan 	for (rela = dstobj->rela;  rela < relalim;  rela++) {
88b9dea67fSPeter Grehan 		void *dstaddr;
89b9dea67fSPeter Grehan 		const Elf_Sym *dstsym;
90b9dea67fSPeter Grehan 		const char *name;
91b9dea67fSPeter Grehan 		size_t size;
92b9dea67fSPeter Grehan 		const void *srcaddr;
93b9dea67fSPeter Grehan 		const Elf_Sym *srcsym = NULL;
948569deafSKonstantin Belousov 		const Obj_Entry *srcobj, *defobj;
958569deafSKonstantin Belousov 		SymLook req;
968569deafSKonstantin Belousov 		int res;
97b9dea67fSPeter Grehan 
98b9dea67fSPeter Grehan 		if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) {
99b9dea67fSPeter Grehan 			continue;
100b9dea67fSPeter Grehan 		}
101b9dea67fSPeter Grehan 
102b9dea67fSPeter Grehan 		dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
103b9dea67fSPeter Grehan 		dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
104b9dea67fSPeter Grehan 		name = dstobj->strtab + dstsym->st_name;
105b9dea67fSPeter Grehan 		size = dstsym->st_size;
1068569deafSKonstantin Belousov 		symlook_init(&req, name);
1078569deafSKonstantin Belousov 		req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
108082f959aSKonstantin Belousov 		req.flags = SYMLOOK_EARLY;
109b9dea67fSPeter Grehan 
1109fee0541SKonstantin Belousov 		for (srcobj = globallist_next(dstobj); srcobj != NULL;
1119fee0541SKonstantin Belousov 		     srcobj = globallist_next(srcobj)) {
1128569deafSKonstantin Belousov 			res = symlook_obj(&req, srcobj);
1138569deafSKonstantin Belousov 			if (res == 0) {
1148569deafSKonstantin Belousov 				srcsym = req.sym_out;
1158569deafSKonstantin Belousov 				defobj = req.defobj_out;
116b9dea67fSPeter Grehan 				break;
117b9dea67fSPeter Grehan 			}
118b9dea67fSPeter Grehan 		}
119b9dea67fSPeter Grehan 
120b9dea67fSPeter Grehan 		if (srcobj == NULL) {
121b9dea67fSPeter Grehan 			_rtld_error("Undefined symbol \"%s\" "
122b9dea67fSPeter Grehan 				    " referenced from COPY"
123b9dea67fSPeter Grehan 				    " relocation in %s", name, dstobj->path);
124b9dea67fSPeter Grehan 			return (-1);
125b9dea67fSPeter Grehan 		}
126b9dea67fSPeter Grehan 
1278569deafSKonstantin Belousov 		srcaddr = (const void *)(defobj->relocbase+srcsym->st_value);
128b9dea67fSPeter Grehan 		memcpy(dstaddr, srcaddr, size);
129b9dea67fSPeter Grehan 		dbg("copy_reloc: src=%p,dst=%p,size=%d\n",srcaddr,dstaddr,size);
130b9dea67fSPeter Grehan 	}
131b9dea67fSPeter Grehan 
132b9dea67fSPeter Grehan 	return (0);
133b9dea67fSPeter Grehan }
134b9dea67fSPeter Grehan 
135b9dea67fSPeter Grehan 
136b9dea67fSPeter Grehan /*
137b9dea67fSPeter Grehan  * Perform early relocation of the run-time linker image
138b9dea67fSPeter Grehan  */
139b9dea67fSPeter Grehan void
reloc_non_plt_self(Elf_Dyn * dynp,Elf_Addr relocbase)140b9dea67fSPeter Grehan reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
141b9dea67fSPeter Grehan {
142a5d5e8ddSPedro F. Giffuni 	const Elf_Rela *rela = NULL, *relalim;
143b9dea67fSPeter Grehan 	Elf_Addr relasz = 0;
144b9dea67fSPeter Grehan 	Elf_Addr *where;
145b9dea67fSPeter Grehan 
146b9dea67fSPeter Grehan 	/*
147b9dea67fSPeter Grehan 	 * Extract the rela/relasz values from the dynamic section
148b9dea67fSPeter Grehan 	 */
149b9dea67fSPeter Grehan 	for (; dynp->d_tag != DT_NULL; dynp++) {
150b9dea67fSPeter Grehan 		switch (dynp->d_tag) {
151b9dea67fSPeter Grehan 		case DT_RELA:
152b9dea67fSPeter Grehan 			rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr);
153b9dea67fSPeter Grehan 			break;
154b9dea67fSPeter Grehan 		case DT_RELASZ:
155b9dea67fSPeter Grehan 			relasz = dynp->d_un.d_val;
156b9dea67fSPeter Grehan 			break;
157b9dea67fSPeter Grehan 		}
158b9dea67fSPeter Grehan 	}
159b9dea67fSPeter Grehan 
160b9dea67fSPeter Grehan 	/*
161b9dea67fSPeter Grehan 	 * Relocate these values
162b9dea67fSPeter Grehan 	 */
163903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *)rela + relasz);
164b9dea67fSPeter Grehan 	for (; rela < relalim; rela++) {
165b9dea67fSPeter Grehan 		where = (Elf_Addr *)(relocbase + rela->r_offset);
166b9dea67fSPeter Grehan 		*where = (Elf_Addr)(relocbase + rela->r_addend);
167b9dea67fSPeter Grehan 	}
168b9dea67fSPeter Grehan }
169b9dea67fSPeter Grehan 
170b9dea67fSPeter Grehan 
171b9dea67fSPeter Grehan /*
172b9dea67fSPeter Grehan  * Relocate a non-PLT object with addend.
173b9dea67fSPeter Grehan  */
174b9dea67fSPeter Grehan static int
reloc_nonplt_object(Obj_Entry * obj_rtld __unused,Obj_Entry * obj,const Elf_Rela * rela,SymCache * cache,int flags,RtldLockState * lockstate)175903e0ffdSAlex Richardson reloc_nonplt_object(Obj_Entry *obj_rtld __unused, Obj_Entry *obj,
176903e0ffdSAlex Richardson     const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate)
177b9dea67fSPeter Grehan {
17857462f8fSBrandon Bergren 	const Elf_Sym	*def = NULL;
179b9dea67fSPeter Grehan 	const Obj_Entry	*defobj;
18057462f8fSBrandon Bergren 	Elf_Addr	*where, symval = 0;
181b9dea67fSPeter Grehan 
18257462f8fSBrandon Bergren 	/*
18357462f8fSBrandon Bergren 	 * First, resolve symbol for relocations which
18457462f8fSBrandon Bergren 	 * reference symbols.
18557462f8fSBrandon Bergren 	 */
186b9dea67fSPeter Grehan 	switch (ELF_R_TYPE(rela->r_info)) {
187b9dea67fSPeter Grehan 
18857462f8fSBrandon Bergren 	case R_PPC_UADDR32:    /* word32 S + A */
18957462f8fSBrandon Bergren 	case R_PPC_ADDR32:
190b9dea67fSPeter Grehan 	case R_PPC_GLOB_DAT:  /* word32 S + A */
19157462f8fSBrandon Bergren 	case R_PPC_DTPMOD32:
19257462f8fSBrandon Bergren 	case R_PPC_TPREL32:
19357462f8fSBrandon Bergren 	case R_PPC_DTPREL32:
194b9dea67fSPeter Grehan 		def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
195082f959aSKonstantin Belousov 		    flags, cache, lockstate);
196b9dea67fSPeter Grehan 		if (def == NULL) {
197b9dea67fSPeter Grehan 			return (-1);
198b9dea67fSPeter Grehan 		}
199b9dea67fSPeter Grehan 
200b9dea67fSPeter Grehan 		/*
20157462f8fSBrandon Bergren 		 * If symbol is IFUNC, only perform relocation
20257462f8fSBrandon Bergren 		 * when caller allowed it by passing
20357462f8fSBrandon Bergren 		 * SYMLOOK_IFUNC flag.  Skip the relocations
20457462f8fSBrandon Bergren 		 * otherwise.
20557462f8fSBrandon Bergren 		 *
20657462f8fSBrandon Bergren 		 * Also error out in case IFUNC relocations
20757462f8fSBrandon Bergren 		 * are specified for TLS, which cannot be
20857462f8fSBrandon Bergren 		 * usefully interpreted.
209b9dea67fSPeter Grehan 		 */
21057462f8fSBrandon Bergren 		if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
21157462f8fSBrandon Bergren 			switch (ELF_R_TYPE(rela->r_info)) {
21257462f8fSBrandon Bergren 			case R_PPC_UADDR32:
21357462f8fSBrandon Bergren 			case R_PPC_ADDR32:
21457462f8fSBrandon Bergren 			case R_PPC_GLOB_DAT:
21557462f8fSBrandon Bergren 				if ((flags & SYMLOOK_IFUNC) == 0) {
21657462f8fSBrandon Bergren 					dbg("Non-PLT reference to IFUNC found!");
21757462f8fSBrandon Bergren 					obj->non_plt_gnu_ifunc = true;
21857462f8fSBrandon Bergren 					return (0);
21957462f8fSBrandon Bergren 				}
22057462f8fSBrandon Bergren 				symval = (Elf_Addr)rtld_resolve_ifunc(
22157462f8fSBrandon Bergren 					defobj, def);
22257462f8fSBrandon Bergren 				break;
22357462f8fSBrandon Bergren 			default:
22457462f8fSBrandon Bergren 				_rtld_error("%s: IFUNC for TLS reloc",
225b9dea67fSPeter Grehan 					obj->path);
226b9dea67fSPeter Grehan 				return (-1);
227b9dea67fSPeter Grehan 			}
22857462f8fSBrandon Bergren 		} else {
22957462f8fSBrandon Bergren 			if ((flags & SYMLOOK_IFUNC) != 0)
23057462f8fSBrandon Bergren 				return (0);
23157462f8fSBrandon Bergren 			symval = (Elf_Addr)defobj->relocbase +
23257462f8fSBrandon Bergren 				def->st_value;
23357462f8fSBrandon Bergren 		}
234b9dea67fSPeter Grehan 		break;
23557462f8fSBrandon Bergren 	default:
23657462f8fSBrandon Bergren 		if ((flags & SYMLOOK_IFUNC) != 0)
23757462f8fSBrandon Bergren 			return (0);
23857462f8fSBrandon Bergren 	}
23957462f8fSBrandon Bergren 	where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
240b9dea67fSPeter Grehan 
24157462f8fSBrandon Bergren 	switch (ELF_R_TYPE(rela->r_info)) {
24257462f8fSBrandon Bergren 	case R_PPC_NONE:
243b9dea67fSPeter Grehan 		break;
24457462f8fSBrandon Bergren 	case R_PPC_UADDR32:
24557462f8fSBrandon Bergren 	case R_PPC_ADDR32:
24657462f8fSBrandon Bergren 	case R_PPC_GLOB_DAT:
24757462f8fSBrandon Bergren 		/* Don't issue write if unnecessary; avoid COW page fault */
24857462f8fSBrandon Bergren 		if (*where != symval + rela->r_addend) {
24957462f8fSBrandon Bergren 			*where = symval + rela->r_addend;
25057462f8fSBrandon Bergren 		}
25157462f8fSBrandon Bergren 		break;
2526c2a9753SSuleiman Souhlal 	case R_PPC_DTPMOD32:
2536c2a9753SSuleiman Souhlal 		*where = (Elf_Addr) defobj->tlsindex;
2546c2a9753SSuleiman Souhlal 		break;
2556c2a9753SSuleiman Souhlal 	case R_PPC_TPREL32:
2566c2a9753SSuleiman Souhlal 		/*
2576c2a9753SSuleiman Souhlal 		 * We lazily allocate offsets for static TLS as we
2586c2a9753SSuleiman Souhlal 		 * see the first relocation that references the
2596c2a9753SSuleiman Souhlal 		 * TLS block. This allows us to support (small
2606c2a9753SSuleiman Souhlal 		 * amounts of) static TLS in dynamically loaded
2616c2a9753SSuleiman Souhlal 		 * modules. If we run out of space, we generate an
2626c2a9753SSuleiman Souhlal 		 * error.
2636c2a9753SSuleiman Souhlal 		 */
264283a4f40SKonstantin Belousov 		if (!defobj->tls_static) {
265903e0ffdSAlex Richardson 			if (!allocate_tls_offset(
266903e0ffdSAlex Richardson 				    __DECONST(Obj_Entry *, defobj))) {
2676c2a9753SSuleiman Souhlal 				_rtld_error("%s: No space available for static "
2686c2a9753SSuleiman Souhlal 				    "Thread Local Storage", obj->path);
2696c2a9753SSuleiman Souhlal 				return (-1);
2706c2a9753SSuleiman Souhlal 			}
2716c2a9753SSuleiman Souhlal 		}
2726c2a9753SSuleiman Souhlal 
2736c2a9753SSuleiman Souhlal 		*(Elf_Addr **)where = *where * sizeof(Elf_Addr)
2746c2a9753SSuleiman Souhlal 		    + (Elf_Addr *)(def->st_value + rela->r_addend
275953cba36SAndreas Tobler 		    + defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE);
2766c2a9753SSuleiman Souhlal 		break;
2776c2a9753SSuleiman Souhlal 	case R_PPC_DTPREL32:
2786c2a9753SSuleiman Souhlal 		*where += (Elf_Addr)(def->st_value + rela->r_addend
2796c2a9753SSuleiman Souhlal 		    - TLS_DTV_OFFSET);
28057462f8fSBrandon Bergren 		break;
28157462f8fSBrandon Bergren 	case R_PPC_RELATIVE: /* word32 B + A */
28257462f8fSBrandon Bergren 		symval = (Elf_Addr)(obj->relocbase + rela->r_addend);
2836c2a9753SSuleiman Souhlal 
28457462f8fSBrandon Bergren 		/* As above, don't issue write unnecessarily */
28557462f8fSBrandon Bergren 		if (*where != symval) {
28657462f8fSBrandon Bergren 			*where = symval;
28757462f8fSBrandon Bergren 		}
28857462f8fSBrandon Bergren 		break;
28957462f8fSBrandon Bergren 	case R_PPC_COPY:
29057462f8fSBrandon Bergren 		/*
29157462f8fSBrandon Bergren 		 * These are deferred until all other relocations
29257462f8fSBrandon Bergren 		 * have been done.  All we do here is make sure
29357462f8fSBrandon Bergren 		 * that the COPY relocation is not in a shared
29457462f8fSBrandon Bergren 		 * library.  They are allowed only in executable
29557462f8fSBrandon Bergren 		 * files.
29657462f8fSBrandon Bergren 		 */
29757462f8fSBrandon Bergren 		if (!obj->mainprog) {
29857462f8fSBrandon Bergren 			_rtld_error("%s: Unexpected R_COPY "
29957462f8fSBrandon Bergren 				    " relocation in shared library",
30057462f8fSBrandon Bergren 				    obj->path);
30157462f8fSBrandon Bergren 			return (-1);
30257462f8fSBrandon Bergren 		}
30357462f8fSBrandon Bergren 		break;
30457462f8fSBrandon Bergren 	case R_PPC_IRELATIVE:
30557462f8fSBrandon Bergren 		/*
30657462f8fSBrandon Bergren 		 * These will be handled by reloc_iresolve().
30757462f8fSBrandon Bergren 		 */
30857462f8fSBrandon Bergren 		obj->irelative = true;
30957462f8fSBrandon Bergren 		break;
31057462f8fSBrandon Bergren 	case R_PPC_JMP_SLOT:
31157462f8fSBrandon Bergren 		/*
31257462f8fSBrandon Bergren 		 * These will be handled by the plt/jmpslot routines
31357462f8fSBrandon Bergren 		 */
3146c2a9753SSuleiman Souhlal 		break;
3156c2a9753SSuleiman Souhlal 
316b9dea67fSPeter Grehan 	default:
317b9dea67fSPeter Grehan 		_rtld_error("%s: Unsupported relocation type %d"
318b9dea67fSPeter Grehan 			    " in non-PLT relocations\n", obj->path,
319b9dea67fSPeter Grehan 			    ELF_R_TYPE(rela->r_info));
320b9dea67fSPeter Grehan 		return (-1);
321b9dea67fSPeter Grehan 	}
322b9dea67fSPeter Grehan 	return (0);
323b9dea67fSPeter Grehan }
324b9dea67fSPeter Grehan 
325b9dea67fSPeter Grehan 
326b9dea67fSPeter Grehan /*
327b9dea67fSPeter Grehan  * Process non-PLT relocations
328b9dea67fSPeter Grehan  */
329b9dea67fSPeter Grehan int
reloc_non_plt(Obj_Entry * obj,Obj_Entry * obj_rtld,int flags,RtldLockState * lockstate)330082f959aSKonstantin Belousov reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
331082f959aSKonstantin Belousov     RtldLockState *lockstate)
332b9dea67fSPeter Grehan {
333b9dea67fSPeter Grehan 	const Elf_Rela *relalim;
334b9dea67fSPeter Grehan 	const Elf_Rela *rela;
335f846c80aSKonstantin Belousov 	const Elf_Phdr *phdr;
336b9dea67fSPeter Grehan 	SymCache *cache;
337b9dea67fSPeter Grehan 	int r = -1;
338b9dea67fSPeter Grehan 
339b9dea67fSPeter Grehan 	/*
340b9dea67fSPeter Grehan 	 * The dynamic loader may be called from a thread, we have
341b9dea67fSPeter Grehan 	 * limited amounts of stack available so we cannot use alloca().
342b9dea67fSPeter Grehan 	 */
34399227f1eSMarcel Moolenaar 	if (obj != obj_rtld) {
344f6265192SKonstantin Belousov 		cache = calloc(obj->dynsymcount, sizeof(SymCache));
3451dfdc15bSRoman Divacky 		/* No need to check for NULL here */
34699227f1eSMarcel Moolenaar 	} else
34799227f1eSMarcel Moolenaar 		cache = NULL;
348b9dea67fSPeter Grehan 
349b9dea67fSPeter Grehan 	/*
350b9dea67fSPeter Grehan 	 * From the SVR4 PPC ABI:
351b9dea67fSPeter Grehan 	 * "The PowerPC family uses only the Elf32_Rela relocation
352b9dea67fSPeter Grehan 	 *  entries with explicit addends."
353b9dea67fSPeter Grehan 	 */
354903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
355b9dea67fSPeter Grehan 	for (rela = obj->rela; rela < relalim; rela++) {
356082f959aSKonstantin Belousov 		if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags,
357082f959aSKonstantin Belousov 		    lockstate) < 0)
358b9dea67fSPeter Grehan 			goto done;
359b9dea67fSPeter Grehan 	}
360b9dea67fSPeter Grehan 	r = 0;
361b9dea67fSPeter Grehan done:
3621dfdc15bSRoman Divacky 	if (cache != NULL)
3631dfdc15bSRoman Divacky 		free(cache);
3644b51c699SNathan Whitehorn 
365f846c80aSKonstantin Belousov 	/*
366f846c80aSKonstantin Belousov 	 * Synchronize icache for executable segments in case we made
367f846c80aSKonstantin Belousov 	 * any changes.
368f846c80aSKonstantin Belousov 	 */
369f846c80aSKonstantin Belousov 	for (phdr = obj->phdr;
370f846c80aSKonstantin Belousov 	    (const char *)phdr < (const char *)obj->phdr + obj->phsize;
371f846c80aSKonstantin Belousov 	    phdr++) {
372f846c80aSKonstantin Belousov 		if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X) != 0) {
373f846c80aSKonstantin Belousov 			__syncicache(obj->relocbase + phdr->p_vaddr,
374f846c80aSKonstantin Belousov 			    phdr->p_memsz);
375f846c80aSKonstantin Belousov 		}
376f846c80aSKonstantin Belousov 	}
3774b51c699SNathan Whitehorn 
378b9dea67fSPeter Grehan 	return (r);
379b9dea67fSPeter Grehan }
380b9dea67fSPeter Grehan 
381b9dea67fSPeter Grehan /*
382b9dea67fSPeter Grehan  * Initialise a PLT slot to the resolving trampoline
383b9dea67fSPeter Grehan  */
384b9dea67fSPeter Grehan static int
reloc_plt_object(Obj_Entry * obj,const Elf_Rela * rela)385b9dea67fSPeter Grehan reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)
386b9dea67fSPeter Grehan {
387b9dea67fSPeter Grehan 	Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
3884f2730f7SNathan Whitehorn 	Elf_Addr *pltresolve, *pltlongresolve, *jmptab;
389b9dea67fSPeter Grehan 	Elf_Addr distance;
3904f2730f7SNathan Whitehorn 	int N = obj->pltrelasize / sizeof(Elf_Rela);
391b9dea67fSPeter Grehan 	int reloff;
392b9dea67fSPeter Grehan 
393b9dea67fSPeter Grehan 	reloff = rela - obj->pltrela;
394b9dea67fSPeter Grehan 
3954f2730f7SNathan Whitehorn 	if (reloff < 0)
396b9dea67fSPeter Grehan 		return (-1);
397b9dea67fSPeter Grehan 
398f62da49bSJustin Hibbits 	if (obj->gotptr != NULL) {
399f62da49bSJustin Hibbits 		*where += (Elf_Addr)obj->relocbase;
400f62da49bSJustin Hibbits 		return (0);
401f62da49bSJustin Hibbits 	}
402f62da49bSJustin Hibbits 
4034f2730f7SNathan Whitehorn 	pltlongresolve = obj->pltgot + 5;
4044f2730f7SNathan Whitehorn 	pltresolve = pltlongresolve + 5;
405b9dea67fSPeter Grehan 
406b9dea67fSPeter Grehan 	distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1);
407b9dea67fSPeter Grehan 
408b9dea67fSPeter Grehan 	dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x",
409b9dea67fSPeter Grehan 	    (void *)where, (void *)pltresolve, reloff, distance);
410b9dea67fSPeter Grehan 
4114f2730f7SNathan Whitehorn 	if (reloff < PLT_EXTENDED_BEGIN) {
412b9dea67fSPeter Grehan 		/* li   r11,reloff  */
413b9dea67fSPeter Grehan 		/* b    pltresolve  */
414b9dea67fSPeter Grehan 		where[0] = 0x39600000 | reloff;
415b9dea67fSPeter Grehan 		where[1] = 0x48000000 | (distance & 0x03fffffc);
4164f2730f7SNathan Whitehorn 	} else {
4174f2730f7SNathan Whitehorn 		jmptab = obj->pltgot + JMPTAB_BASE(N);
4184f2730f7SNathan Whitehorn 		jmptab[reloff] = (u_int)pltlongresolve;
4194f2730f7SNathan Whitehorn 
4204f2730f7SNathan Whitehorn 		/* lis	r11,jmptab[reloff]@ha */
4214f2730f7SNathan Whitehorn 		/* lwzu	r12,jmptab[reloff]@l(r11) */
4224f2730f7SNathan Whitehorn 		/* mtctr r12 */
4234f2730f7SNathan Whitehorn 		/* bctr */
4244f2730f7SNathan Whitehorn 		where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]);
4254f2730f7SNathan Whitehorn 		where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]);
4264f2730f7SNathan Whitehorn 		where[2] = 0x7d8903a6;
4274f2730f7SNathan Whitehorn 		where[3] = 0x4e800420;
4284f2730f7SNathan Whitehorn 	}
4294f2730f7SNathan Whitehorn 
430b9dea67fSPeter Grehan 
431b9dea67fSPeter Grehan 	/*
43217bbcc52SNathan Whitehorn 	 * The icache will be sync'd in reloc_plt, which is called
433b9dea67fSPeter Grehan 	 * after all the slots have been updated
434b9dea67fSPeter Grehan 	 */
435b9dea67fSPeter Grehan 
436b9dea67fSPeter Grehan 	return (0);
437b9dea67fSPeter Grehan }
438b9dea67fSPeter Grehan 
439b9dea67fSPeter Grehan /*
440b9dea67fSPeter Grehan  * Process the PLT relocations.
441b9dea67fSPeter Grehan  */
442b9dea67fSPeter Grehan int
reloc_plt(Obj_Entry * obj,int flags __unused,RtldLockState * lockstate __unused)4434849c3a5SMichal Meloun reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)
444b9dea67fSPeter Grehan {
445b9dea67fSPeter Grehan 	const Elf_Rela *relalim;
446b9dea67fSPeter Grehan 	const Elf_Rela *rela;
44717bbcc52SNathan Whitehorn 	int N = obj->pltrelasize / sizeof(Elf_Rela);
448b9dea67fSPeter Grehan 
449b9dea67fSPeter Grehan 	if (obj->pltrelasize != 0) {
450b9dea67fSPeter Grehan 
451903e0ffdSAlex Richardson 		relalim = (const Elf_Rela *)((const char *)obj->pltrela +
452b9dea67fSPeter Grehan 		    obj->pltrelasize);
453b9dea67fSPeter Grehan 		for (rela = obj->pltrela;  rela < relalim;  rela++) {
45457462f8fSBrandon Bergren 			if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
45557462f8fSBrandon Bergren 				dbg("ABI violation - found IRELATIVE in the PLT.");
45657462f8fSBrandon Bergren 				obj->irelative = true;
45757462f8fSBrandon Bergren 				continue;
45857462f8fSBrandon Bergren 			}
45957462f8fSBrandon Bergren 
46057462f8fSBrandon Bergren 			/*
46157462f8fSBrandon Bergren 			 * PowerPC(64) .rela.plt is composed of an array of
46257462f8fSBrandon Bergren 			 * R_PPC_JMP_SLOT relocations. Unlike other platforms,
46357462f8fSBrandon Bergren 			 * this is the ONLY relocation type that is valid here.
46457462f8fSBrandon Bergren 			 */
465b9dea67fSPeter Grehan 			assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
466b9dea67fSPeter Grehan 
467b9dea67fSPeter Grehan 			if (reloc_plt_object(obj, rela) < 0) {
468b9dea67fSPeter Grehan 				return (-1);
469b9dea67fSPeter Grehan 			}
470b9dea67fSPeter Grehan 		}
471b9dea67fSPeter Grehan 	}
472b9dea67fSPeter Grehan 
47317bbcc52SNathan Whitehorn 	/*
47417bbcc52SNathan Whitehorn 	 * Sync the icache for the byte range represented by the
47517bbcc52SNathan Whitehorn 	 * trampoline routines and call slots.
47617bbcc52SNathan Whitehorn 	 */
477f62da49bSJustin Hibbits 	if (obj->pltgot != NULL && obj->gotptr == NULL)
47817bbcc52SNathan Whitehorn 		__syncicache(obj->pltgot, JMPTAB_BASE(N)*4);
47917bbcc52SNathan Whitehorn 
480b9dea67fSPeter Grehan 	return (0);
481b9dea67fSPeter Grehan }
482b9dea67fSPeter Grehan 
483b9dea67fSPeter Grehan /*
484b9dea67fSPeter Grehan  * LD_BIND_NOW was set - force relocation for all jump slots
485b9dea67fSPeter Grehan  */
486b9dea67fSPeter Grehan int
reloc_jmpslots(Obj_Entry * obj,int flags,RtldLockState * lockstate)487082f959aSKonstantin Belousov reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
488b9dea67fSPeter Grehan {
489b9dea67fSPeter Grehan 	const Obj_Entry *defobj;
490b9dea67fSPeter Grehan 	const Elf_Rela *relalim;
491b9dea67fSPeter Grehan 	const Elf_Rela *rela;
492b9dea67fSPeter Grehan 	const Elf_Sym *def;
493b9dea67fSPeter Grehan 	Elf_Addr *where;
494b9dea67fSPeter Grehan 	Elf_Addr target;
495b9dea67fSPeter Grehan 
496903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *)obj->pltrela +
497903e0ffdSAlex Richardson 	    obj->pltrelasize);
498b9dea67fSPeter Grehan 	for (rela = obj->pltrela; rela < relalim; rela++) {
49957462f8fSBrandon Bergren 		/* This isn't actually a jump slot, ignore it. */
50057462f8fSBrandon Bergren 		if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE)
50157462f8fSBrandon Bergren 			continue;
502b9dea67fSPeter Grehan 		assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
503b9dea67fSPeter Grehan 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
504b9dea67fSPeter Grehan 		def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
505082f959aSKonstantin Belousov 		    SYMLOOK_IN_PLT | flags, NULL, lockstate);
506b9dea67fSPeter Grehan 		if (def == NULL) {
507b9dea67fSPeter Grehan 			dbg("reloc_jmpslots: sym not found");
508b9dea67fSPeter Grehan 			return (-1);
509b9dea67fSPeter Grehan 		}
510b9dea67fSPeter Grehan 
511b9dea67fSPeter Grehan 		target = (Elf_Addr)(defobj->relocbase + def->st_value);
512b9dea67fSPeter Grehan 
51357462f8fSBrandon Bergren 		if (def == &sym_zero) {
51457462f8fSBrandon Bergren 			/* Zero undefined weak symbols */
51557462f8fSBrandon Bergren 			*where = 0;
51657462f8fSBrandon Bergren 		} else {
51757462f8fSBrandon Bergren 			if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
51857462f8fSBrandon Bergren 				/* LD_BIND_NOW, ifunc in shared lib.*/
51957462f8fSBrandon Bergren 				obj->gnu_ifunc = true;
52057462f8fSBrandon Bergren 				continue;
52157462f8fSBrandon Bergren 			}
522b9dea67fSPeter Grehan 			reloc_jmpslot(where, target, defobj, obj,
523b9dea67fSPeter Grehan 			    (const Elf_Rel *) rela);
524b9dea67fSPeter Grehan 		}
52557462f8fSBrandon Bergren 	}
526b9dea67fSPeter Grehan 
527b9dea67fSPeter Grehan 	obj->jmpslots_done = true;
528b9dea67fSPeter Grehan 
529b9dea67fSPeter Grehan 	return (0);
530b9dea67fSPeter Grehan }
531b9dea67fSPeter Grehan 
532b9dea67fSPeter Grehan 
533b9dea67fSPeter Grehan /*
53457462f8fSBrandon Bergren  * Update the value of a PLT jump slot.
535b9dea67fSPeter Grehan  */
536b9dea67fSPeter Grehan Elf_Addr
reloc_jmpslot(Elf_Addr * wherep,Elf_Addr target,const Obj_Entry * defobj __unused,const Obj_Entry * obj,const Elf_Rel * rel)537903e0ffdSAlex Richardson reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target,
538903e0ffdSAlex Richardson     const Obj_Entry *defobj __unused, const Obj_Entry *obj, const Elf_Rel *rel)
539b9dea67fSPeter Grehan {
540b9dea67fSPeter Grehan 	Elf_Addr offset;
541b9dea67fSPeter Grehan 	const Elf_Rela *rela = (const Elf_Rela *) rel;
542b9dea67fSPeter Grehan 
543b9dea67fSPeter Grehan 	dbg(" reloc_jmpslot: where=%p, target=%p",
544b9dea67fSPeter Grehan 	    (void *)wherep, (void *)target);
545b9dea67fSPeter Grehan 
546e35ddbe4SKonstantin Belousov 	if (ld_bind_not)
547e35ddbe4SKonstantin Belousov 		goto out;
548e35ddbe4SKonstantin Belousov 
54957462f8fSBrandon Bergren 
55057462f8fSBrandon Bergren 	/*
55157462f8fSBrandon Bergren 	 * Process Secure-PLT.
55257462f8fSBrandon Bergren 	 */
55357462f8fSBrandon Bergren 	if (obj->gotptr != NULL) {
55457462f8fSBrandon Bergren 		assert(wherep >= (Elf_Word *)obj->pltgot);
55557462f8fSBrandon Bergren 		assert(wherep <
55657462f8fSBrandon Bergren 		    (Elf_Word *)obj->pltgot + obj->pltrelasize);
55757462f8fSBrandon Bergren 		if (*wherep != target)
55857462f8fSBrandon Bergren 			*wherep = target;
55957462f8fSBrandon Bergren 		goto out;
56057462f8fSBrandon Bergren 	}
56157462f8fSBrandon Bergren 
56257462f8fSBrandon Bergren 	/*
56357462f8fSBrandon Bergren 	 * BSS-PLT optimization:
56457462f8fSBrandon Bergren 	 * Branch directly to the target if it is within +/- 32Mb,
56557462f8fSBrandon Bergren 	 * otherwise go indirectly via the pltcall trampoline call and
56657462f8fSBrandon Bergren 	 * jump table.
56757462f8fSBrandon Bergren 	 */
56857462f8fSBrandon Bergren 	offset = target - (Elf_Addr)wherep;
56957462f8fSBrandon Bergren 	if (abs((int)offset) < 32*1024*1024) {     /* inside 32MB? */
570b9dea67fSPeter Grehan 		/*
571b9dea67fSPeter Grehan 		 * At the PLT entry pointed at by `wherep', construct
572b9dea67fSPeter Grehan 		 * a direct transfer to the now fully resolved function
573b9dea67fSPeter Grehan 		 * address.
574b9dea67fSPeter Grehan 		 */
575b9dea67fSPeter Grehan 		/* b    value   # branch directly */
576b9dea67fSPeter Grehan 		*wherep = 0x48000000 | (offset & 0x03fffffc);
577b9dea67fSPeter Grehan 		__syncicache(wherep, 4);
578b9dea67fSPeter Grehan 	} else {
579b9dea67fSPeter Grehan 		Elf_Addr *pltcall, *jmptab;
580b9dea67fSPeter Grehan 		int distance;
581b9dea67fSPeter Grehan 		int N = obj->pltrelasize / sizeof(Elf_Rela);
582b9dea67fSPeter Grehan 		int reloff = rela - obj->pltrela;
583b9dea67fSPeter Grehan 
5844f2730f7SNathan Whitehorn 		if (reloff < 0)
585b9dea67fSPeter Grehan 			return (-1);
586b9dea67fSPeter Grehan 
587b9dea67fSPeter Grehan 		pltcall = obj->pltgot;
588b9dea67fSPeter Grehan 
5894f2730f7SNathan Whitehorn 		dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n",
590b9dea67fSPeter Grehan 		    reloff, N);
591b9dea67fSPeter Grehan 
5924f2730f7SNathan Whitehorn 		jmptab = obj->pltgot + JMPTAB_BASE(N);
593b9dea67fSPeter Grehan 		jmptab[reloff] = target;
594c606eab4SNathan Whitehorn 		mb(); /* Order jmptab update before next changes */
595b9dea67fSPeter Grehan 
5964f2730f7SNathan Whitehorn 		if (reloff < PLT_EXTENDED_BEGIN) {
5974f2730f7SNathan Whitehorn 			/* for extended PLT entries, we keep the old code */
5984f2730f7SNathan Whitehorn 
599b9dea67fSPeter Grehan 			distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1);
600b9dea67fSPeter Grehan 
601b9dea67fSPeter Grehan 			/* li   r11,reloff */
602b9dea67fSPeter Grehan 			/* b    pltcall  # use indirect pltcall routine */
6039d00444dSNathan Whitehorn 
6049d00444dSNathan Whitehorn 			/* first instruction same as before */
605b9dea67fSPeter Grehan 			wherep[1] = 0x48000000 | (distance & 0x03fffffc);
606b9dea67fSPeter Grehan 			__syncicache(wherep, 8);
607b9dea67fSPeter Grehan 		}
6084f2730f7SNathan Whitehorn 	}
609b9dea67fSPeter Grehan 
610e35ddbe4SKonstantin Belousov out:
611b9dea67fSPeter Grehan 	return (target);
612b9dea67fSPeter Grehan }
613b9dea67fSPeter Grehan 
6146be4b697SKonstantin Belousov int
reloc_iresolve(Obj_Entry * obj,struct Struct_RtldLockState * lockstate)61557462f8fSBrandon Bergren reloc_iresolve(Obj_Entry *obj,
61657462f8fSBrandon Bergren     struct Struct_RtldLockState *lockstate)
6176be4b697SKonstantin Belousov {
61857462f8fSBrandon Bergren 	/*
61957462f8fSBrandon Bergren 	 * Since PLT slots on PowerPC are always R_PPC_JMP_SLOT,
62057462f8fSBrandon Bergren 	 * R_PPC_IRELATIVE is in RELA.
62157462f8fSBrandon Bergren 	 */
62257462f8fSBrandon Bergren 	const Elf_Rela *relalim;
62357462f8fSBrandon Bergren 	const Elf_Rela *rela;
62457462f8fSBrandon Bergren 	Elf_Addr *where, target, *ptr;
6256be4b697SKonstantin Belousov 
62657462f8fSBrandon Bergren 	if (!obj->irelative)
62757462f8fSBrandon Bergren 		return (0);
62857462f8fSBrandon Bergren 
62957462f8fSBrandon Bergren 	relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
63057462f8fSBrandon Bergren 	for (rela = obj->rela;  rela < relalim;  rela++) {
63157462f8fSBrandon Bergren 		if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
63257462f8fSBrandon Bergren 			ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
63357462f8fSBrandon Bergren 			where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
63457462f8fSBrandon Bergren 
63557462f8fSBrandon Bergren 			lock_release(rtld_bind_lock, lockstate);
63657462f8fSBrandon Bergren 			target = call_ifunc_resolver(ptr);
63757462f8fSBrandon Bergren 			wlock_acquire(rtld_bind_lock, lockstate);
63857462f8fSBrandon Bergren 
63957462f8fSBrandon Bergren 			*where = target;
64057462f8fSBrandon Bergren 		}
64157462f8fSBrandon Bergren 	}
64257462f8fSBrandon Bergren 	/*
64357462f8fSBrandon Bergren 	 * XXX Remove me when lld is fixed!
64457462f8fSBrandon Bergren 	 * LLD currently makes illegal relocations in the PLT.
64557462f8fSBrandon Bergren 	 */
64657462f8fSBrandon Bergren 	relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
64757462f8fSBrandon Bergren 	for (rela = obj->pltrela;  rela < relalim;  rela++) {
64857462f8fSBrandon Bergren 		if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {
64957462f8fSBrandon Bergren 			ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
65057462f8fSBrandon Bergren 			where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
65157462f8fSBrandon Bergren 
65257462f8fSBrandon Bergren 			lock_release(rtld_bind_lock, lockstate);
65357462f8fSBrandon Bergren 			target = call_ifunc_resolver(ptr);
65457462f8fSBrandon Bergren 			wlock_acquire(rtld_bind_lock, lockstate);
65557462f8fSBrandon Bergren 
65657462f8fSBrandon Bergren 			*where = target;
65757462f8fSBrandon Bergren 		}
65857462f8fSBrandon Bergren 	}
65957462f8fSBrandon Bergren 
66057462f8fSBrandon Bergren 	obj->irelative = false;
6616be4b697SKonstantin Belousov 	return (0);
6626be4b697SKonstantin Belousov }
6636be4b697SKonstantin Belousov 
6646be4b697SKonstantin Belousov int
reloc_iresolve_nonplt(Obj_Entry * obj __unused,struct Struct_RtldLockState * lockstate __unused)665c5ca0d11SKonstantin Belousov reloc_iresolve_nonplt(Obj_Entry *obj __unused,
666c5ca0d11SKonstantin Belousov     struct Struct_RtldLockState *lockstate __unused)
667c5ca0d11SKonstantin Belousov {
668c5ca0d11SKonstantin Belousov 	return (0);
669c5ca0d11SKonstantin Belousov }
670c5ca0d11SKonstantin Belousov 
671c5ca0d11SKonstantin Belousov int
reloc_gnu_ifunc(Obj_Entry * obj __unused,int flags __unused,struct Struct_RtldLockState * lockstate __unused)672903e0ffdSAlex Richardson reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused,
673903e0ffdSAlex Richardson     struct Struct_RtldLockState *lockstate __unused)
6746be4b697SKonstantin Belousov {
67557462f8fSBrandon Bergren 	const Elf_Rela *relalim;
67657462f8fSBrandon Bergren 	const Elf_Rela *rela;
67757462f8fSBrandon Bergren 	Elf_Addr *where, target;
67857462f8fSBrandon Bergren 	const Elf_Sym *def;
67957462f8fSBrandon Bergren 	const Obj_Entry *defobj;
6806be4b697SKonstantin Belousov 
68157462f8fSBrandon Bergren 	if (!obj->gnu_ifunc)
68257462f8fSBrandon Bergren 		return (0);
68357462f8fSBrandon Bergren 	relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
68457462f8fSBrandon Bergren 	for (rela = obj->pltrela;  rela < relalim;  rela++) {
68557462f8fSBrandon Bergren 		if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) {
68657462f8fSBrandon Bergren 			where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
68757462f8fSBrandon Bergren 			def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
68857462f8fSBrandon Bergren 			    SYMLOOK_IN_PLT | flags, NULL, lockstate);
68957462f8fSBrandon Bergren 			if (def == NULL)
69057462f8fSBrandon Bergren 				return (-1);
69157462f8fSBrandon Bergren 			if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
69257462f8fSBrandon Bergren 				continue;
69357462f8fSBrandon Bergren 			lock_release(rtld_bind_lock, lockstate);
69457462f8fSBrandon Bergren 			target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
69557462f8fSBrandon Bergren 			wlock_acquire(rtld_bind_lock, lockstate);
69657462f8fSBrandon Bergren 			reloc_jmpslot(where, target, defobj, obj,
69757462f8fSBrandon Bergren 			    (const Elf_Rel *)rela);
69857462f8fSBrandon Bergren 		}
69957462f8fSBrandon Bergren 	}
70057462f8fSBrandon Bergren 	obj->gnu_ifunc = false;
7016be4b697SKonstantin Belousov 	return (0);
7026be4b697SKonstantin Belousov }
703b9dea67fSPeter Grehan 
704b9dea67fSPeter Grehan /*
705b9dea67fSPeter Grehan  * Setup the plt glue routines.
706b9dea67fSPeter Grehan  */
707b9dea67fSPeter Grehan #define PLTCALL_SIZE	   	20
7084f2730f7SNathan Whitehorn #define PLTLONGRESOLVE_SIZE	20
709b9dea67fSPeter Grehan #define PLTRESOLVE_SIZE		24
710b9dea67fSPeter Grehan 
711b9dea67fSPeter Grehan void
init_pltgot(Obj_Entry * obj)712b9dea67fSPeter Grehan init_pltgot(Obj_Entry *obj)
713b9dea67fSPeter Grehan {
7144f2730f7SNathan Whitehorn 	Elf_Word *pltcall, *pltresolve, *pltlongresolve;
715b9dea67fSPeter Grehan 	Elf_Word *jmptab;
716b9dea67fSPeter Grehan 	int N = obj->pltrelasize / sizeof(Elf_Rela);
717b9dea67fSPeter Grehan 
718b9dea67fSPeter Grehan 	pltcall = obj->pltgot;
719b9dea67fSPeter Grehan 
720b9dea67fSPeter Grehan 	if (pltcall == NULL) {
721b9dea67fSPeter Grehan 		return;
722b9dea67fSPeter Grehan 	}
723b9dea67fSPeter Grehan 
724f62da49bSJustin Hibbits 	/* Handle Secure-PLT first, if applicable. */
725f62da49bSJustin Hibbits 	if (obj->gotptr != NULL) {
726f62da49bSJustin Hibbits 		obj->gotptr[1] = (Elf_Addr)_rtld_bind_secureplt_start;
727f62da49bSJustin Hibbits 		obj->gotptr[2] = (Elf_Addr)obj;
728f62da49bSJustin Hibbits 		dbg("obj %s secure-plt gotptr=%p start=%p obj=%p",
729f62da49bSJustin Hibbits 		    obj->path, obj->gotptr,
730f62da49bSJustin Hibbits 		    (void *)obj->gotptr[1], (void *)obj->gotptr[2]);
731f62da49bSJustin Hibbits 		return;
732f62da49bSJustin Hibbits 	}
733f62da49bSJustin Hibbits 
734b9dea67fSPeter Grehan 	/*
735b9dea67fSPeter Grehan 	 * From the SVR4 PPC ABI:
736b9dea67fSPeter Grehan 	 *
737b9dea67fSPeter Grehan 	 * 'The first 18 words (72 bytes) of the PLT are reserved for
738b9dea67fSPeter Grehan 	 * use by the dynamic linker.
739b9dea67fSPeter Grehan 	 *   ...
740b9dea67fSPeter Grehan 	 * 'If the executable or shared object requires N procedure
741b9dea67fSPeter Grehan 	 *  linkage table entries, the link editor shall reserve 3*N
742b9dea67fSPeter Grehan 	 *  words (12*N bytes) following the 18 reserved words. The
743b9dea67fSPeter Grehan 	 *  first 2*N of these words are the procedure linkage table
744b9dea67fSPeter Grehan 	 *  entries themselves. The static linker directs calls to bytes
745b9dea67fSPeter Grehan 	 *  (72 + (i-1)*8), for i between 1 and N inclusive. The remaining
746b9dea67fSPeter Grehan 	 *  N words (4*N bytes) are reserved for use by the dynamic linker.'
747b9dea67fSPeter Grehan 	 */
748b9dea67fSPeter Grehan 
749b9dea67fSPeter Grehan 	/*
750b9dea67fSPeter Grehan 	 * Copy the absolute-call assembler stub into the first part of
751b9dea67fSPeter Grehan 	 * the reserved PLT area.
752b9dea67fSPeter Grehan 	 */
753b9dea67fSPeter Grehan 	memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE);
754b9dea67fSPeter Grehan 
755b9dea67fSPeter Grehan 	/*
756b9dea67fSPeter Grehan 	 * Determine the address of the jumptable, which is the dyn-linker
757b9dea67fSPeter Grehan 	 * reserved area after the call cells. Write the absolute address
758b9dea67fSPeter Grehan 	 * of the jumptable into the absolute-call assembler code so it
759b9dea67fSPeter Grehan 	 * can determine this address.
760b9dea67fSPeter Grehan 	 */
7614f2730f7SNathan Whitehorn 	jmptab = obj->pltgot + JMPTAB_BASE(N);
762b9dea67fSPeter Grehan 	pltcall[1] |= _ppc_ha(jmptab);	   /* addis 11,11,jmptab@ha */
763b9dea67fSPeter Grehan 	pltcall[2] |= _ppc_la(jmptab);     /* lwz   11,jmptab@l(11) */
764b9dea67fSPeter Grehan 
765b9dea67fSPeter Grehan 	/*
7664f2730f7SNathan Whitehorn 	 * Skip down 20 bytes into the initial reserved area and copy
767b9dea67fSPeter Grehan 	 * in the standard resolving assembler call. Into this assembler,
768b9dea67fSPeter Grehan 	 * insert the absolute address of the _rtld_bind_start routine
769b9dea67fSPeter Grehan 	 * and the address of the relocation object.
7704f2730f7SNathan Whitehorn 	 *
7714f2730f7SNathan Whitehorn 	 * We place pltlongresolve first, so it can fix up its arguments
7724f2730f7SNathan Whitehorn 	 * and then fall through to the regular PLT resolver.
773b9dea67fSPeter Grehan 	 */
7744f2730f7SNathan Whitehorn 	pltlongresolve = obj->pltgot + 5;
775b9dea67fSPeter Grehan 
7764f2730f7SNathan Whitehorn 	memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve,
7774f2730f7SNathan Whitehorn 	    PLTLONGRESOLVE_SIZE);
7784f2730f7SNathan Whitehorn 	pltlongresolve[0] |= _ppc_ha(jmptab);	/* lis	12,jmptab@ha	*/
7794f2730f7SNathan Whitehorn 	pltlongresolve[1] |= _ppc_la(jmptab);	/* addi	12,12,jmptab@l	*/
7804f2730f7SNathan Whitehorn 
7814f2730f7SNathan Whitehorn 	pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t);
782b9dea67fSPeter Grehan 	memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE);
783b9dea67fSPeter Grehan 	pltresolve[0] |= _ppc_ha(_rtld_bind_start);
784b9dea67fSPeter Grehan 	pltresolve[1] |= _ppc_la(_rtld_bind_start);
785b9dea67fSPeter Grehan 	pltresolve[3] |= _ppc_ha(obj);
786b9dea67fSPeter Grehan 	pltresolve[4] |= _ppc_la(obj);
787b9dea67fSPeter Grehan 
788b9dea67fSPeter Grehan 	/*
78917bbcc52SNathan Whitehorn 	 * The icache will be sync'd in reloc_plt, which is called
79017bbcc52SNathan Whitehorn 	 * after all the slots have been updated
791b9dea67fSPeter Grehan 	 */
792b9dea67fSPeter Grehan }
793fca32c74SDoug Rabson 
79457462f8fSBrandon Bergren /*
79557462f8fSBrandon Bergren  * 32 bit cpu feature flag fields.
79657462f8fSBrandon Bergren  */
79757462f8fSBrandon Bergren u_long cpu_features;
79857462f8fSBrandon Bergren u_long cpu_features2;
79957462f8fSBrandon Bergren 
80057462f8fSBrandon Bergren void
powerpc_abi_variant_hook(Elf_Auxinfo ** aux_info)80157462f8fSBrandon Bergren powerpc_abi_variant_hook(Elf_Auxinfo** aux_info)
80257462f8fSBrandon Bergren {
80357462f8fSBrandon Bergren 	/*
80457462f8fSBrandon Bergren 	 * Since aux_info[] is easier to work with than aux, go ahead and
80557462f8fSBrandon Bergren 	 * initialize cpu_features / cpu_features2.
80657462f8fSBrandon Bergren 	 */
80757462f8fSBrandon Bergren 	cpu_features = -1UL;
80857462f8fSBrandon Bergren 	cpu_features2 = -1UL;
80957462f8fSBrandon Bergren 	if (aux_info[AT_HWCAP] != NULL)
81057462f8fSBrandon Bergren 		cpu_features = aux_info[AT_HWCAP]->a_un.a_val;
81157462f8fSBrandon Bergren 	if (aux_info[AT_HWCAP2] != NULL)
81257462f8fSBrandon Bergren 		cpu_features2 = aux_info[AT_HWCAP2]->a_un.a_val;
81357462f8fSBrandon Bergren }
81457462f8fSBrandon Bergren 
815fca32c74SDoug Rabson void
ifunc_init(Elf_Auxinfo * aux_info[__min_size (AT_COUNT)]__unused)816*33658afdSJessica Clarke ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)
8174352999eSKonstantin Belousov {
81841fc6f68SMarius Strobl 
81941fc6f68SMarius Strobl }
82041fc6f68SMarius Strobl 
82141fc6f68SMarius Strobl void
allocate_initial_tls(Obj_Entry * list)822fca32c74SDoug Rabson allocate_initial_tls(Obj_Entry *list)
823fca32c74SDoug Rabson {
824fca32c74SDoug Rabson 
825fca32c74SDoug Rabson 	/*
826fca32c74SDoug Rabson 	* Fix the size of the static TLS block by using the maximum
827fca32c74SDoug Rabson 	* offset allocated so far and adding a bit for dynamic modules to
828fca32c74SDoug Rabson 	* use.
829fca32c74SDoug Rabson 	*/
830fca32c74SDoug Rabson 
83195335dd3SStephen J. Kiernan 	tls_static_space = tls_last_offset + tls_last_size +
83295335dd3SStephen J. Kiernan 	    ld_static_tls_extra;
833fca32c74SDoug Rabson 
8348bcdb144SJohn Baldwin 	_tcb_set(allocate_tls(list, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));
835fca32c74SDoug Rabson }
836fca32c74SDoug Rabson 
837fca32c74SDoug Rabson void*
__tls_get_addr(tls_index * ti)838fca32c74SDoug Rabson __tls_get_addr(tls_index* ti)
839fca32c74SDoug Rabson {
8408bcdb144SJohn Baldwin 	uintptr_t **dtvp;
841fca32c74SDoug Rabson 	char *p;
842fca32c74SDoug Rabson 
8438bcdb144SJohn Baldwin 	dtvp = &_tcb_get()->tcb_dtv;
8448bcdb144SJohn Baldwin 	p = tls_get_addr_common(dtvp, ti->ti_module, ti->ti_offset);
8456c2a9753SSuleiman Souhlal 
8466c2a9753SSuleiman Souhlal 	return (p + TLS_DTV_OFFSET);
847fca32c74SDoug Rabson }
848