16e8d4c1andrew/*-
2f6c3ac5emaste * Copyright (c) 2014-2015 The FreeBSD Foundation
36e8d4c1andrew * All rights reserved.
46e8d4c1andrew *
56e8d4c1andrew * Portions of this software were developed by Andrew Turner
66e8d4c1andrew * under sponsorship from the FreeBSD Foundation.
76e8d4c1andrew *
86e8d4c1andrew * Redistribution and use in source and binary forms, with or without
96e8d4c1andrew * modification, are permitted provided that the following conditions
106e8d4c1andrew * are met:
116e8d4c1andrew * 1. Redistributions of source code must retain the above copyright
126e8d4c1andrew *    notice, this list of conditions and the following disclaimer.
136e8d4c1andrew * 2. Redistributions in binary form must reproduce the above copyright
146e8d4c1andrew *    notice, this list of conditions and the following disclaimer in the
156e8d4c1andrew *    documentation and/or other materials provided with the distribution.
166e8d4c1andrew *
176e8d4c1andrew * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
186e8d4c1andrew * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
196e8d4c1andrew * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
206e8d4c1andrew * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
216e8d4c1andrew * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
226e8d4c1andrew * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
236e8d4c1andrew * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
246e8d4c1andrew * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
256e8d4c1andrew * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
266e8d4c1andrew * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
276e8d4c1andrew * SUCH DAMAGE.
286e8d4c1andrew */
296e8d4c1andrew
306e8d4c1andrew#include <sys/cdefs.h>
316e8d4c1andrew__FBSDID("$FreeBSD$");
326e8d4c1andrew
336e8d4c1andrew#include <sys/types.h>
346e8d4c1andrew
356e8d4c1andrew#include <stdlib.h>
366e8d4c1andrew
376e8d4c1andrew#include "debug.h"
386e8d4c1andrew#include "rtld.h"
396e8d4c1andrew#include "rtld_printf.h"
406e8d4c1andrew
416e8d4c1andrew/*
426e8d4c1andrew * It is possible for the compiler to emit relocations for unaligned data.
436e8d4c1andrew * We handle this situation with these inlines.
446e8d4c1andrew */
456e8d4c1andrew#define	RELOC_ALIGNED_P(x) \
466e8d4c1andrew	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
476e8d4c1andrew
486e8d4c1andrew/*
496e8d4c1andrew * This is not the correct prototype, but we only need it for
506e8d4c1andrew * a function pointer to a simple asm function.
516e8d4c1andrew */
524a03477mmelvoid *_rtld_tlsdesc_static(void *);
534a03477mmelvoid *_rtld_tlsdesc_undef(void *);
54ff48071andrewvoid *_rtld_tlsdesc_dynamic(void *);
55ff48071andrew
566e8d4c1andrewvoid _exit(int);
576e8d4c1andrew
586e8d4c1andrewvoid
596e8d4c1andrewinit_pltgot(Obj_Entry *obj)
606e8d4c1andrew{
616e8d4c1andrew
626e8d4c1andrew	if (obj->pltgot != NULL) {
636e8d4c1andrew		obj->pltgot[1] = (Elf_Addr) obj;
646e8d4c1andrew		obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
656e8d4c1andrew	}
666e8d4c1andrew}
676e8d4c1andrew
686e8d4c1andrewint
696e8d4c1andrewdo_copy_relocations(Obj_Entry *dstobj)
706e8d4c1andrew{
716e8d4c1andrew	const Obj_Entry *srcobj, *defobj;
726e8d4c1andrew	const Elf_Rela *relalim;
736e8d4c1andrew	const Elf_Rela *rela;
746e8d4c1andrew	const Elf_Sym *srcsym;
756e8d4c1andrew	const Elf_Sym *dstsym;
766e8d4c1andrew	const void *srcaddr;
776e8d4c1andrew	const char *name;
786e8d4c1andrew	void *dstaddr;
796e8d4c1andrew	SymLook req;
806e8d4c1andrew	size_t size;
816e8d4c1andrew	int res;
826e8d4c1andrew
836e8d4c1andrew	/*
846e8d4c1andrew	 * COPY relocs are invalid outside of the main program
856e8d4c1andrew	 */
866e8d4c1andrew	assert(dstobj->mainprog);
876e8d4c1andrew
88b885decarichardson	relalim = (const Elf_Rela *)((const char *)dstobj->rela +
896e8d4c1andrew	    dstobj->relasize);
906e8d4c1andrew	for (rela = dstobj->rela; rela < relalim; rela++) {
916e8d4c1andrew		if (ELF_R_TYPE(rela->r_info) != R_AARCH64_COPY)
926e8d4c1andrew			continue;
936e8d4c1andrew
946e8d4c1andrew		dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
956e8d4c1andrew		dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
966e8d4c1andrew		name = dstobj->strtab + dstsym->st_name;
976e8d4c1andrew		size = dstsym->st_size;
986e8d4c1andrew
996e8d4c1andrew		symlook_init(&req, name);
1006e8d4c1andrew		req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
1016e8d4c1andrew		req.flags = SYMLOOK_EARLY;
1026e8d4c1andrew
1035f1c6d1kib		for (srcobj = globallist_next(dstobj); srcobj != NULL;
1045f1c6d1kib		     srcobj = globallist_next(srcobj)) {
1056e8d4c1andrew			res = symlook_obj(&req, srcobj);
1066e8d4c1andrew			if (res == 0) {
1076e8d4c1andrew				srcsym = req.sym_out;
1086e8d4c1andrew				defobj = req.defobj_out;
1096e8d4c1andrew				break;
1106e8d4c1andrew			}
1116e8d4c1andrew		}
1126e8d4c1andrew		if (srcobj == NULL) {
11390d0da9mmel			_rtld_error("Undefined symbol \"%s\" referenced from "
11490d0da9mmel			    "COPY relocation in %s", name, dstobj->path);
1156e8d4c1andrew			return (-1);
1166e8d4c1andrew		}
1176e8d4c1andrew
1186e8d4c1andrew		srcaddr = (const void *)(defobj->relocbase + srcsym->st_value);
1196e8d4c1andrew		memcpy(dstaddr, srcaddr, size);
1206e8d4c1andrew	}
1216e8d4c1andrew
1226e8d4c1andrew	return (0);
1236e8d4c1andrew}
1246e8d4c1andrew
125ff48071andrewstruct tls_data {
1264a03477mmel	Elf_Addr	dtv_gen;
1274a03477mmel	int		tls_index;
1284a03477mmel	Elf_Addr	tls_offs;
129ff48071andrew};
130ff48071andrew
1314a03477mmelstatic Elf_Addr
1324a03477mmelreloc_tlsdesc_alloc(int tlsindex, Elf_Addr tlsoffs)
133ff48071andrew{
134ff48071andrew	struct tls_data *tlsdesc;
135ff48071andrew
136ff48071andrew	tlsdesc = xmalloc(sizeof(struct tls_data));
1374a03477mmel	tlsdesc->dtv_gen = tls_dtv_generation;
1384a03477mmel	tlsdesc->tls_index = tlsindex;
1394a03477mmel	tlsdesc->tls_offs = tlsoffs;
140ff48071andrew
1414a03477mmel	return ((Elf_Addr)tlsdesc);
142ff48071andrew}
143ff48071andrew
1444a03477mmelstatic void
1454a03477mmelreloc_tlsdesc(const Obj_Entry *obj, const Elf_Rela *rela, Elf_Addr *where,
1464a03477mmel    int flags, RtldLockState *lockstate)
147ff48071andrew{
148ff48071andrew	const Elf_Sym *def;
149ff48071andrew	const Obj_Entry *defobj;
1504a03477mmel	Elf_Addr offs;
1514a03477mmel
1524a03477mmel
1534a03477mmel	offs = 0;
1544a03477mmel	if (ELF_R_SYM(rela->r_info) != 0) {
1554a03477mmel		def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags,
1564a03477mmel			    NULL, lockstate);
1574a03477mmel		if (def == NULL)
1584a03477mmel			rtld_die();
1594a03477mmel		offs = def->st_value;
1604a03477mmel		obj = defobj;
1614a03477mmel		if (def->st_shndx == SHN_UNDEF) {
1624a03477mmel			/* Weak undefined thread variable */
1634a03477mmel			where[0] = (Elf_Addr)_rtld_tlsdesc_undef;
1644a03477mmel			where[1] = rela->r_addend;
1654a03477mmel			return;
1664a03477mmel		}
1674a03477mmel	}
1684a03477mmel	offs += rela->r_addend;
169ff48071andrew
1704a03477mmel	if (obj->tlsoffset != 0) {
1714a03477mmel		/* Variable is in initialy allocated TLS segment */
1724a03477mmel		where[0] = (Elf_Addr)_rtld_tlsdesc_static;
1734a03477mmel		where[1] = obj->tlsoffset + offs;
1749ce2f86andrew	} else {
1754a03477mmel		/* TLS offest is unknown at load time, use dynamic resolving */
1769ce2f86andrew		where[0] = (Elf_Addr)_rtld_tlsdesc_dynamic;
1774a03477mmel		where[1] = reloc_tlsdesc_alloc(obj->tlsindex, offs);
1789ce2f86andrew	}
1799ce2f86andrew}
1809ce2f86andrew
1816e8d4c1andrew/*
1826e8d4c1andrew * Process the PLT relocations.
1836e8d4c1andrew */
1846e8d4c1andrewint
1854a03477mmelreloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate)
1866e8d4c1andrew{
1876e8d4c1andrew	const Elf_Rela *relalim;
1886e8d4c1andrew	const Elf_Rela *rela;
1896e8d4c1andrew
19090d0da9mmel	relalim = (const Elf_Rela *)((const char *)obj->pltrela +
19190d0da9mmel	    obj->pltrelasize);
1926e8d4c1andrew	for (rela = obj->pltrela; rela < relalim; rela++) {
1936e8d4c1andrew		Elf_Addr *where;
1946e8d4c1andrew
1956e8d4c1andrew		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
1966e8d4c1andrew
1976e8d4c1andrew		switch(ELF_R_TYPE(rela->r_info)) {
1986e8d4c1andrew		case R_AARCH64_JUMP_SLOT:
1996e8d4c1andrew			*where += (Elf_Addr)obj->relocbase;
2006e8d4c1andrew			break;
2016e8d4c1andrew		case R_AARCH64_TLSDESC:
2024a03477mmel			reloc_tlsdesc(obj, rela, where, SYMLOOK_IN_PLT | flags,
2034a03477mmel			    lockstate);
2046e8d4c1andrew			break;
205c148541andrew		case R_AARCH64_IRELATIVE:
206c148541andrew			obj->irelative = true;
207c148541andrew			break;
2087376378mmel		case R_AARCH64_NONE:
2097376378mmel			break;
2106e8d4c1andrew		default:
2116e8d4c1andrew			_rtld_error("Unknown relocation type %u in PLT",
2126e8d4c1andrew			    (unsigned int)ELF_R_TYPE(rela->r_info));
2136e8d4c1andrew			return (-1);
2146e8d4c1andrew		}
2156e8d4c1andrew	}
2166e8d4c1andrew
2176e8d4c1andrew	return (0);
2186e8d4c1andrew}
2196e8d4c1andrew
2206e8d4c1andrew/*
2216e8d4c1andrew * LD_BIND_NOW was set - force relocation for all jump slots
2226e8d4c1andrew */
2236e8d4c1andrewint
2246e8d4c1andrewreloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
2256e8d4c1andrew{
2266e8d4c1andrew	const Obj_Entry *defobj;
2276e8d4c1andrew	const Elf_Rela *relalim;
2286e8d4c1andrew	const Elf_Rela *rela;
2296e8d4c1andrew	const Elf_Sym *def;
2300275a96mmel
2310275a96mmel	if (obj->jmpslots_done)
2320275a96mmel		return (0);
2336e8d4c1andrew
23490d0da9mmel	relalim = (const Elf_Rela *)((const char *)obj->pltrela +
23590d0da9mmel	    obj->pltrelasize);
2366e8d4c1andrew	for (rela = obj->pltrela; rela < relalim; rela++) {
237c148541andrew		Elf_Addr *where, target;
2386e8d4c1andrew
239ff48071andrew		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
2406e8d4c1andrew		switch(ELF_R_TYPE(rela->r_info)) {
2416e8d4c1andrew		case R_AARCH64_JUMP_SLOT:
2426e8d4c1andrew			def = find_symdef(ELF_R_SYM(rela->r_info), obj,
2436e8d4c1andrew			    &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate);
244c148541andrew			if (def == NULL)
2456e8d4c1andrew				return (-1);
246c148541andrew			if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
247c148541andrew				obj->gnu_ifunc = true;
248c148541andrew				continue;
2496e8d4c1andrew			}
250c148541andrew			target = (Elf_Addr)(defobj->relocbase + def->st_value);
251c148541andrew			reloc_jmpslot(where, target, defobj, obj,
252c148541andrew			    (const Elf_Rel *)rela);
2536e8d4c1andrew			break;
2546e8d4c1andrew		}
2556e8d4c1andrew	}
2560275a96mmel	obj->jmpslots_done = true;
2576e8d4c1andrew
2586e8d4c1andrew	return (0);
2596e8d4c1andrew}
2606e8d4c1andrew
261a3859a4kibstatic void
262a3859a4kibreloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela,
263a3859a4kib    RtldLockState *lockstate)
264a3859a4kib{
265a3859a4kib	Elf_Addr *where, target, *ptr;
266a3859a4kib
267a3859a4kib	ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
268a3859a4kib	where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
269a3859a4kib	lock_release(rtld_bind_lock, lockstate);
270a3859a4kib	target = call_ifunc_resolver(ptr);
271a3859a4kib	wlock_acquire(rtld_bind_lock, lockstate);
272a3859a4kib	*where = target;
273a3859a4kib}
274a3859a4kib
2756e8d4c1andrewint
2766e8d4c1andrewreloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
2776e8d4c1andrew{
278c148541andrew	const Elf_Rela *relalim;
279c148541andrew	const Elf_Rela *rela;
2806e8d4c1andrew
281c148541andrew	if (!obj->irelative)
282c148541andrew		return (0);
283a3859a4kib	obj->irelative = false;
284a3859a4kib	relalim = (const Elf_Rela *)((const char *)obj->pltrela +
285a3859a4kib	    obj->pltrelasize);
286c148541andrew	for (rela = obj->pltrela;  rela < relalim;  rela++) {
287a3859a4kib		if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE)
288a3859a4kib			reloc_iresolve_one(obj, rela, lockstate);
289a3859a4kib	}
290a3859a4kib	return (0);
291a3859a4kib}
292a3859a4kib
293a3859a4kibint
294a3859a4kibreloc_iresolve_nonplt(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
295a3859a4kib{
296a3859a4kib	const Elf_Rela *relalim;
297a3859a4kib	const Elf_Rela *rela;
298a3859a4kib
299a3859a4kib	if (!obj->irelative_nonplt)
300a3859a4kib		return (0);
301a3859a4kib	obj->irelative_nonplt = false;
302a3859a4kib	relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
303a3859a4kib	for (rela = obj->rela;  rela < relalim;  rela++) {
304a3859a4kib		if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE)
305a3859a4kib			reloc_iresolve_one(obj, rela, lockstate);
306c148541andrew	}
3076e8d4c1andrew	return (0);
3086e8d4c1andrew}
3096e8d4c1andrew
3106e8d4c1andrewint
3116e8d4c1andrewreloc_gnu_ifunc(Obj_Entry *obj, int flags,
3126e8d4c1andrew   struct Struct_RtldLockState *lockstate)
3136e8d4c1andrew{
314c148541andrew	const Elf_Rela *relalim;
315c148541andrew	const Elf_Rela *rela;
316c148541andrew	Elf_Addr *where, target;
317c148541andrew	const Elf_Sym *def;
318c148541andrew	const Obj_Entry *defobj;
3196e8d4c1andrew
320c148541andrew	if (!obj->gnu_ifunc)
321c148541andrew		return (0);
322b885decarichardson	relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);
323c148541andrew	for (rela = obj->pltrela;  rela < relalim;  rela++) {
324c148541andrew		if (ELF_R_TYPE(rela->r_info) == R_AARCH64_JUMP_SLOT) {
325c148541andrew			where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
326c148541andrew			def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
327c148541andrew			    SYMLOOK_IN_PLT | flags, NULL, lockstate);
328c148541andrew			if (def == NULL)
329c148541andrew				return (-1);
330c148541andrew			if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
331c148541andrew				continue;
332c148541andrew			lock_release(rtld_bind_lock, lockstate);
333c148541andrew			target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
334c148541andrew			wlock_acquire(rtld_bind_lock, lockstate);
335c148541andrew			reloc_jmpslot(where, target, defobj, obj,
336c148541andrew			    (const Elf_Rel *)rela);
337c148541andrew		}
338c148541andrew	}
339c148541andrew	obj->gnu_ifunc = false;
3406e8d4c1andrew	return (0);
3416e8d4c1andrew}
3426e8d4c1andrew
3436e8d4c1andrewElf_Addr
344b885decarichardsonreloc_jmpslot(Elf_Addr *where, Elf_Addr target,
345b885decarichardson    const Obj_Entry *defobj __unused, const Obj_Entry *obj __unused,
346b885decarichardson    const Elf_Rel *rel)
3476e8d4c1andrew{
3486e8d4c1andrew
349c148541andrew	assert(ELF_R_TYPE(rel->r_info) == R_AARCH64_JUMP_SLOT ||
350c148541andrew	    ELF_R_TYPE(rel->r_info) == R_AARCH64_IRELATIVE);
3516e8d4c1andrew
3523d8511bkib	if (*where != target && !ld_bind_not)
3536e8d4c1andrew		*where = target;
3543d8511bkib	return (target);
3556e8d4c1andrew}
3566e8d4c1andrew
357be68c0bkibvoid
358df6afadkibifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused)
359be68c0bkib{
360d9ac9c2marius
361d9ac9c2marius}
362d9ac9c2marius
363d9ac9c2mariusvoid
364d9ac9c2mariuspre_init(void)
365d9ac9c2marius{
366d9ac9c2marius
367be68c0bkib}
368be68c0bkib
3696e8d4c1andrew/*
3706e8d4c1andrew * Process non-PLT relocations
3716e8d4c1andrew */
3726e8d4c1andrewint
3736e8d4c1andrewreloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
3746e8d4c1andrew    RtldLockState *lockstate)
3756e8d4c1andrew{
3766e8d4c1andrew	const Obj_Entry *defobj;
3776e8d4c1andrew	const Elf_Rela *relalim;
3786e8d4c1andrew	const Elf_Rela *rela;
3796e8d4c1andrew	const Elf_Sym *def;
3806e8d4c1andrew	SymCache *cache;
381c148541andrew	Elf_Addr *where, symval;
3826e8d4c1andrew
3836e8d4c1andrew	/*
3846e8d4c1andrew	 * The dynamic loader may be called from a thread, we have
3856e8d4c1andrew	 * limited amounts of stack available so we cannot use alloca().
3866e8d4c1andrew	 */
3876e8d4c1andrew	if (obj == obj_rtld)
3886e8d4c1andrew		cache = NULL;
3896e8d4c1andrew	else
3906e8d4c1andrew		cache = calloc(obj->dynsymcount, sizeof(SymCache));
3916e8d4c1andrew		/* No need to check for NULL here */
3926e8d4c1andrew
393b885decarichardson	relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);
3946e8d4c1andrew	for (rela = obj->rela; rela < relalim; rela++) {
395c148541andrew		/*
396c148541andrew		 * First, resolve symbol for relocations which
397c148541andrew		 * reference symbols.
398c148541andrew		 */
3996e8d4c1andrew		switch (ELF_R_TYPE(rela->r_info)) {
4006e8d4c1andrew		case R_AARCH64_ABS64:
4016e8d4c1andrew		case R_AARCH64_GLOB_DAT:
402c148541andrew		case R_AARCH64_TLS_TPREL64:
4037376378mmel		case R_AARCH64_TLS_DTPREL64:
4047376378mmel		case R_AARCH64_TLS_DTPMOD64:
405c148541andrew			def = find_symdef(ELF_R_SYM(rela->r_info), obj,
406c148541andrew			    &defobj, flags, cache, lockstate);
4076e8d4c1andrew			if (def == NULL)
4086e8d4c1andrew				return (-1);
409c148541andrew			/*
410c148541andrew			 * If symbol is IFUNC, only perform relocation
411c148541andrew			 * when caller allowed it by passing
412c148541andrew			 * SYMLOOK_IFUNC flag.  Skip the relocations
413c148541andrew			 * otherwise.
414c148541andrew			 *
415c148541andrew			 * Also error out in case IFUNC relocations
416c148541andrew			 * are specified for TLS, which cannot be
417c148541andrew			 * usefully interpreted.
418c148541andrew			 */
419c148541andrew			if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
420c148541andrew				switch (ELF_R_TYPE(rela->r_info)) {
421c148541andrew				case R_AARCH64_ABS64:
422c148541andrew				case R_AARCH64_GLOB_DAT:
423c148541andrew					if ((flags & SYMLOOK_IFUNC) == 0) {
424c148541andrew						obj->non_plt_gnu_ifunc = true;
425c148541andrew						continue;
426c148541andrew					}
427c148541andrew					symval = (Elf_Addr)rtld_resolve_ifunc(
428c148541andrew					    defobj, def);
429c148541andrew					break;
430c148541andrew				default:
431c148541andrew					_rtld_error("%s: IFUNC for TLS reloc",
432c148541andrew					    obj->path);
433c148541andrew					return (-1);
434c148541andrew				}
435c148541andrew			} else {
436c148541andrew				if ((flags & SYMLOOK_IFUNC) != 0)
437c148541andrew					continue;
438c148541andrew				symval = (Elf_Addr)defobj->relocbase +
439c148541andrew				    def->st_value;
440c148541andrew			}
441c148541andrew			break;
442c148541andrew		default:
443c148541andrew			if ((flags & SYMLOOK_IFUNC) != 0)
444c148541andrew				continue;
445c148541andrew		}
4466e8d4c1andrew
447c148541andrew		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
448c148541andrew
449c148541andrew		switch (ELF_R_TYPE(rela->r_info)) {
450c148541andrew		case R_AARCH64_ABS64:
451c148541andrew		case R_AARCH64_GLOB_DAT:
452c148541andrew			*where = symval + rela->r_addend;
4536e8d4c1andrew			break;
4546e8d4c1andrew		case R_AARCH64_COPY:
4556e8d4c1andrew			/*
4566e8d4c1andrew			 * These are deferred until all other relocations have
4576e8d4c1andrew			 * been done. All we do here is make sure that the
4586e8d4c1andrew			 * COPY relocation is not in a shared library. They
4596e8d4c1andrew			 * are allowed only in executable files.
4606e8d4c1andrew			 */
4616e8d4c1andrew			if (!obj->mainprog) {
4626e8d4c1andrew				_rtld_error("%s: Unexpected R_AARCH64_COPY "
4636e8d4c1andrew				    "relocation in shared library", obj->path);
4646e8d4c1andrew				return (-1);
4656e8d4c1andrew			}
4666e8d4c1andrew			break;
4679ce2f86andrew		case R_AARCH64_TLSDESC:
4684a03477mmel			reloc_tlsdesc(obj, rela, where, flags, lockstate);
4699ce2f86andrew			break;
470ff48071andrew		case R_AARCH64_TLS_TPREL64:
471ff48071andrew			/*
472ff48071andrew			 * We lazily allocate offsets for static TLS as we
473ff48071andrew			 * see the first relocation that references the
474ff48071andrew			 * TLS block. This allows us to support (small
475ff48071andrew			 * amounts of) static TLS in dynamically loaded
476ff48071andrew			 * modules. If we run out of space, we generate an
477ff48071andrew			 * error.
478ff48071andrew			 */
479ff48071andrew			if (!defobj->tls_done) {
480b885decarichardson				if (!allocate_tls_offset(
481b885decarichardson				    __DECONST(Obj_Entry *, defobj))) {
482ff48071andrew					_rtld_error(
483ff48071andrew					    "%s: No space available for static "
484ff48071andrew					    "Thread Local Storage", obj->path);
485ff48071andrew					return (-1);
486ff48071andrew				}
487ff48071andrew			}
4884a03477mmel			/* Test weak undefined thread variable */
4894a03477mmel			if (def->st_shndx != SHN_UNDEF) {
4904a03477mmel				*where = def->st_value + rela->r_addend +
4914a03477mmel				    defobj->tlsoffset;
4924a03477mmel			} else {
4934a03477mmel				/*
4944a03477mmel				 * XXX We should relocate undefined thread
4954a03477mmel				 * weak variable address to NULL, but how?
4964a03477mmel				 * Can we return error in this situation?
4974a03477mmel				 */
4984a03477mmel				rtld_printf("%s: Unable to relocate undefined "
4994a03477mmel				"weak TLS variable\n", obj->path);
5004a03477mmel#if 0
5014a03477mmel				return (-1);
5024a03477mmel#else
5034a03477mmel				*where = def->st_value + rela->r_addend +
5044a03477mmel				    defobj->tlsoffset;
5054a03477mmel#endif
5064a03477mmel			}
507ff48071andrew			break;
5087376378mmel
5097376378mmel		/*
5107376378mmel		 * !!! BEWARE !!!
5117376378mmel		 * ARM ELF ABI defines TLS_DTPMOD64 as 1029, and TLS_DTPREL64
5127376378mmel		 * as 1028. But actual bfd linker and the glibc RTLD linker
5137376378mmel		 * treats TLS_DTPMOD64 as 1028 and TLS_DTPREL64 1029.
5147376378mmel		 */
5157376378mmel		case R_AARCH64_TLS_DTPREL64: /* efectively is TLS_DTPMOD64 */
5167376378mmel			*where += (Elf_Addr)defobj->tlsindex;
5177376378mmel			break;
5187376378mmel		case R_AARCH64_TLS_DTPMOD64: /* efectively is TLS_DTPREL64 */
5197376378mmel			*where += (Elf_Addr)(def->st_value + rela->r_addend);
5207376378mmel			break;
5216e8d4c1andrew		case R_AARCH64_RELATIVE:
5226e8d4c1andrew			*where = (Elf_Addr)(obj->relocbase + rela->r_addend);
5236e8d4c1andrew			break;
5247376378mmel		case R_AARCH64_NONE:
5257376378mmel			break;
526a3859a4kib		case R_AARCH64_IRELATIVE:
527a3859a4kib			obj->irelative_nonplt = true;
528a3859a4kib			break;
5296e8d4c1andrew		default:
5306e8d4c1andrew			rtld_printf("%s: Unhandled relocation %lu\n",
5316e8d4c1andrew			    obj->path, ELF_R_TYPE(rela->r_info));
5326e8d4c1andrew			return (-1);
5336e8d4c1andrew		}
5346e8d4c1andrew	}
5356e8d4c1andrew
5366e8d4c1andrew	return (0);
5376e8d4c1andrew}
5386e8d4c1andrew
5396e8d4c1andrewvoid
5406e8d4c1andrewallocate_initial_tls(Obj_Entry *objs)
5416e8d4c1andrew{
5426e8d4c1andrew	Elf_Addr **tp;
5436e8d4c1andrew
5446e8d4c1andrew	/*
5456e8d4c1andrew	* Fix the size of the static TLS block by using the maximum
5466e8d4c1andrew	* offset allocated so far and adding a bit for dynamic modules to
5476e8d4c1andrew	* use.
5486e8d4c1andrew	*/
5496e8d4c1andrew	tls_static_space = tls_last_offset + tls_last_size +
5506e8d4c1andrew	    RTLD_STATIC_TLS_EXTRA;
5516e8d4c1andrew
5526e8d4c1andrew	tp = (Elf_Addr **) allocate_tls(objs, NULL, TLS_TCB_SIZE, 16);
5536e8d4c1andrew
5546e8d4c1andrew	asm volatile("msr	tpidr_el0, %0" : : "r"(tp));
5556e8d4c1andrew}
556d2e12e4mmel
557d2e12e4mmelvoid *
558d2e12e4mmel__tls_get_addr(tls_index* ti)
559d2e12e4mmel{
560d2e12e4mmel      char *p;
561d2e12e4mmel      void *_tp;
562d2e12e4mmel
563d2e12e4mmel      __asm __volatile("mrs	%0, tpidr_el0"  : "=r" (_tp));
564d2e12e4mmel      p = tls_get_addr_common((Elf_Addr **)(_tp), ti->ti_module, ti->ti_offset);
565d2e12e4mmel
566d2e12e4mmel      return (p);
567d2e12e4mmel}
568