rtld_machine.c revision 1.29
1117395Skan/*	$OpenBSD: rtld_machine.c,v 1.29 2019/12/07 22:57:48 guenther Exp $	*/
2
3/*
4 * Copyright (c) 2013 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18/*
19 * Copyright (c) 1999 Dale Rahn
20 *
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
23 * are met:
24 * 1. Redistributions of source code must retain the above copyright
25 *    notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 *    notice, this list of conditions and the following disclaimer in the
28 *    documentation and/or other materials provided with the distribution.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
31 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
34 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 */
43
44#define _DYN_LOADER
45
46#include <sys/types.h>
47#include <sys/mman.h>
48#include <sys/syscall.h>
49#include <sys/unistd.h>
50
51#include <nlist.h>
52#include <link.h>
53
54#include "syscall.h"
55#include "archdep.h"
56#include "resolve.h"
57
58Elf_Addr _dl_bind(elf_object_t *object, int reloff);
59void	_dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr);
60
61int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
62
63int
64_dl_md_reloc(elf_object_t *object, int rel, int relasz)
65{
66	int	i;
67	int	numrela;
68	int	relrela;
69	int	fails = 0;
70	Elf_Addr loff;
71	Elf_RelA  *relas;
72	Elf_Addr prev_value = 0, prev_ooff = 0;
73	const Elf_Sym *prev_sym = NULL;
74
75	loff = object->obj_base;
76	numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
77	relrela = rel == DT_RELA ? object->relacount : 0;
78
79	relas = (Elf_RelA *)(object->Dyn.info[rel]);
80
81	if (relas == NULL)
82		return 0;
83
84	if (relrela > numrela)
85		_dl_die("relacount > numrel: %d > %d", relrela, numrela);
86
87	/* tight loop for leading RELATIVE relocs */
88	for (i = 0; i < relrela; i++, relas++) {
89		Elf_Addr *r_addr;
90
91		r_addr = (Elf_Addr *)(relas->r_offset + loff);
92		*r_addr = relas->r_addend + loff;
93	}
94	for (; i < numrela; i++, relas++) {
95		Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
96		Elf_Addr addend, newval;
97		const Elf_Sym *sym;
98		const char *symn;
99		int type;
100
101		type = ELF_R_TYPE(relas->r_info);
102
103		if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
104			continue;
105
106		if (type == RELOC_NONE)
107			continue;
108
109		sym = object->dyn.symtab;
110		sym += ELF_R_SYM(relas->r_info);
111		symn = object->dyn.strtab + sym->st_name;
112
113		if (type == RELOC_COPY) {
114			/*
115			 * we need to find a symbol, that is not in the current
116			 * object, start looking at the beginning of the list,
117			 * searching all objects but _not_ the current object,
118			 * first one found wins.
119			 */
120			struct sym_res sr;
121
122			sr = _dl_find_symbol(symn,
123			    SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
124			    sym, object);
125			if (sr.sym != NULL) {
126				_dl_bcopy((void *)(sr.obj->obj_base +
127				    sr.sym->st_value), r_addr, sym->st_size);
128			} else
129				fails++;
130
131			continue;
132		}
133
134		if (ELF_R_SYM(relas->r_info) &&
135		    !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
136		    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
137		    sym != prev_sym) {
138			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
139			    ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
140				prev_sym = sym;
141				prev_value = 0;
142				prev_ooff = object->obj_base;
143			} else {
144				struct sym_res sr;
145
146				sr = _dl_find_symbol(symn,
147				    SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
148				    ((type == RELOC_GOTP_ENT) ?
149				    SYM_PLT : SYM_NOTPLT), sym, object);
150
151				if (sr.sym == NULL) {
152					if (ELF_ST_BIND(sym->st_info) !=
153					    STB_WEAK)
154						fails++;
155					continue;
156				}
157				prev_sym = sym;
158				prev_value = sr.sym->st_value;
159				prev_ooff = sr.obj->obj_base;
160			}
161		}
162
163		if (type == RELOC_GOTP_ENT) {
164			_dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
165			    relas->r_addend + loff,
166			    prev_ooff + prev_value);
167			continue;
168		}
169
170		if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
171		    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
172		    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE))
173			addend = relas->r_addend;
174		else
175			addend = prev_value + relas->r_addend;
176
177		switch (type) {
178		case RELOC_16L:
179			newval = prev_ooff + addend;
180			*(unsigned short *)r_addr = newval & 0xffff;
181			_dl_cacheflush((unsigned long)r_addr, 2);
182			break;
183		case RELOC_16H:
184			newval = prev_ooff + addend;
185			*(unsigned short *)r_addr = newval >> 16;
186			_dl_cacheflush((unsigned long)r_addr, 2);
187			break;
188		case RELOC_DISP26:
189			newval = prev_ooff + addend;
190			newval -= (Elf_Addr)r_addr;
191			if ((newval >> 28) != 0 && (newval >> 28) != 0x0f)
192				_dl_die("%s: out of range DISP26"
193				    " relocation to '%s' at %p\n",
194				    object->load_name, symn, (void *)r_addr);
195			*r_addr = (*r_addr & 0xfc000000) |
196			    (((int32_t)newval >> 2) & 0x03ffffff);
197			_dl_cacheflush((unsigned long)r_addr, 4);
198			break;
199		case RELOC_32:
200			newval = prev_ooff + addend;
201			*r_addr = newval;
202			break;
203		case RELOC_BBASED_32:
204			newval = loff + addend;
205			*r_addr = newval;
206			break;
207		default:
208			_dl_die("%s: unsupported relocation '%s' %d at %p\n",
209			    object->load_name, symn, type, (void *)r_addr);
210		}
211	}
212
213	return fails;
214}
215
216/*
217 * GOTP_ENT relocations are special in that they define both a .got and a
218 * .plt relocation.
219 */
220void
221_dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
222{
223	uint16_t *plt_entry = (uint16_t *)plt_addr;
224
225	/* .got update */
226	*(Elf_Addr *)got_addr = val;
227	/* .plt update */
228	plt_entry[1] = got_addr >> 16;
229	plt_entry[3] = got_addr & 0xffff;
230}
231
232/*
233 *	Relocate the Global Offset Table (GOT).
234 *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
235 *	otherwise the lazy binding plt operation is preserved.
236 */
237int
238_dl_md_reloc_got(elf_object_t *object, int lazy)
239{
240	extern void _dl_bind_start(void);	/* XXX */
241	int	fails = 0;
242	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
243	Elf_Addr plt_start, plt_end;
244
245	if (pltgot == NULL)
246		return 0;
247
248	pltgot[1] = (Elf_Addr)object;
249	pltgot[2] = (Elf_Addr)_dl_bind_start;
250
251	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
252		return 0;
253
254	if (!lazy) {
255		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
256	} else {
257		if (object->obj_base != 0) {
258			int cnt;
259			Elf_Addr *addr;
260			Elf_RelA *rela;
261
262			cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
263			rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
264
265			for (; cnt != 0; cnt--, rela++) {
266				addr = (Elf_Addr *)(object->obj_base +
267				    rela->r_offset);
268				_dl_md_reloc_gotp_ent((Elf_Addr)addr,
269				    object->obj_base + rela->r_addend,
270				    *addr + object->obj_base);
271			}
272		}
273	}
274
275	/*
276	 * Force a cache sync here on the whole PLT if we updated it
277	 * (and have the DT entries to find what we need to flush),
278	 * otherwise I$ might have stale information.
279	 */
280	plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM];
281	plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM];
282	if ((!lazy || object->obj_base != 0) && plt_start != 0 &&
283	    plt_end != 0) {
284		size_t plt_size = plt_end - plt_start;
285		if (plt_size != 0)
286			_dl_cacheflush(plt_start + object->obj_base, plt_size);
287	}
288
289	return fails;
290}
291
292Elf_Addr
293_dl_bind(elf_object_t *object, int reloff)
294{
295	Elf_RelA *rel;
296	struct sym_res sr;
297	const Elf_Sym *sym;
298	const char *symn;
299	uint64_t cookie = pcookie;
300	struct {
301		struct __kbind param;
302		Elf_Addr newval;
303	} buf;
304
305	rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
306
307	sym = object->dyn.symtab;
308	sym += ELF_R_SYM(rel->r_info);
309	symn = object->dyn.strtab + sym->st_name;
310
311	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
312	    sym, object);
313	if (sr.sym == NULL)
314		_dl_die("lazy binding failed!");
315
316	buf.newval = sr.obj->obj_base + sr.sym->st_value;
317
318	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
319		return buf.newval;
320
321	buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
322	buf.param.kb_size = sizeof(Elf_Addr);
323
324	/* directly code the syscall, so that it's actually inline here */
325	{
326		register long syscall_num __asm("r13") = SYS_kbind;
327		register void *arg1 __asm("r2") = &buf;
328		register long  arg2 __asm("r3") = sizeof(buf);
329		register long  arg3 __asm("r4") = 0xffffffff & (cookie >> 32);
330		register long  arg4 __asm("r5") = 0xffffffff &  cookie;
331
332		__asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0"
333		    : "+r" (arg1), "+r" (arg2) : "r" (syscall_num),
334		    "r" (arg3), "r" (arg4) : "memory");
335	}
336
337	return buf.newval;
338}
339