rtld_machine.c revision 1.1
154359Sroberto/*	$OpenBSD: rtld_machine.c,v 1.1 2017/01/11 14:11:27 patrick Exp $ */
2132451Sroberto
354359Sroberto/*
454359Sroberto * Copyright (c) 2004 Dale Rahn
582498Sroberto *
654359Sroberto * Redistribution and use in source and binary forms, with or without
754359Sroberto * modification, are permitted provided that the following conditions
854359Sroberto * are met:
954359Sroberto * 1. Redistributions of source code must retain the above copyright
1054359Sroberto *    notice, this list of conditions and the following disclaimer.
1154359Sroberto * 2. Redistributions in binary form must reproduce the above copyright
12182007Sroberto *    notice, this list of conditions and the following disclaimer in the
1354359Sroberto *    documentation and/or other materials provided with the distribution.
1482498Sroberto *
1582498Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
1682498Sroberto * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1782498Sroberto * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1882498Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1982498Sroberto * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2054359Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21132451Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2254359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2354359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2454359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25132451Sroberto * SUCH DAMAGE.
26132451Sroberto *
27132451Sroberto */
2854359Sroberto
2954359Sroberto#define _DYN_LOADER
30132451Sroberto
31132451Sroberto#include <sys/types.h>
32132451Sroberto#include <sys/mman.h>
33132451Sroberto#include <sys/syscall.h>
34132451Sroberto#include <sys/unistd.h>
3554359Sroberto
36132451Sroberto#include <nlist.h>
37132451Sroberto#include <link.h>
38132451Sroberto
39132451Sroberto#include "syscall.h"
4054359Sroberto#include "archdep.h"
4154359Sroberto#include "resolve.h"
4254359Sroberto
4354359Srobertoint64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
4454359Sroberto
4554359Srobertovoid _dl_bind_start(void); /* XXX */
4654359SrobertoElf_Addr _dl_bind(elf_object_t *object, int index);
4754359Sroberto#define _RF_S		0x80000000		/* Resolve symbol */
4854359Sroberto#define _RF_A		0x40000000		/* Use addend */
4954359Sroberto#define _RF_P		0x20000000		/* Location relative */
5054359Sroberto#define _RF_G		0x10000000		/* GOT offset */
5154359Sroberto#define _RF_B		0x08000000		/* Load address relative */
5254359Sroberto#define _RF_U		0x04000000		/* Unaligned */
5354359Sroberto#define _RF_V		0x02000000		/* ERROR */
5454359Sroberto#define _RF_SZ(s)	(((s) & 0xff) << 8)	/* memory target size */
5554359Sroberto#define _RF_RS(s)	((s) & 0xff)		/* right shift */
5654359Srobertostatic int reloc_target_flags[] = {
5754359Sroberto	0,						/*  0 NONE */
5854359Sroberto	[ R_AARCH64_ABS64 ] =
5954359Sroberto	  _RF_V|_RF_S|_RF_A|		_RF_SZ(64) | _RF_RS(0),	/* ABS64 */
6054359Sroberto	[ R_AARCH64_GLOB_DAT ] =
6154359Sroberto	  _RF_V|_RF_S|_RF_A|		_RF_SZ(64) | _RF_RS(0),	/* GLOB_DAT */
6254359Sroberto	[ R_AARCH64_JUMP_SLOT ] =
6354359Sroberto	  _RF_V|_RF_S|			_RF_SZ(64) | _RF_RS(0),	/* JUMP_SLOT */
64132451Sroberto	[ R_AARCH64_RELATIVE ] =
6554359Sroberto	  _RF_V|_RF_B|_RF_A|		_RF_SZ(64) | _RF_RS(0),	/* REL64 */
6654359Sroberto	[ R_AARCH64_TLSDESC ] =
6754359Sroberto	  _RF_V|_RF_S,
68132451Sroberto	[ R_AARCH64_TLS_TPREL64 ] =
69132451Sroberto	  _RF_V|_RF_S,
7054359Sroberto	[ R_AARCH64_COPY ] =
71132451Sroberto	  _RF_V|_RF_S|			_RF_SZ(32) | _RF_RS(0),	/* 20 COPY */
72132451Sroberto
73132451Sroberto};
7454359Sroberto
7554359Sroberto#define RELOC_RESOLVE_SYMBOL(t)		((reloc_target_flags[t] & _RF_S) != 0)
7654359Sroberto#define RELOC_PC_RELATIVE(t)		((reloc_target_flags[t] & _RF_P) != 0)
7754359Sroberto#define RELOC_BASE_RELATIVE(t)		((reloc_target_flags[t] & _RF_B) != 0)
78132451Sroberto#define RELOC_UNALIGNED(t)		((reloc_target_flags[t] & _RF_U) != 0)
79132451Sroberto#define RELOC_USE_ADDEND(t)		((reloc_target_flags[t] & _RF_A) != 0)
80132451Sroberto#define RELOC_TARGET_SIZE(t)		((reloc_target_flags[t] >> 8) & 0xff)
8154359Sroberto#define RELOC_VALUE_RIGHTSHIFT(t)	(reloc_target_flags[t] & 0xff)
8254359Srobertostatic Elf_Addr reloc_target_bitmask[] = {
8354359Sroberto#define _BM(x)  (~(Elf_Addr)0 >> ((8*sizeof(reloc_target_bitmask[0])) - (x)))
8454359Sroberto	0,		/*  0 NONE */
8554359Sroberto	[ R_AARCH64_ABS64 ] = _BM(64),
86132451Sroberto	[ R_AARCH64_GLOB_DAT ] = _BM(64),
87132451Sroberto	[ R_AARCH64_JUMP_SLOT ] = _BM(64),
8854359Sroberto	[ R_AARCH64_RELATIVE ] = _BM(64),
8954359Sroberto	[ R_AARCH64_TLSDESC ] = _BM(64),
9054359Sroberto	[ R_AARCH64_TLS_TPREL64 ] = _BM(64),
9154359Sroberto	[ R_AARCH64_COPY ] = _BM(64),
9254359Sroberto#undef _BM
9354359Sroberto};
9454359Sroberto#define RELOC_VALUE_BITMASK(t)	(reloc_target_bitmask[t])
9554359Sroberto
9654359Sroberto#define R_TYPE(x) R_AARCH64_ ## x
9754359Sroberto
9854359Srobertovoid _dl_reloc_plt(Elf_Word *where, Elf_Addr value, Elf_RelA *rel);
9954359Sroberto
10054359Sroberto#define nitems(_a)     (sizeof((_a)) / sizeof((_a)[0]))
10154359Sroberto
10254359Srobertoint
10354359Sroberto_dl_md_reloc(elf_object_t *object, int rel, int relsz)
10454359Sroberto{
10554359Sroberto	long	i;
10654359Sroberto	long	numrel;
10754359Sroberto	long	relrel;
108132451Sroberto	int	fails = 0;
109132451Sroberto	Elf_Addr loff;
11054359Sroberto	Elf_Addr prev_value = 0;
11154359Sroberto	const Elf_Sym *prev_sym = NULL;
11254359Sroberto	Elf_RelA *rels;
11354359Sroberto	struct load_list *llist;
11454359Sroberto
11554359Sroberto	loff = object->obj_base;
11654359Sroberto	numrel = object->Dyn.info[relsz] / sizeof(Elf_RelA);
11754359Sroberto	relrel = rel == DT_RELA ? object->relcount : 0;
11854359Sroberto	rels = (Elf_RelA *)(object->Dyn.info[rel]);
11954359Sroberto
12054359Sroberto	if (rels == NULL)
12154359Sroberto		return(0);
12254359Sroberto
12354359Sroberto	if (relrel > numrel) {
12454359Sroberto		_dl_printf("relcount > numrel: %ld > %ld\n", relrel, numrel);
12554359Sroberto		_dl_exit(20);
12654359Sroberto	}
127132451Sroberto
12854359Sroberto	/*
12954359Sroberto	 * unprotect some segments if we need it.
13054359Sroberto	 */
13154359Sroberto	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
13254359Sroberto		for (llist = object->load_list;
13354359Sroberto		    llist != NULL;
13454359Sroberto		    llist = llist->next) {
13554359Sroberto			if (!(llist->prot & PROT_WRITE))
13654359Sroberto				_dl_mprotect(llist->start, llist->size,
13754359Sroberto				    llist->prot|PROT_WRITE);
13854359Sroberto		}
13954359Sroberto	}
14054359Sroberto
14154359Sroberto	/* tight loop for leading RELATIVE relocs */
14254359Sroberto	for (i = 0; i < relrel; i++, rels++) {
14354359Sroberto		Elf_Addr *where;
14454359Sroberto
14554359Sroberto#ifdef DEBUG
14654359Sroberto		if (ELF_R_TYPE(rels->r_info) != R_TYPE(RELATIVE)) {
14754359Sroberto			_dl_printf("RELCOUNT wrong\n");
14854359Sroberto			_dl_exit(20);
14954359Sroberto		}
15054359Sroberto#endif
15154359Sroberto		where = (Elf_Addr *)(rels->r_offset + loff);
15254359Sroberto		*where += loff;
15354359Sroberto	}
15454359Sroberto	for (; i < numrel; i++, rels++) {
15554359Sroberto		Elf_Addr *where, value, ooff, mask;
15654359Sroberto		Elf_Word type;
15754359Sroberto		const Elf_Sym *sym, *this;
15854359Sroberto		const char *symn;
15954359Sroberto
16054359Sroberto		type = ELF_R_TYPE(rels->r_info);
16154359Sroberto
16254359Sroberto		if (type >= nitems(reloc_target_flags)) {
16354359Sroberto			_dl_printf(" bad relocation %d %d\n", i, type);
16454359Sroberto			_dl_exit(1);
16554359Sroberto		}
16654359Sroberto		if ((reloc_target_flags[type] & _RF_V)==0) {
16754359Sroberto			_dl_printf(" bad relocation %d %d\n", i, type);
16854359Sroberto			_dl_exit(1);
16954359Sroberto		}
17054359Sroberto		if (type == R_TYPE(NONE))
17154359Sroberto			continue;
17254359Sroberto
17354359Sroberto		if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
17454359Sroberto			continue;
17554359Sroberto
17654359Sroberto		where = (Elf_Addr *)(rels->r_offset + loff);
17754359Sroberto
17854359Sroberto		if (RELOC_USE_ADDEND(type))
17954359Sroberto			value = rels->r_addend;
18054359Sroberto		else
181182007Sroberto			value = 0;
182182007Sroberto
183182007Sroberto		sym = NULL;
184182007Sroberto		symn = NULL;
18554359Sroberto		if (RELOC_RESOLVE_SYMBOL(type)) {
186182007Sroberto			sym = object->dyn.symtab;
187182007Sroberto			sym += ELF_R_SYM(rels->r_info);
188182007Sroberto			symn = object->dyn.strtab + sym->st_name;
189182007Sroberto
190182007Sroberto			if (sym->st_shndx != SHN_UNDEF &&
191182007Sroberto			    ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
192182007Sroberto				value += loff;
193182007Sroberto			} else if (sym == prev_sym) {
194182007Sroberto				value += prev_value;
195182007Sroberto			} else {
196182007Sroberto				this = NULL;
197182007Sroberto				ooff = _dl_find_symbol_bysym(object,
198182007Sroberto				    ELF_R_SYM(rels->r_info), &this,
199182007Sroberto				    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
20054359Sroberto				    ((type == R_TYPE(JUMP_SLOT)) ?
20154359Sroberto					SYM_PLT : SYM_NOTPLT),
202182007Sroberto				    sym, NULL);
203182007Sroberto				if (this == NULL) {
20454359Srobertoresolve_failed:
205182007Sroberto					if (ELF_ST_BIND(sym->st_info) !=
20654359Sroberto					    STB_WEAK)
20754359Sroberto						fails++;
20854359Sroberto					continue;
20954359Sroberto				}
21054359Sroberto				prev_sym = sym;
21154359Sroberto				prev_value = (Elf_Addr)(ooff + this->st_value);
212132451Sroberto				value += prev_value;
21354359Sroberto			}
21454359Sroberto		}
21554359Sroberto
21654359Sroberto		if (type == R_TYPE(JUMP_SLOT)) {
217182007Sroberto			/*
21854359Sroberto			_dl_reloc_plt((Elf_Word *)where, value, rels);
21954359Sroberto			*/
220132451Sroberto			*where = value;
221132451Sroberto			continue;
222132451Sroberto		}
22354359Sroberto
22454359Sroberto		if (type == R_TYPE(COPY)) {
22554359Sroberto			void *dstaddr = where;
226132451Sroberto			const void *srcaddr;
227132451Sroberto			const Elf_Sym *dstsym = sym, *srcsym = NULL;
228132451Sroberto			Elf_Addr soff;
229132451Sroberto
230132451Sroberto			soff = _dl_find_symbol(symn, &srcsym,
231132451Sroberto			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
23254359Sroberto			    dstsym, object, NULL);
23354359Sroberto			if (srcsym == NULL)
234132451Sroberto				goto resolve_failed;
235132451Sroberto
23654359Sroberto			srcaddr = (void *)(soff + srcsym->st_value);
23754359Sroberto			_dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
23854359Sroberto			continue;
239132451Sroberto		}
24054359Sroberto
24154359Sroberto		if (RELOC_PC_RELATIVE(type))
24254359Sroberto			value -= (Elf_Addr)where;
24354359Sroberto		if (RELOC_BASE_RELATIVE(type))
24454359Sroberto			value += loff;
24554359Sroberto
24654359Sroberto		mask = RELOC_VALUE_BITMASK(type);
247182007Sroberto		value >>= RELOC_VALUE_RIGHTSHIFT(type);
24854359Sroberto		value &= mask;
24954359Sroberto
25054359Sroberto		if (RELOC_UNALIGNED(type)) {
25154359Sroberto			/* Handle unaligned relocations. */
25254359Sroberto			Elf_Addr tmp = 0;
25354359Sroberto			char *ptr = (char *)where;
25454359Sroberto			int i, size = RELOC_TARGET_SIZE(type)/8;
25554359Sroberto
25654359Sroberto			/* Read it in one byte at a time. */
25754359Sroberto			for (i=0; i<size; i++)
258132451Sroberto				tmp = (tmp << 8) | ptr[i];
25954359Sroberto
260132451Sroberto			tmp &= ~mask;
26154359Sroberto			tmp |= value;
26254359Sroberto
263182007Sroberto			/* Write it back out. */
264182007Sroberto			for (i=0; i<size; i++)
265132451Sroberto				ptr[i] = ((tmp >> (8*i)) & 0xff);
266182007Sroberto		} else {
267132451Sroberto			*where &= ~mask;
26854359Sroberto			*where |= value;
26954359Sroberto		}
27054359Sroberto	}
27154359Sroberto
272132451Sroberto	/* reprotect the unprotected segments */
273132451Sroberto	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
27454359Sroberto		for (llist = object->load_list;
27554359Sroberto		    llist != NULL;
27654359Sroberto		    llist = llist->next) {
27754359Sroberto			if (!(llist->prot & PROT_WRITE))
27854359Sroberto				_dl_mprotect(llist->start, llist->size,
27954359Sroberto				    llist->prot);
28054359Sroberto		}
281132451Sroberto	}
282132451Sroberto
28354359Sroberto	return (fails);
284132451Sroberto}
285132451Sroberto
286132451Sroberto/*
28754359Sroberto *	Relocate the Global Offset Table (GOT).
28854359Sroberto *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
28954359Sroberto *	otherwise the lazy binding plt initialization is performed.
29054359Sroberto */
291182007Srobertoint
292132451Sroberto_dl_md_reloc_got(elf_object_t *object, int lazy)
293132451Sroberto{
29454359Sroberto	int	fails = 0;
29554359Sroberto	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
296132451Sroberto	int i, num;
297132451Sroberto	Elf_RelA *rel;
29854359Sroberto
29954359Sroberto	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
30054359Sroberto		return (0);
30154359Sroberto
30254359Sroberto	if (object->traced)
30354359Sroberto		lazy = 1;
30454359Sroberto
305182007Sroberto	lazy = 0; // until support is written.
30654359Sroberto
30754359Sroberto	if (!lazy) {
30854359Sroberto		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
30954359Sroberto	} else {
31054359Sroberto		rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
31154359Sroberto		num = (object->Dyn.info[DT_PLTRELSZ]);
31254359Sroberto
31354359Sroberto		for (i = 0; i < num/sizeof(Elf_RelA); i++, rel++) {
31454359Sroberto			Elf_Addr *where;
31554359Sroberto			where = (Elf_Addr *)(rel->r_offset + object->obj_base);
31654359Sroberto			*where += object->obj_base;
31754359Sroberto		}
31854359Sroberto
319132451Sroberto		pltgot[1] = (Elf_Addr)object;
320132451Sroberto		pltgot[2] = (Elf_Addr)_dl_bind_start;
32154359Sroberto	}
32254359Sroberto
32354359Sroberto	/* mprotect the GOT */
32454359Sroberto	_dl_protect_segment(object, 0, "__got_start", "__got_end", PROT_READ);
32554359Sroberto
32654359Sroberto	return (fails);
32754359Sroberto}
32854359Sroberto
32954359SrobertoElf_Addr
33054359Sroberto_dl_bind(elf_object_t *object, int relidx)
33154359Sroberto{
33254359Sroberto	Elf_RelA *rel;
33354359Sroberto	const Elf_Sym *sym, *this;
33454359Sroberto	const char *symn;
33554359Sroberto	const elf_object_t *sobj;
33654359Sroberto	Elf_Addr ooff;
33754359Sroberto	int64_t cookie = pcookie;
33854359Sroberto	struct {
33954359Sroberto		struct __kbind param;
34054359Sroberto		Elf_Addr newval;
34154359Sroberto	} buf;
34254359Sroberto
34354359Sroberto	rel = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + (relidx);
344132451Sroberto
34554359Sroberto	sym = object->dyn.symtab;
34654359Sroberto	sym += ELF_R_SYM(rel->r_info);
34754359Sroberto	symn = object->dyn.strtab + sym->st_name;
34854359Sroberto
34954359Sroberto	this = NULL;
35054359Sroberto	ooff = _dl_find_symbol(symn,  &this,
35154359Sroberto	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
35254359Sroberto	if (this == NULL) {
35354359Sroberto		_dl_printf("lazy binding failed!\n");
35454359Sroberto		*(volatile int *)0 = 0;		/* XXX */
35554359Sroberto	}
35654359Sroberto
35754359Sroberto	buf.newval = ooff + this->st_value;
35854359Sroberto
359	if (sobj->traced && _dl_trace_plt(sobj, symn))
360		return buf.newval;
361
362	buf.param.kb_addr = (Elf_Word *)(object->obj_base + rel->r_offset);
363	buf.param.kb_size = sizeof(Elf_Addr);
364
365	/* directly code the syscall, so that it's actually inline here */
366	{
367		register long syscall_num __asm("x8") = SYS_kbind;
368		register void *arg1 __asm("x0") = &buf;
369		register long  arg2 __asm("x1") = sizeof(buf);
370		register long  arg3 __asm("x2") = cookie;
371
372		__asm volatile("svc 0" : "+r" (arg1), "+r" (arg2)
373		    : "r" (syscall_num), "r" (arg3)
374		    : "cc", "memory");
375	}
376
377	return buf.newval;
378}
379