rtld_machine.c revision 1.1
1/*	$OpenBSD: rtld_machine.c,v 1.1 2021/04/28 15:16:26 drahn Exp $ */
2
3/*
4 * Copyright (c) 2004,2021 Dale Rahn <drahn@openbsd.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29#define _DYN_LOADER
30
31#include <sys/types.h>
32#include <sys/mman.h>
33#include <sys/syscall.h>
34#include <sys/unistd.h>
35
36#include <nlist.h>
37#include <link.h>
38
39#include "syscall.h"
40#include "archdep.h"
41#include "resolve.h"
42
43int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
44#define R_TYPE(x) R_RISCV_ ## x
45
46void _dl_bind_start(void); /* XXX */
47Elf_Addr _dl_bind(elf_object_t *object, int index);
48#define _RF_S		0x80000000		/* Resolve symbol */
49#define _RF_A		0x40000000		/* Use addend */
50#define _RF_P		0x20000000		/* Location relative */
51#define _RF_G		0x10000000		/* GOT offset */
52#define _RF_B		0x08000000		/* Load address relative */
53#define _RF_V		0x02000000		/* ERROR */
54#define _RF_SZ(s)	(((s) & 0xff) << 8)	/* memory target size */
55#define _RF_RS(s)	((s) & 0xff)		/* right shift */
56static const int reloc_target_flags[] = {
57	[ R_TYPE(NONE) ] = 0,
58	[ R_TYPE(32) ] =
59	  _RF_V|_RF_S|_RF_A|		_RF_SZ(32) | _RF_RS(0),	/* GLOB_DAT */
60	[ R_TYPE(64) ] =
61	  _RF_V|_RF_S|_RF_A|		_RF_SZ(64) | _RF_RS(0),	/* GLOB_DAT */
62	[ R_TYPE(JUMP_SLOT) ] =
63	  _RF_V|_RF_S|			_RF_SZ(64) | _RF_RS(0),	/* JUMP_SLOT */
64	[ R_TYPE(RELATIVE) ] =
65	  _RF_V|_RF_B|_RF_A|		_RF_SZ(64) | _RF_RS(0),	/* REL64 */
66//	[ R_TYPE(TLSDESC) ] =		 _RF_V|_RF_S,
67	[ R_TYPE(TLS_TPREL64) ] =	 _RF_V|_RF_S,
68	[ R_TYPE(COPY) ] =
69	  _RF_V|_RF_S|			_RF_SZ(32) | _RF_RS(0),	/* 20 COPY */
70
71};
72
73#define RELOC_RESOLVE_SYMBOL(t)		((reloc_target_flags[t] & _RF_S) != 0)
74#define RELOC_PC_RELATIVE(t)		((reloc_target_flags[t] & _RF_P) != 0)
75#define RELOC_BASE_RELATIVE(t)		((reloc_target_flags[t] & _RF_B) != 0)
76#define RELOC_USE_ADDEND(t)		((reloc_target_flags[t] & _RF_A) != 0)
77#define RELOC_TARGET_SIZE(t)		((reloc_target_flags[t] >> 8) & 0xff)
78#define RELOC_VALUE_RIGHTSHIFT(t)	(reloc_target_flags[t] & 0xff)
79
80static const Elf_Addr reloc_target_bitmask[] = {
81#define _BM(x)  (~(Elf_Addr)0 >> ((8*sizeof(reloc_target_bitmask[0])) - (x)))
82	[ R_TYPE(NONE) ] = 0,
83	[ R_TYPE(32) ] = _BM(32),
84	[ R_TYPE(64) ] = _BM(64),
85	[ R_TYPE(JUMP_SLOT) ] = _BM(64),
86	[ R_TYPE(RELATIVE) ] = _BM(64),
87//	[ R_TYPE(TLSDESC) ] = _BM(64),
88	[ R_TYPE(TLS_TPREL64) ] = _BM(64),
89	[ R_TYPE(COPY) ] = _BM(64),
90#undef _BM
91};
92#define RELOC_VALUE_BITMASK(t)	(reloc_target_bitmask[t])
93
94
95void _dl_reloc_plt(Elf_Word *where, Elf_Addr value, Elf_RelA *rel);
96
97#define nitems(_a)     (sizeof((_a)) / sizeof((_a)[0]))
98
99int
100_dl_md_reloc(elf_object_t *object, int rel, int relsz)
101{
102	long	i;
103	long	numrel;
104	long	relrel;
105	int	fails = 0;
106	Elf_Addr loff;
107	Elf_Addr prev_value = 0;
108	const Elf_Sym *prev_sym = NULL;
109	Elf_RelA *rels;
110
111	loff = object->obj_base;
112	numrel = object->Dyn.info[relsz] / sizeof(Elf_RelA);
113	relrel = rel == DT_RELA ? object->relcount : 0;
114	rels = (Elf_RelA *)(object->Dyn.info[rel]);
115
116	if (rels == NULL)
117		return 0;
118
119	if (relrel > numrel)
120		_dl_die("relcount > numrel: %ld > %ld", relrel, numrel);
121
122	/* tight loop for leading RELATIVE relocs */
123	for (i = 0; i < relrel; i++, rels++) {
124		Elf_Addr *where;
125
126		where = (Elf_Addr *)(rels->r_offset + loff);
127		*where += loff;
128	}
129	for (; i < numrel; i++, rels++) {
130		Elf_Addr *where, value, mask;
131		Elf_Word type;
132		const Elf_Sym *sym;
133		const char *symn;
134
135		type = ELF_R_TYPE(rels->r_info);
136
137		if (type >= nitems(reloc_target_flags) ||
138		    (reloc_target_flags[type] & _RF_V) == 0)
139			_dl_die("bad relocation %ld %d", i, type);
140
141		if (type == R_TYPE(NONE))
142			continue;
143
144		if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
145			continue;
146
147		where = (Elf_Addr *)(rels->r_offset + loff);
148
149		if (RELOC_USE_ADDEND(type))
150			value = rels->r_addend;
151		else
152			value = 0;
153
154		sym = NULL;
155		symn = NULL;
156		if (RELOC_RESOLVE_SYMBOL(type)) {
157			sym = object->dyn.symtab;
158			sym += ELF_R_SYM(rels->r_info);
159			symn = object->dyn.strtab + sym->st_name;
160
161			if (sym->st_shndx != SHN_UNDEF &&
162			    ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
163				value += loff;
164			} else if (sym == prev_sym) {
165				value += prev_value;
166			} else {
167				struct sym_res sr;
168
169				sr = _dl_find_symbol(symn,
170				    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
171				    ((type == R_TYPE(JUMP_SLOT)) ?
172					SYM_PLT : SYM_NOTPLT), sym, object);
173				if (sr.sym == NULL) {
174resolve_failed:
175					if (ELF_ST_BIND(sym->st_info) !=
176					    STB_WEAK)
177						fails++;
178					continue;
179				}
180				prev_sym = sym;
181				prev_value = (Elf_Addr)(sr.obj->obj_base +
182				    sr.sym->st_value);
183				value += prev_value;
184			}
185		}
186
187		if (type == R_TYPE(JUMP_SLOT)) {
188			/*
189			_dl_reloc_plt((Elf_Word *)where, value, rels);
190			*/
191			*where = value;
192			continue;
193		}
194
195		if (type == R_TYPE(COPY)) {
196			void *dstaddr = where;
197			const void *srcaddr;
198			const Elf_Sym *dstsym = sym;
199			struct sym_res sr;
200
201			sr = _dl_find_symbol(symn,
202			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
203			    dstsym, object);
204			if (sr.sym == NULL)
205				goto resolve_failed;
206
207			srcaddr = (void *)(sr.obj->obj_base + sr.sym->st_value);
208			_dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
209			continue;
210		}
211
212		if (RELOC_PC_RELATIVE(type))
213			value -= (Elf_Addr)where;
214		if (RELOC_BASE_RELATIVE(type))
215			value += loff;
216
217		mask = RELOC_VALUE_BITMASK(type);
218		value >>= RELOC_VALUE_RIGHTSHIFT(type);
219		value &= mask;
220
221		*where &= ~mask;
222		*where |= value;
223	}
224
225	return fails;
226}
227
228/*
229 *	Relocate the Global Offset Table (GOT).
230 *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
231 *	otherwise the lazy binding plt initialization is performed.
232 */
233int
234_dl_md_reloc_got(elf_object_t *object, int lazy)
235{
236	int	fails = 0;
237	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
238	int i, num;
239	Elf_RelA *rel;
240
241	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
242		return 0;
243
244	// XXX - fix and enable.
245	lazy = 0;
246
247	if (!lazy) {
248		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
249	} else {
250		rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
251		num = (object->Dyn.info[DT_PLTRELSZ]);
252
253		for (i = 0; i < num/sizeof(Elf_RelA); i++, rel++) {
254			Elf_Addr *where;
255			where = (Elf_Addr *)(rel->r_offset + object->obj_base);
256			*where += object->obj_base;
257		}
258
259		pltgot[1] = (Elf_Addr)object;
260		pltgot[2] = (Elf_Addr)_dl_bind_start;
261	}
262
263	return fails;
264}
265
266Elf_Addr
267_dl_bind(elf_object_t *object, int relidx)
268{
269	Elf_RelA *rel;
270	const Elf_Sym *sym;
271	const char *symn;
272	struct sym_res sr;
273	int64_t cookie = pcookie;
274	struct {
275		struct __kbind param;
276		Elf_Addr newval;
277	} buf;
278
279	rel = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + (relidx);
280
281	sym = object->dyn.symtab;
282	sym += ELF_R_SYM(rel->r_info);
283	symn = object->dyn.strtab + sym->st_name;
284
285	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
286	    sym, object);
287	if (sr.sym == NULL)
288		_dl_die("lazy binding failed!");
289
290	buf.newval = sr.obj->obj_base + sr.sym->st_value;
291
292	if (sr.obj->traced && _dl_trace_plt(sr.obj, symn))
293		return buf.newval;
294
295	buf.param.kb_addr = (Elf_Word *)(object->obj_base + rel->r_offset);
296	buf.param.kb_size = sizeof(Elf_Addr);
297
298	/* directly code the syscall, so that it's actually inline here */
299	{
300		register long syscall_num __asm("t0") = SYS_kbind;
301		register void *arg1 __asm("a0") = &buf;
302		register long  arg2 __asm("a1") = sizeof(buf);
303		register long  arg3 __asm("x2") = cookie;
304
305		__asm volatile("ecall" : "+r" (arg1), "+r" (arg2)
306		    : "r" (syscall_num), "r" (arg3)
307		    : "cc", "memory");
308	}
309
310	return buf.newval;
311}
312