1/*	$OpenBSD: rtld_machine.c,v 1.44 2022/08/29 02:08:13 jsg Exp $	*/
2
3/*
4 * Copyright (c) 2004 Michael Shalayeff
5 * Copyright (c) 2001 Niklas Hallqvist
6 * Copyright (c) 2001 Artur Grabowski
7 * Copyright (c) 1999 Dale Rahn
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
28 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#define _DYN_LOADER
33
34#include <sys/types.h>
35#include <sys/exec_elf.h>
36#include <sys/syscall.h>
37#include <sys/tree.h>
38#include <sys/unistd.h>
39
40#include <machine/reloc.h>
41#include <machine/vmparam.h>	/* SYSCALLGATE */
42
43#include "util.h"
44#define	_dl_bind XXX_dl_bind
45#include "resolve.h"
46#undef	_dl_bind
47uint64_t _dl_bind(elf_object_t *object, int reloff);
48
49typedef
50struct hppa_plabel {
51	Elf_Addr	pc;
52	Elf_Addr	*sl;
53	SPLAY_ENTRY(hppa_plabel) node;
54} hppa_plabel_t;
55SPLAY_HEAD(_dl_md_plabels, hppa_plabel) _dl_md_plabel_root;
56
57void	_hppa_dl_set_dp(Elf_Addr *dp);	/* from ldasm.S */
58
59int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
60
61static __inline int
62_dl_md_plcmp(hppa_plabel_t *a, hppa_plabel_t *b)
63{
64	if (a->sl < b->sl)
65		return -1;
66	else if (a->sl > b->sl)
67		return 1;
68	else if (a->pc < b->pc)
69		return -1;
70	else if (a->pc > b->pc)
71		return 1;
72	else
73		return 0;
74}
75
76SPLAY_PROTOTYPE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
77SPLAY_GENERATE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
78
79Elf_Addr
80_dl_md_plabel(Elf_Addr pc, Elf_Addr *sl)
81{
82	hppa_plabel_t key, *p;
83
84	key.pc = pc;
85	key.sl = sl;
86	p = SPLAY_FIND(_dl_md_plabels, &_dl_md_plabel_root, &key);
87	if (p == NULL) {
88		p = _dl_malloc(sizeof(*p));
89		if (p == NULL)
90			_dl_oom();
91		p->pc = pc;
92		p->sl = sl;
93		SPLAY_INSERT(_dl_md_plabels, &_dl_md_plabel_root, p);
94	}
95
96	return (Elf_Addr)p | 2;
97}
98
99int
100_dl_md_reloc(elf_object_t *object, int rel, int relasz)
101{
102	Elf_RelA	*rela;
103	Elf_Addr	loff;
104	int	num_relative;
105	int	i, numrela, fails = 0;
106
107	loff = object->obj_base;
108	numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
109	num_relative = rel == DT_RELA ? object->relacount : 0;
110	rela = (Elf_RelA *)(object->Dyn.info[rel]);
111
112#ifdef DEBUG
113	DL_DEB(("object %s relasz %x, numrela %x loff %x\n",
114	    object->load_name, object->Dyn.info[relasz], numrela, loff));
115#endif
116
117	if (rela == NULL)
118		return 0;
119
120	/* either it's an ld bug or a wacky hpux abi */
121	if (!object->dyn.pltgot)
122		object->Dyn.info[DT_PLTGOT] += loff;
123
124	if (object->dyn.init && !((Elf_Addr)object->dyn.init & 2)) {
125		Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.init,
126		    object->dyn.pltgot);
127#ifdef DEBUG
128		DL_DEB(("PLABEL32: %p:%p(_init) -> 0x%x in %s\n",
129		    object->dyn.init, object->dyn.pltgot,
130		    addr, object->load_name));
131#endif
132		object->dyn.init = (void *)addr;
133	}
134
135	if (object->dyn.fini && !((Elf_Addr)object->dyn.fini & 2)) {
136		Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.fini,
137		    object->dyn.pltgot);
138#ifdef DEBUG
139		DL_DEB(("PLABEL32: %p:%p(_fini) -> 0x%x in %s\n",
140		    object->dyn.fini, object->dyn.pltgot,
141		    addr, object->load_name));
142#endif
143		object->dyn.fini = (void *)addr;
144	}
145
146	/*
147	 * this is normally done by the crt0 code but we have to make
148	 * sure it's set here to allow constructors to call functions
149	 * that are overridden in the user binary (that are un-pic)
150	 */
151	if (object->obj_type == OBJTYPE_EXE)
152		_hppa_dl_set_dp(object->dyn.pltgot);
153
154	/* tight loop for leading relative relocs */
155	for (i = 0; i < num_relative; i++, rela++) {
156		Elf_Addr *where = (Elf_Addr *)(rela->r_offset + loff);
157		*where = rela->r_addend + loff;
158	}
159	for (; i < numrela; i++, rela++) {
160		struct sym_res sr;
161		const Elf_Sym *sym;
162		Elf_Addr *pt;
163		const char *symn;
164		int type;
165
166		type = ELF_R_TYPE(rela->r_info);
167		if (type == RELOC_NONE)
168			continue;
169
170		sym = object->dyn.symtab + ELF_R_SYM(rela->r_info);
171		symn = object->dyn.strtab + sym->st_name;
172		pt = (Elf_Addr *)(rela->r_offset + loff);
173
174		if (ELF_R_SYM(rela->r_info) && sym->st_name) {
175			sr = _dl_find_symbol(symn,
176			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
177			    sym, object);
178			if (sr.sym == NULL) {
179				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
180					fails++;
181				continue;
182			}
183		} else {
184			sr.sym = NULL;
185			sr.obj = object;
186		}
187
188#ifdef DEBUG
189		DL_DEB(("*pt=%x r_addend=%x r_sym=%x\n",
190		    *pt, rela->r_addend, ELF_R_SYM(rela->r_info)));
191#endif
192
193		switch (type) {
194		case RELOC_DIR32:
195			if (ELF_R_SYM(rela->r_info) && sym->st_name) {
196				*pt = sr.obj->obj_base + sr.sym->st_value +
197				    rela->r_addend;
198#ifdef DEBUG
199				DL_DEB(("[%x]DIR32: %s:%s -> 0x%x in %s\n",
200				    i, symn, object->load_name,
201				    *pt, sr.obj->load_name));
202#endif
203			} else {
204				/*
205				 * Either a relative relocation (symbol 0)
206				 * or a relocation against a local section
207				 */
208				*pt = loff + sym->st_value + rela->r_addend;
209#ifdef DEBUG
210				DL_DEB(("[%x]DIR32: %s @ 0x%x\n", i,
211				    object->load_name, *pt));
212#endif
213			}
214			break;
215
216		case RELOC_PLABEL32:
217			if (ELF_R_SYM(rela->r_info)) {
218				if (ELF_ST_TYPE(sr.sym->st_info) != STT_FUNC) {
219					DL_DEB(("[%x]PLABEL32: bad\n", i));
220					break;
221				}
222				*pt = _dl_md_plabel(sr.obj->obj_base +
223				    sr.sym->st_value + rela->r_addend,
224				    sr.obj->dyn.pltgot);
225#ifdef DEBUG
226				DL_DEB(("[%x]PLABEL32: %s:%s -> 0x%x in %s\n",
227				    i, symn, object->load_name,
228				    *pt, sr.obj->load_name));
229#endif
230			} else {
231				*pt = loff + rela->r_addend;
232#ifdef DEBUG
233				DL_DEB(("[%x]PLABEL32: %s @ 0x%x\n", i,
234				    object->load_name, *pt));
235#endif
236			}
237			break;
238
239		case RELOC_IPLT:
240			if (ELF_R_SYM(rela->r_info)) {
241				pt[0] = sr.obj->obj_base + sr.sym->st_value +
242				    rela->r_addend;
243				pt[1] = (Elf_Addr)sr.obj->dyn.pltgot;
244#ifdef DEBUG
245				DL_DEB(("[%x]IPLT: %s:%s -> 0x%x:0x%x in %s\n",
246				    i, symn, object->load_name,
247				    pt[0], pt[1], sr.obj->load_name));
248#endif
249			} else {
250				pt[0] = loff + rela->r_addend;
251				pt[1] = (Elf_Addr)object->dyn.pltgot;
252#ifdef DEBUG
253				DL_DEB(("[%x]IPLT: %s @ 0x%x:0x%x\n", i,
254				    object->load_name, pt[0], pt[1]));
255#endif
256			}
257			break;
258
259		case RELOC_COPY:
260		{
261			sr = _dl_find_symbol(symn,
262			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
263			    sym, object);
264			if (sr.sym) {
265				_dl_bcopy((void *)(sr.obj->obj_base +
266				    sr.sym->st_value), pt, sym->st_size);
267#ifdef DEBUG
268				DL_DEB(("[%x]COPY: %s[%x]:%s -> %p[%x] in %s\n",
269				    i, symn, sr.obj->obj_base +
270				    sr.sym->st_value, object->load_name,
271				    pt, sym->st_size, sr.obj->load_name));
272#endif
273			} else
274				DL_DEB(("[%x]COPY: no sym\n", i));
275			break;
276		}
277		default:
278			DL_DEB(("[%x]UNKNOWN(%d): type=%d off=0x%lx "
279			    "addend=0x%lx rel=0x%x\n", i, type,
280			    ELF_R_TYPE(rela->r_info), rela->r_offset,
281			    rela->r_addend, *pt));
282			break;
283		}
284	}
285
286	return fails;
287}
288
289extern void _dl_bind_start(void);
290
291#define PLT_STUB_SIZE	(7 * 4)
292#define PLT_ENTRY_SIZE	(2 * 4)
293#define PLT_STUB_GOTOFF	(4 * 4)
294
295#define PLT_STUB_MAGIC1	0x00c0ffee
296#define PLT_STUB_MAGIC2	0xdeadbeef
297
298#define PLT_STUB_INSN1	0x0e801081	/* ldw	0(%r20), %r1 */
299#define PLT_STUB_INSN2	0xe820c000	/* bv	%r0(%r1) */
300
301int
302_dl_md_reloc_got(elf_object_t *object, int lazy)
303{
304	Elf_RelA *rela;
305	Elf_Addr  ooff;
306	int	i, numrela, fails = 0;
307
308	if (object->dyn.pltrel != DT_RELA)
309		return 0;
310
311	if (!lazy) {
312		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
313	} else {
314		register Elf_Addr ltp __asm ("%r19");
315		Elf_Addr *got = NULL;
316
317		rela = (Elf_RelA *)(object->dyn.jmprel);
318		numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
319		ooff = object->obj_base;
320
321		/*
322		 * Find the PLT stub by looking at all the
323		 * relocations.  The PLT stub should be at the end of
324		 * the .plt section so we start with the last
325		 * relocation, since the linker should have emitted
326		 * them in order.
327		 */
328		for (i = numrela - 1; i >= 0; i--) {
329			got = (Elf_Addr *)(ooff + rela[i].r_offset +
330			    PLT_ENTRY_SIZE + PLT_STUB_SIZE);
331			if (got[-2] == PLT_STUB_MAGIC1 ||
332			    got[-1] == PLT_STUB_MAGIC2)
333				break;
334			got = NULL;
335		}
336		if (got == NULL)
337			return 1;
338
339		/*
340		 * Patch up the PLT stub such that it doesn't clobber
341		 * %r22, which is used to pass on the errno values
342		 * from failed system calls to __cerrno() in libc.
343		 */
344		got[-7] = PLT_STUB_INSN1;
345		got[-6] = PLT_STUB_INSN2;
346		__asm volatile("fdc 0(%0)" :: "r" (&got[-7]));
347		__asm volatile("fdc 0(%0)" :: "r" (&got[-6]));
348		__asm volatile("sync");
349		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-7]));
350		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-6]));
351		__asm volatile("sync");
352
353		/*
354		 * Fill in the PLT stub such that it invokes the
355		 * _dl_bind_start() trampoline to fix up the
356		 * relocation.
357		 */
358		got[1] = (Elf_Addr)object;
359		got[-2] = (Elf_Addr)&_dl_bind_start;
360		got[-1] = ltp;
361		/*
362		 * We need the real address of the trampoline.  Get it
363		 * from the function descriptor if that's what we got.
364		 */
365		if (got[-2] & 2) {
366			hppa_plabel_t *p = (hppa_plabel_t *)(got[-2] & ~2);
367			got[-2] = p->pc;
368		}
369		/*
370		 * Even though we didn't modify any instructions it
371		 * seems we still need to synchronize the caches.
372		 * There may be instructions in the same cache line
373		 * and they end up being corrupted otherwise.
374		 */
375		__asm volatile("fdc 0(%0)" :: "r" (&got[-2]));
376		__asm volatile("fdc 0(%0)" :: "r" (&got[-1]));
377		__asm volatile("sync");
378		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-2]));
379		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-1]));
380		__asm volatile("sync");
381		for (i = 0; i < numrela; i++, rela++) {
382			Elf_Addr *r_addr = (Elf_Addr *)(ooff + rela->r_offset);
383
384			if (ELF_R_TYPE(rela->r_info) != RELOC_IPLT) {
385				_dl_printf("unexpected reloc 0x%x\n",
386				    ELF_R_TYPE(rela->r_info));
387				return 1;
388			}
389
390			if (ELF_R_SYM(rela->r_info)) {
391				r_addr[0] = (Elf_Addr)got - PLT_STUB_GOTOFF;
392				r_addr[1] = (Elf_Addr) (rela -
393				    (Elf_RelA *)object->dyn.jmprel);
394			} else {
395				r_addr[0] = ooff + rela->r_addend;
396				r_addr[1] = (Elf_Addr)object->dyn.pltgot;
397			}
398		}
399	}
400
401	return fails;
402}
403
404/*
405 * Resolve a symbol at run-time.
406 */
407uint64_t
408_dl_bind(elf_object_t *object, int reloff)
409{
410	struct sym_res sr;
411	const Elf_Sym *sym;
412	const char *symn;
413	Elf_Addr value;
414	Elf_RelA *rela;
415	uint64_t cookie = pcookie;
416	struct {
417		struct __kbind param;
418		uint64_t newval;
419	} buf;
420
421	rela = (Elf_RelA *)object->dyn.jmprel + reloff;
422
423	sym = object->dyn.symtab;
424	sym += ELF_R_SYM(rela->r_info);
425	symn = object->dyn.strtab + sym->st_name;
426
427	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
428	    sym, object);
429	if (sr.sym == NULL)
430		_dl_die("lazy binding failed!");
431
432	value = sr.obj->obj_base + sr.sym->st_value + rela->r_addend;
433
434	buf.newval = ((uint64_t)value << 32) | (Elf_Addr)sr.obj->dyn.pltgot;
435
436	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
437		return buf.newval;
438
439	buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
440	buf.param.kb_size = sizeof(uint64_t);
441
442	/* directly code the syscall, so that it's actually inline here */
443	{
444		register long r1 __asm__("r1") = SYSCALLGATE;
445		register void *arg0 __asm__("r26") = &buf;
446		register long arg1 __asm__("r25") = sizeof(buf);
447		register long arg2 __asm__("r24") = 0xffffffff & (cookie >> 32);
448		register long arg3 __asm__("r23") = 0xffffffff & cookie;
449		__asm__ volatile ("ble 4(%%sr7, %%r1) ! ldi %0, %%r22"
450		    :
451		    : "i" (SYS_kbind), "r" (r1), "r"(arg0), "r"(arg1),
452		      "r"(arg2), "r"(arg3)
453		    : "r22", "r28", "r29", "cc", "memory");
454	}
455
456	return buf.newval;
457}
458