xref: /freebsd/libexec/rtld-elf/aarch64/reloc.c (revision 4849c3a5)
1047c6e3aSAndrew Turner /*-
2f5f299c3SEd Maste  * Copyright (c) 2014-2015 The FreeBSD Foundation
3047c6e3aSAndrew Turner  * All rights reserved.
4047c6e3aSAndrew Turner  *
5047c6e3aSAndrew Turner  * Portions of this software were developed by Andrew Turner
6047c6e3aSAndrew Turner  * under sponsorship from the FreeBSD Foundation.
7047c6e3aSAndrew Turner  *
8047c6e3aSAndrew Turner  * Redistribution and use in source and binary forms, with or without
9047c6e3aSAndrew Turner  * modification, are permitted provided that the following conditions
10047c6e3aSAndrew Turner  * are met:
11047c6e3aSAndrew Turner  * 1. Redistributions of source code must retain the above copyright
12047c6e3aSAndrew Turner  *    notice, this list of conditions and the following disclaimer.
13047c6e3aSAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
14047c6e3aSAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
15047c6e3aSAndrew Turner  *    documentation and/or other materials provided with the distribution.
16047c6e3aSAndrew Turner  *
17047c6e3aSAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18047c6e3aSAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19047c6e3aSAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20047c6e3aSAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21047c6e3aSAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22047c6e3aSAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23047c6e3aSAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24047c6e3aSAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25047c6e3aSAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26047c6e3aSAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27047c6e3aSAndrew Turner  * SUCH DAMAGE.
28047c6e3aSAndrew Turner  */
29047c6e3aSAndrew Turner 
30047c6e3aSAndrew Turner #include <sys/cdefs.h>
31047c6e3aSAndrew Turner __FBSDID("$FreeBSD$");
32047c6e3aSAndrew Turner 
33047c6e3aSAndrew Turner #include <sys/types.h>
34047c6e3aSAndrew Turner 
35047c6e3aSAndrew Turner #include <stdlib.h>
36047c6e3aSAndrew Turner 
37047c6e3aSAndrew Turner #include "debug.h"
38047c6e3aSAndrew Turner #include "rtld.h"
39047c6e3aSAndrew Turner #include "rtld_printf.h"
40047c6e3aSAndrew Turner 
41047c6e3aSAndrew Turner /*
42047c6e3aSAndrew Turner  * It is possible for the compiler to emit relocations for unaligned data.
43047c6e3aSAndrew Turner  * We handle this situation with these inlines.
44047c6e3aSAndrew Turner  */
45047c6e3aSAndrew Turner #define	RELOC_ALIGNED_P(x) \
46047c6e3aSAndrew Turner 	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
47047c6e3aSAndrew Turner 
48047c6e3aSAndrew Turner /*
49047c6e3aSAndrew Turner  * This is not the correct prototype, but we only need it for
50047c6e3aSAndrew Turner  * a function pointer to a simple asm function.
51047c6e3aSAndrew Turner  */
52*4849c3a5SMichal Meloun void *_rtld_tlsdesc_static(void *);
53*4849c3a5SMichal Meloun void *_rtld_tlsdesc_undef(void *);
54a97120d6SAndrew Turner void *_rtld_tlsdesc_dynamic(void *);
55a97120d6SAndrew Turner 
56047c6e3aSAndrew Turner void _exit(int);
57047c6e3aSAndrew Turner 
58047c6e3aSAndrew Turner void
59047c6e3aSAndrew Turner init_pltgot(Obj_Entry *obj)
60047c6e3aSAndrew Turner {
61047c6e3aSAndrew Turner 
62047c6e3aSAndrew Turner 	if (obj->pltgot != NULL) {
63047c6e3aSAndrew Turner 		obj->pltgot[1] = (Elf_Addr) obj;
64047c6e3aSAndrew Turner 		obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
65047c6e3aSAndrew Turner 	}
66047c6e3aSAndrew Turner }
67047c6e3aSAndrew Turner 
68047c6e3aSAndrew Turner int
69047c6e3aSAndrew Turner do_copy_relocations(Obj_Entry *dstobj)
70047c6e3aSAndrew Turner {
71047c6e3aSAndrew Turner 	const Obj_Entry *srcobj, *defobj;
72047c6e3aSAndrew Turner 	const Elf_Rela *relalim;
73047c6e3aSAndrew Turner 	const Elf_Rela *rela;
74047c6e3aSAndrew Turner 	const Elf_Sym *srcsym;
75047c6e3aSAndrew Turner 	const Elf_Sym *dstsym;
76047c6e3aSAndrew Turner 	const void *srcaddr;
77047c6e3aSAndrew Turner 	const char *name;
78047c6e3aSAndrew Turner 	void *dstaddr;
79047c6e3aSAndrew Turner 	SymLook req;
80047c6e3aSAndrew Turner 	size_t size;
81047c6e3aSAndrew Turner 	int res;
82047c6e3aSAndrew Turner 
83047c6e3aSAndrew Turner 	/*
84047c6e3aSAndrew Turner 	 * COPY relocs are invalid outside of the main program
85047c6e3aSAndrew Turner 	 */
86047c6e3aSAndrew Turner 	assert(dstobj->mainprog);
87047c6e3aSAndrew Turner 
88903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *)dstobj->rela +
89047c6e3aSAndrew Turner 	    dstobj->relasize);
90047c6e3aSAndrew Turner 	for (rela = dstobj->rela; rela < relalim; rela++) {
91047c6e3aSAndrew Turner 		if (ELF_R_TYPE(rela->r_info) != R_AARCH64_COPY)
92047c6e3aSAndrew Turner 			continue;
93047c6e3aSAndrew Turner 
94047c6e3aSAndrew Turner 		dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
95047c6e3aSAndrew Turner 		dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
96047c6e3aSAndrew Turner 		name = dstobj->strtab + dstsym->st_name;
97047c6e3aSAndrew Turner 		size = dstsym->st_size;
98047c6e3aSAndrew Turner 
99047c6e3aSAndrew Turner 		symlook_init(&req, name);
100047c6e3aSAndrew Turner 		req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
101047c6e3aSAndrew Turner 		req.flags = SYMLOOK_EARLY;
102047c6e3aSAndrew Turner 
1039fee0541SKonstantin Belousov 		for (srcobj = globallist_next(dstobj); srcobj != NULL;
1049fee0541SKonstantin Belousov 		     srcobj = globallist_next(srcobj)) {
105047c6e3aSAndrew Turner 			res = symlook_obj(&req, srcobj);
106047c6e3aSAndrew Turner 			if (res == 0) {
107047c6e3aSAndrew Turner 				srcsym = req.sym_out;
108047c6e3aSAndrew Turner 				defobj = req.defobj_out;
109047c6e3aSAndrew Turner 				break;
110047c6e3aSAndrew Turner 			}
111047c6e3aSAndrew Turner 		}
112047c6e3aSAndrew Turner 		if (srcobj == NULL) {
113e8c479fdSMichal Meloun 			_rtld_error("Undefined symbol \"%s\" referenced from "
114e8c479fdSMichal Meloun 			    "COPY relocation in %s", name, dstobj->path);
115047c6e3aSAndrew Turner 			return (-1);
116047c6e3aSAndrew Turner 		}
117047c6e3aSAndrew Turner 
118047c6e3aSAndrew Turner 		srcaddr = (const void *)(defobj->relocbase + srcsym->st_value);
119047c6e3aSAndrew Turner 		memcpy(dstaddr, srcaddr, size);
120047c6e3aSAndrew Turner 	}
121047c6e3aSAndrew Turner 
122047c6e3aSAndrew Turner 	return (0);
123047c6e3aSAndrew Turner }
124047c6e3aSAndrew Turner 
125a97120d6SAndrew Turner struct tls_data {
126*4849c3a5SMichal Meloun 	Elf_Addr	dtv_gen;
127*4849c3a5SMichal Meloun 	int		tls_index;
128*4849c3a5SMichal Meloun 	Elf_Addr	tls_offs;
129a97120d6SAndrew Turner };
130a97120d6SAndrew Turner 
131*4849c3a5SMichal Meloun static Elf_Addr
132*4849c3a5SMichal Meloun reloc_tlsdesc_alloc(int tlsindex, Elf_Addr tlsoffs)
133a97120d6SAndrew Turner {
134a97120d6SAndrew Turner 	struct tls_data *tlsdesc;
135a97120d6SAndrew Turner 
136a97120d6SAndrew Turner 	tlsdesc = xmalloc(sizeof(struct tls_data));
137*4849c3a5SMichal Meloun 	tlsdesc->dtv_gen = tls_dtv_generation;
138*4849c3a5SMichal Meloun 	tlsdesc->tls_index = tlsindex;
139*4849c3a5SMichal Meloun 	tlsdesc->tls_offs = tlsoffs;
140a97120d6SAndrew Turner 
141*4849c3a5SMichal Meloun 	return ((Elf_Addr)tlsdesc);
142a97120d6SAndrew Turner }
143a97120d6SAndrew Turner 
14402dbdb16SAndrew Turner static void
145*4849c3a5SMichal Meloun reloc_tlsdesc(const Obj_Entry *obj, const Elf_Rela *rela, Elf_Addr *where,
146*4849c3a5SMichal Meloun     int flags, RtldLockState *lockstate)
14702dbdb16SAndrew Turner {
148*4849c3a5SMichal Meloun 	const Elf_Sym *def;
149*4849c3a5SMichal Meloun 	const Obj_Entry *defobj;
150*4849c3a5SMichal Meloun 	Elf_Addr offs;
151*4849c3a5SMichal Meloun 
152*4849c3a5SMichal Meloun 
153*4849c3a5SMichal Meloun 	offs = 0;
154*4849c3a5SMichal Meloun 	if (ELF_R_SYM(rela->r_info) != 0) {
155*4849c3a5SMichal Meloun 		def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags,
156*4849c3a5SMichal Meloun 			    NULL, lockstate);
157*4849c3a5SMichal Meloun 		if (def == NULL)
158*4849c3a5SMichal Meloun 			rtld_die();
159*4849c3a5SMichal Meloun 		offs = def->st_value;
160*4849c3a5SMichal Meloun 		obj = defobj;
161*4849c3a5SMichal Meloun 		if (def->st_shndx == SHN_UNDEF) {
162*4849c3a5SMichal Meloun 			/* Weak undefined thread variable */
163*4849c3a5SMichal Meloun 			where[0] = (Elf_Addr)_rtld_tlsdesc_undef;
164*4849c3a5SMichal Meloun 			where[1] = rela->r_addend;
165*4849c3a5SMichal Meloun 			return;
166*4849c3a5SMichal Meloun 		}
167*4849c3a5SMichal Meloun 	}
168*4849c3a5SMichal Meloun 	offs += rela->r_addend;
169*4849c3a5SMichal Meloun 
170*4849c3a5SMichal Meloun 	if (obj->tlsoffset != 0) {
171*4849c3a5SMichal Meloun 		/* Variable is in initialy allocated TLS segment */
172*4849c3a5SMichal Meloun 		where[0] = (Elf_Addr)_rtld_tlsdesc_static;
173*4849c3a5SMichal Meloun 		where[1] = obj->tlsoffset + offs;
17402dbdb16SAndrew Turner 	} else {
175*4849c3a5SMichal Meloun 		/* TLS offest is unknown at load time, use dynamic resolving */
17602dbdb16SAndrew Turner 		where[0] = (Elf_Addr)_rtld_tlsdesc_dynamic;
177*4849c3a5SMichal Meloun 		where[1] = reloc_tlsdesc_alloc(obj->tlsindex, offs);
17802dbdb16SAndrew Turner 	}
17902dbdb16SAndrew Turner }
18002dbdb16SAndrew Turner 
181047c6e3aSAndrew Turner /*
182047c6e3aSAndrew Turner  * Process the PLT relocations.
183047c6e3aSAndrew Turner  */
184047c6e3aSAndrew Turner int
185*4849c3a5SMichal Meloun reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate)
186047c6e3aSAndrew Turner {
187047c6e3aSAndrew Turner 	const Elf_Rela *relalim;
188047c6e3aSAndrew Turner 	const Elf_Rela *rela;
189047c6e3aSAndrew Turner 
190e8c479fdSMichal Meloun 	relalim = (const Elf_Rela *)((const char *)obj->pltrela +
191e8c479fdSMichal Meloun 	    obj->pltrelasize);
192047c6e3aSAndrew Turner 	for (rela = obj->pltrela; rela < relalim; rela++) {
193047c6e3aSAndrew Turner 		Elf_Addr *where;
194047c6e3aSAndrew Turner 
195047c6e3aSAndrew Turner 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
196047c6e3aSAndrew Turner 
197047c6e3aSAndrew Turner 		switch(ELF_R_TYPE(rela->r_info)) {
198047c6e3aSAndrew Turner 		case R_AARCH64_JUMP_SLOT:
199047c6e3aSAndrew Turner 			*where += (Elf_Addr)obj->relocbase;
200047c6e3aSAndrew Turner 			break;
201047c6e3aSAndrew Turner 		case R_AARCH64_TLSDESC:
202*4849c3a5SMichal Meloun 			reloc_tlsdesc(obj, rela, where, SYMLOOK_IN_PLT | flags,
203*4849c3a5SMichal Meloun 			    lockstate);
204047c6e3aSAndrew Turner 			break;
2056e4fdb5cSAndrew Turner 		case R_AARCH64_IRELATIVE:
2066e4fdb5cSAndrew Turner 			obj->irelative = true;
2076e4fdb5cSAndrew Turner 			break;
20863003c4bSMichal Meloun 		case R_AARCH64_NONE:
20963003c4bSMichal Meloun 			break;
210047c6e3aSAndrew Turner 		default:
211047c6e3aSAndrew Turner 			_rtld_error("Unknown relocation type %u in PLT",
212047c6e3aSAndrew Turner 			    (unsigned int)ELF_R_TYPE(rela->r_info));
213047c6e3aSAndrew Turner 			return (-1);
214047c6e3aSAndrew Turner 		}
215047c6e3aSAndrew Turner 	}
216047c6e3aSAndrew Turner 
217047c6e3aSAndrew Turner 	return (0);
218047c6e3aSAndrew Turner }
219047c6e3aSAndrew Turner 
220047c6e3aSAndrew Turner /*
221047c6e3aSAndrew Turner  * LD_BIND_NOW was set - force relocation for all jump slots
222047c6e3aSAndrew Turner  */
223047c6e3aSAndrew Turner int
224047c6e3aSAndrew Turner reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
225047c6e3aSAndrew Turner {
226047c6e3aSAndrew Turner 	const Obj_Entry *defobj;
227047c6e3aSAndrew Turner 	const Elf_Rela *relalim;
228047c6e3aSAndrew Turner 	const Elf_Rela *rela;
229047c6e3aSAndrew Turner 	const Elf_Sym *def;
230419333b9SMichal Meloun 
231419333b9SMichal Meloun 	if (obj->jmpslots_done)
232419333b9SMichal Meloun 		return (0);
233047c6e3aSAndrew Turner 
234e8c479fdSMichal Meloun 	relalim = (const Elf_Rela *)((const char *)obj->pltrela +
235e8c479fdSMichal Meloun 	    obj->pltrelasize);
236047c6e3aSAndrew Turner 	for (rela = obj->pltrela; rela < relalim; rela++) {
2376e4fdb5cSAndrew Turner 		Elf_Addr *where, target;
238047c6e3aSAndrew Turner 
239a97120d6SAndrew Turner 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
240047c6e3aSAndrew Turner 		switch(ELF_R_TYPE(rela->r_info)) {
241047c6e3aSAndrew Turner 		case R_AARCH64_JUMP_SLOT:
242047c6e3aSAndrew Turner 			def = find_symdef(ELF_R_SYM(rela->r_info), obj,
243047c6e3aSAndrew Turner 			    &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate);
2446e4fdb5cSAndrew Turner 			if (def == NULL)
245047c6e3aSAndrew Turner 				return (-1);
2466e4fdb5cSAndrew Turner 			if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
2476e4fdb5cSAndrew Turner 				obj->gnu_ifunc = true;
2486e4fdb5cSAndrew Turner 				continue;
249047c6e3aSAndrew Turner 			}
2506e4fdb5cSAndrew Turner 			target = (Elf_Addr)(defobj->relocbase + def->st_value);
2516e4fdb5cSAndrew Turner 			reloc_jmpslot(where, target, defobj, obj,
2526e4fdb5cSAndrew Turner 			    (const Elf_Rel *)rela);
253047c6e3aSAndrew Turner 			break;
254047c6e3aSAndrew Turner 		}
255047c6e3aSAndrew Turner 	}
256419333b9SMichal Meloun 	obj->jmpslots_done = true;
257047c6e3aSAndrew Turner 
258047c6e3aSAndrew Turner 	return (0);
259047c6e3aSAndrew Turner }
260047c6e3aSAndrew Turner 
261047c6e3aSAndrew Turner int
262047c6e3aSAndrew Turner reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
263047c6e3aSAndrew Turner {
2646e4fdb5cSAndrew Turner 	const Elf_Rela *relalim;
2656e4fdb5cSAndrew Turner 	const Elf_Rela *rela;
2666e4fdb5cSAndrew Turner 	Elf_Addr *where, target, *ptr;
267047c6e3aSAndrew Turner 
2686e4fdb5cSAndrew Turner 	if (!obj->irelative)
2696e4fdb5cSAndrew Turner 		return (0);
270903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
2716e4fdb5cSAndrew Turner 	for (rela = obj->pltrela;  rela < relalim;  rela++) {
2726e4fdb5cSAndrew Turner 		if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE) {
2736e4fdb5cSAndrew Turner 			ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
2746e4fdb5cSAndrew Turner 			where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
2756e4fdb5cSAndrew Turner 			lock_release(rtld_bind_lock, lockstate);
2766e4fdb5cSAndrew Turner 			target = call_ifunc_resolver(ptr);
2776e4fdb5cSAndrew Turner 			wlock_acquire(rtld_bind_lock, lockstate);
2786e4fdb5cSAndrew Turner 			*where = target;
2796e4fdb5cSAndrew Turner 		}
2806e4fdb5cSAndrew Turner 	}
2816e4fdb5cSAndrew Turner 	obj->irelative = false;
282047c6e3aSAndrew Turner 	return (0);
283047c6e3aSAndrew Turner }
284047c6e3aSAndrew Turner 
285047c6e3aSAndrew Turner int
286047c6e3aSAndrew Turner reloc_gnu_ifunc(Obj_Entry *obj, int flags,
287047c6e3aSAndrew Turner    struct Struct_RtldLockState *lockstate)
288047c6e3aSAndrew Turner {
2896e4fdb5cSAndrew Turner 	const Elf_Rela *relalim;
2906e4fdb5cSAndrew Turner 	const Elf_Rela *rela;
2916e4fdb5cSAndrew Turner 	Elf_Addr *where, target;
2926e4fdb5cSAndrew Turner 	const Elf_Sym *def;
2936e4fdb5cSAndrew Turner 	const Obj_Entry *defobj;
294047c6e3aSAndrew Turner 
2956e4fdb5cSAndrew Turner 	if (!obj->gnu_ifunc)
2966e4fdb5cSAndrew Turner 		return (0);
297903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
2986e4fdb5cSAndrew Turner 	for (rela = obj->pltrela;  rela < relalim;  rela++) {
2996e4fdb5cSAndrew Turner 		if (ELF_R_TYPE(rela->r_info) == R_AARCH64_JUMP_SLOT) {
3006e4fdb5cSAndrew Turner 			where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
3016e4fdb5cSAndrew Turner 			def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
3026e4fdb5cSAndrew Turner 			    SYMLOOK_IN_PLT | flags, NULL, lockstate);
3036e4fdb5cSAndrew Turner 			if (def == NULL)
3046e4fdb5cSAndrew Turner 				return (-1);
3056e4fdb5cSAndrew Turner 			if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
3066e4fdb5cSAndrew Turner 				continue;
3076e4fdb5cSAndrew Turner 			lock_release(rtld_bind_lock, lockstate);
3086e4fdb5cSAndrew Turner 			target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
3096e4fdb5cSAndrew Turner 			wlock_acquire(rtld_bind_lock, lockstate);
3106e4fdb5cSAndrew Turner 			reloc_jmpslot(where, target, defobj, obj,
3116e4fdb5cSAndrew Turner 			    (const Elf_Rel *)rela);
3126e4fdb5cSAndrew Turner 		}
3136e4fdb5cSAndrew Turner 	}
3146e4fdb5cSAndrew Turner 	obj->gnu_ifunc = false;
315047c6e3aSAndrew Turner 	return (0);
316047c6e3aSAndrew Turner }
317047c6e3aSAndrew Turner 
318047c6e3aSAndrew Turner Elf_Addr
319903e0ffdSAlex Richardson reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
320903e0ffdSAlex Richardson     const Obj_Entry *defobj __unused, const Obj_Entry *obj __unused,
321903e0ffdSAlex Richardson     const Elf_Rel *rel)
322047c6e3aSAndrew Turner {
323047c6e3aSAndrew Turner 
3246e4fdb5cSAndrew Turner 	assert(ELF_R_TYPE(rel->r_info) == R_AARCH64_JUMP_SLOT ||
3256e4fdb5cSAndrew Turner 	    ELF_R_TYPE(rel->r_info) == R_AARCH64_IRELATIVE);
326047c6e3aSAndrew Turner 
327e35ddbe4SKonstantin Belousov 	if (*where != target && !ld_bind_not)
328047c6e3aSAndrew Turner 		*where = target;
329e35ddbe4SKonstantin Belousov 	return (target);
330047c6e3aSAndrew Turner }
331047c6e3aSAndrew Turner 
3324352999eSKonstantin Belousov void
333d27078f9SKonstantin Belousov ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused)
3344352999eSKonstantin Belousov {
33541fc6f68SMarius Strobl 
33641fc6f68SMarius Strobl }
33741fc6f68SMarius Strobl 
33841fc6f68SMarius Strobl void
33941fc6f68SMarius Strobl pre_init(void)
34041fc6f68SMarius Strobl {
34141fc6f68SMarius Strobl 
3424352999eSKonstantin Belousov }
3434352999eSKonstantin Belousov 
344047c6e3aSAndrew Turner /*
345047c6e3aSAndrew Turner  * Process non-PLT relocations
346047c6e3aSAndrew Turner  */
347047c6e3aSAndrew Turner int
348047c6e3aSAndrew Turner reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
349047c6e3aSAndrew Turner     RtldLockState *lockstate)
350047c6e3aSAndrew Turner {
351047c6e3aSAndrew Turner 	const Obj_Entry *defobj;
352047c6e3aSAndrew Turner 	const Elf_Rela *relalim;
353047c6e3aSAndrew Turner 	const Elf_Rela *rela;
354047c6e3aSAndrew Turner 	const Elf_Sym *def;
355047c6e3aSAndrew Turner 	SymCache *cache;
3566e4fdb5cSAndrew Turner 	Elf_Addr *where, symval;
357047c6e3aSAndrew Turner 
358047c6e3aSAndrew Turner 	/*
359047c6e3aSAndrew Turner 	 * The dynamic loader may be called from a thread, we have
360047c6e3aSAndrew Turner 	 * limited amounts of stack available so we cannot use alloca().
361047c6e3aSAndrew Turner 	 */
362047c6e3aSAndrew Turner 	if (obj == obj_rtld)
363047c6e3aSAndrew Turner 		cache = NULL;
364047c6e3aSAndrew Turner 	else
365047c6e3aSAndrew Turner 		cache = calloc(obj->dynsymcount, sizeof(SymCache));
366047c6e3aSAndrew Turner 		/* No need to check for NULL here */
367047c6e3aSAndrew Turner 
368903e0ffdSAlex Richardson 	relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
369047c6e3aSAndrew Turner 	for (rela = obj->rela; rela < relalim; rela++) {
3706e4fdb5cSAndrew Turner 		/*
3716e4fdb5cSAndrew Turner 		 * First, resolve symbol for relocations which
3726e4fdb5cSAndrew Turner 		 * reference symbols.
3736e4fdb5cSAndrew Turner 		 */
3746e4fdb5cSAndrew Turner 		switch (ELF_R_TYPE(rela->r_info)) {
3756e4fdb5cSAndrew Turner 		case R_AARCH64_ABS64:
3766e4fdb5cSAndrew Turner 		case R_AARCH64_GLOB_DAT:
3776e4fdb5cSAndrew Turner 		case R_AARCH64_TLS_TPREL64:
37863003c4bSMichal Meloun 		case R_AARCH64_TLS_DTPREL64:
37963003c4bSMichal Meloun 		case R_AARCH64_TLS_DTPMOD64:
3806e4fdb5cSAndrew Turner 			def = find_symdef(ELF_R_SYM(rela->r_info), obj,
3816e4fdb5cSAndrew Turner 			    &defobj, flags, cache, lockstate);
3826e4fdb5cSAndrew Turner 			if (def == NULL)
3836e4fdb5cSAndrew Turner 				return (-1);
3846e4fdb5cSAndrew Turner 			/*
3856e4fdb5cSAndrew Turner 			 * If symbol is IFUNC, only perform relocation
3866e4fdb5cSAndrew Turner 			 * when caller allowed it by passing
3876e4fdb5cSAndrew Turner 			 * SYMLOOK_IFUNC flag.  Skip the relocations
3886e4fdb5cSAndrew Turner 			 * otherwise.
3896e4fdb5cSAndrew Turner 			 *
3906e4fdb5cSAndrew Turner 			 * Also error out in case IFUNC relocations
3916e4fdb5cSAndrew Turner 			 * are specified for TLS, which cannot be
3926e4fdb5cSAndrew Turner 			 * usefully interpreted.
3936e4fdb5cSAndrew Turner 			 */
3946e4fdb5cSAndrew Turner 			if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
3956e4fdb5cSAndrew Turner 				switch (ELF_R_TYPE(rela->r_info)) {
3966e4fdb5cSAndrew Turner 				case R_AARCH64_ABS64:
3976e4fdb5cSAndrew Turner 				case R_AARCH64_GLOB_DAT:
3986e4fdb5cSAndrew Turner 					if ((flags & SYMLOOK_IFUNC) == 0) {
3996e4fdb5cSAndrew Turner 						obj->non_plt_gnu_ifunc = true;
4006e4fdb5cSAndrew Turner 						continue;
4016e4fdb5cSAndrew Turner 					}
4026e4fdb5cSAndrew Turner 					symval = (Elf_Addr)rtld_resolve_ifunc(
4036e4fdb5cSAndrew Turner 					    defobj, def);
4046e4fdb5cSAndrew Turner 					break;
4056e4fdb5cSAndrew Turner 				default:
4066e4fdb5cSAndrew Turner 					_rtld_error("%s: IFUNC for TLS reloc",
4076e4fdb5cSAndrew Turner 					    obj->path);
4086e4fdb5cSAndrew Turner 					return (-1);
4096e4fdb5cSAndrew Turner 				}
4106e4fdb5cSAndrew Turner 			} else {
4116e4fdb5cSAndrew Turner 				if ((flags & SYMLOOK_IFUNC) != 0)
4126e4fdb5cSAndrew Turner 					continue;
4136e4fdb5cSAndrew Turner 				symval = (Elf_Addr)defobj->relocbase +
4146e4fdb5cSAndrew Turner 				    def->st_value;
4156e4fdb5cSAndrew Turner 			}
4166e4fdb5cSAndrew Turner 			break;
4176e4fdb5cSAndrew Turner 		default:
4186e4fdb5cSAndrew Turner 			if ((flags & SYMLOOK_IFUNC) != 0)
4196e4fdb5cSAndrew Turner 				continue;
4206e4fdb5cSAndrew Turner 		}
4216e4fdb5cSAndrew Turner 
422047c6e3aSAndrew Turner 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
423047c6e3aSAndrew Turner 
424047c6e3aSAndrew Turner 		switch (ELF_R_TYPE(rela->r_info)) {
425047c6e3aSAndrew Turner 		case R_AARCH64_ABS64:
426047c6e3aSAndrew Turner 		case R_AARCH64_GLOB_DAT:
4276e4fdb5cSAndrew Turner 			*where = symval + rela->r_addend;
428047c6e3aSAndrew Turner 			break;
429047c6e3aSAndrew Turner 		case R_AARCH64_COPY:
430047c6e3aSAndrew Turner 			/*
431047c6e3aSAndrew Turner 			 * These are deferred until all other relocations have
432047c6e3aSAndrew Turner 			 * been done. All we do here is make sure that the
433047c6e3aSAndrew Turner 			 * COPY relocation is not in a shared library. They
434047c6e3aSAndrew Turner 			 * are allowed only in executable files.
435047c6e3aSAndrew Turner 			 */
436047c6e3aSAndrew Turner 			if (!obj->mainprog) {
437047c6e3aSAndrew Turner 				_rtld_error("%s: Unexpected R_AARCH64_COPY "
438047c6e3aSAndrew Turner 				    "relocation in shared library", obj->path);
439047c6e3aSAndrew Turner 				return (-1);
440047c6e3aSAndrew Turner 			}
441047c6e3aSAndrew Turner 			break;
44202dbdb16SAndrew Turner 		case R_AARCH64_TLSDESC:
443*4849c3a5SMichal Meloun 			reloc_tlsdesc(obj, rela, where, flags, lockstate);
44402dbdb16SAndrew Turner 			break;
445a97120d6SAndrew Turner 		case R_AARCH64_TLS_TPREL64:
446a97120d6SAndrew Turner 			/*
447a97120d6SAndrew Turner 			 * We lazily allocate offsets for static TLS as we
448a97120d6SAndrew Turner 			 * see the first relocation that references the
449a97120d6SAndrew Turner 			 * TLS block. This allows us to support (small
450a97120d6SAndrew Turner 			 * amounts of) static TLS in dynamically loaded
451a97120d6SAndrew Turner 			 * modules. If we run out of space, we generate an
452a97120d6SAndrew Turner 			 * error.
453a97120d6SAndrew Turner 			 */
454a97120d6SAndrew Turner 			if (!defobj->tls_done) {
455903e0ffdSAlex Richardson 				if (!allocate_tls_offset(
456903e0ffdSAlex Richardson 				    __DECONST(Obj_Entry *, defobj))) {
457a97120d6SAndrew Turner 					_rtld_error(
458a97120d6SAndrew Turner 					    "%s: No space available for static "
459a97120d6SAndrew Turner 					    "Thread Local Storage", obj->path);
460a97120d6SAndrew Turner 					return (-1);
461a97120d6SAndrew Turner 				}
462a97120d6SAndrew Turner 			}
463*4849c3a5SMichal Meloun 			/* Test weak undefined thread variable */
464*4849c3a5SMichal Meloun 			if (def->st_shndx != SHN_UNDEF) {
465a97120d6SAndrew Turner 				*where = def->st_value + rela->r_addend +
4667c812942SAndrew Turner 				    defobj->tlsoffset;
467*4849c3a5SMichal Meloun 			} else {
468*4849c3a5SMichal Meloun 				/*
469*4849c3a5SMichal Meloun 				 * XXX We should relocate undefined thread
470*4849c3a5SMichal Meloun 				 * weak variable address to NULL, but how?
471*4849c3a5SMichal Meloun 				 * Can we return error in this situation?
472*4849c3a5SMichal Meloun 				 */
473*4849c3a5SMichal Meloun 				rtld_printf("%s: Unable to relocate undefined "
474*4849c3a5SMichal Meloun 				"weak TLS variable\n", obj->path);
475*4849c3a5SMichal Meloun #if 0
476*4849c3a5SMichal Meloun 				return (-1);
477*4849c3a5SMichal Meloun #else
478*4849c3a5SMichal Meloun 				*where = def->st_value + rela->r_addend +
479*4849c3a5SMichal Meloun 				    defobj->tlsoffset;
480*4849c3a5SMichal Meloun #endif
481*4849c3a5SMichal Meloun 			}
482a97120d6SAndrew Turner 			break;
48363003c4bSMichal Meloun 
48463003c4bSMichal Meloun 		/*
48563003c4bSMichal Meloun 		 * !!! BEWARE !!!
48663003c4bSMichal Meloun 		 * ARM ELF ABI defines TLS_DTPMOD64 as 1029, and TLS_DTPREL64
48763003c4bSMichal Meloun 		 * as 1028. But actual bfd linker and the glibc RTLD linker
48863003c4bSMichal Meloun 		 * treats TLS_DTPMOD64 as 1028 and TLS_DTPREL64 1029.
48963003c4bSMichal Meloun 		 */
49063003c4bSMichal Meloun 		case R_AARCH64_TLS_DTPREL64: /* efectively is TLS_DTPMOD64 */
49163003c4bSMichal Meloun 			*where += (Elf_Addr)defobj->tlsindex;
49263003c4bSMichal Meloun 			break;
49363003c4bSMichal Meloun 		case R_AARCH64_TLS_DTPMOD64: /* efectively is TLS_DTPREL64 */
49463003c4bSMichal Meloun 			*where += (Elf_Addr)(def->st_value + rela->r_addend);
49563003c4bSMichal Meloun 			break;
496047c6e3aSAndrew Turner 		case R_AARCH64_RELATIVE:
497047c6e3aSAndrew Turner 			*where = (Elf_Addr)(obj->relocbase + rela->r_addend);
498047c6e3aSAndrew Turner 			break;
49963003c4bSMichal Meloun 		case R_AARCH64_NONE:
50063003c4bSMichal Meloun 			break;
501047c6e3aSAndrew Turner 		default:
502047c6e3aSAndrew Turner 			rtld_printf("%s: Unhandled relocation %lu\n",
503047c6e3aSAndrew Turner 			    obj->path, ELF_R_TYPE(rela->r_info));
504047c6e3aSAndrew Turner 			return (-1);
505047c6e3aSAndrew Turner 		}
506047c6e3aSAndrew Turner 	}
507047c6e3aSAndrew Turner 
508047c6e3aSAndrew Turner 	return (0);
509047c6e3aSAndrew Turner }
510047c6e3aSAndrew Turner 
511047c6e3aSAndrew Turner void
512047c6e3aSAndrew Turner allocate_initial_tls(Obj_Entry *objs)
513047c6e3aSAndrew Turner {
514047c6e3aSAndrew Turner 	Elf_Addr **tp;
515047c6e3aSAndrew Turner 
516047c6e3aSAndrew Turner 	/*
517047c6e3aSAndrew Turner 	* Fix the size of the static TLS block by using the maximum
518047c6e3aSAndrew Turner 	* offset allocated so far and adding a bit for dynamic modules to
519047c6e3aSAndrew Turner 	* use.
520047c6e3aSAndrew Turner 	*/
521047c6e3aSAndrew Turner 	tls_static_space = tls_last_offset + tls_last_size +
522047c6e3aSAndrew Turner 	    RTLD_STATIC_TLS_EXTRA;
523047c6e3aSAndrew Turner 
524047c6e3aSAndrew Turner 	tp = (Elf_Addr **) allocate_tls(objs, NULL, TLS_TCB_SIZE, 16);
525047c6e3aSAndrew Turner 
526047c6e3aSAndrew Turner 	asm volatile("msr	tpidr_el0, %0" : : "r"(tp));
527047c6e3aSAndrew Turner }
52822e9ff95SMichal Meloun 
52922e9ff95SMichal Meloun void *
53022e9ff95SMichal Meloun __tls_get_addr(tls_index* ti)
53122e9ff95SMichal Meloun {
53222e9ff95SMichal Meloun       char *p;
53322e9ff95SMichal Meloun       void *_tp;
53422e9ff95SMichal Meloun 
53522e9ff95SMichal Meloun       __asm __volatile("mrs	%0, tpidr_el0"  : "=r" (_tp));
53622e9ff95SMichal Meloun       p = tls_get_addr_common((Elf_Addr **)(_tp), ti->ti_module, ti->ti_offset);
53722e9ff95SMichal Meloun 
53822e9ff95SMichal Meloun       return (p);
53922e9ff95SMichal Meloun }
540