xref: /openbsd/libexec/ld.so/hppa/rtld_machine.c (revision 998de4a5)
1 /*	$OpenBSD: rtld_machine.c,v 1.34 2016/08/27 22:52:21 guenther 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/mman.h>
36 #include <sys/tree.h>
37 #include <sys/syscall.h>
38 #include <sys/unistd.h>
39 
40 #include <machine/vmparam.h>	/* SYSCALLGATE */
41 
42 #include <nlist.h>
43 #include <link.h>
44 #include <string.h>
45 
46 #include "syscall.h"
47 #include "archdep.h"
48 #define	_dl_bind XXX_dl_bind
49 #include "resolve.h"
50 #undef	_dl_bind
51 uint64_t _dl_bind(elf_object_t *object, int reloff);
52 
53 typedef
54 struct hppa_plabel {
55 	Elf_Addr	pc;
56 	Elf_Addr	*sl;
57 	SPLAY_ENTRY(hppa_plabel) node;
58 } hppa_plabel_t;
59 SPLAY_HEAD(_dl_md_plabels, hppa_plabel) _dl_md_plabel_root;
60 
61 void	_hppa_dl_set_dp(Elf_Addr *dp);	/* from ldasm.S */
62 
63 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
64 
65 static __inline int
66 _dl_md_plcmp(hppa_plabel_t *a, hppa_plabel_t *b)
67 {
68 	if (a->sl < b->sl)
69 		return (-1);
70 	else if (a->sl > b->sl)
71 		return (1);
72 	else if (a->pc < b->pc)
73 		return (-1);
74 	else if (a->pc > b->pc)
75 		return (1);
76 	else
77 		return (0);
78 }
79 
80 SPLAY_PROTOTYPE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
81 SPLAY_GENERATE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
82 
83 Elf_Addr
84 _dl_md_plabel(Elf_Addr pc, Elf_Addr *sl)
85 {
86 	hppa_plabel_t key, *p;
87 
88 	key.pc = pc;
89 	key.sl = sl;
90 	p = SPLAY_FIND(_dl_md_plabels, &_dl_md_plabel_root, &key);
91 	if (p == NULL) {
92 		p = _dl_malloc(sizeof(*p));
93 		if (p == NULL)
94 			_dl_exit(5);
95 		p->pc = pc;
96 		p->sl = sl;
97 		SPLAY_INSERT(_dl_md_plabels, &_dl_md_plabel_root, p);
98 	}
99 
100 	return (Elf_Addr)p | 2;
101 }
102 
103 int
104 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
105 {
106 	Elf_RelA	*rela;
107 	Elf_Addr	loff;
108 	int	i, numrela, fails = 0;
109 	size_t	size;
110 	struct load_list *llist;
111 
112 	loff = object->obj_base;
113 	numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
114 	rela = (Elf_RelA *)(object->Dyn.info[rel]);
115 
116 #ifdef DEBUG
117 	DL_DEB(("object %s relasz %x, numrela %x loff %x\n",
118 	    object->load_name, object->Dyn.info[relasz], numrela, loff));
119 #endif
120 
121 	if (rela == NULL)
122 		return (0);
123 
124 	/* either it's an ld bug or a wacky hpux abi */
125 	if (!object->dyn.pltgot)
126 		object->Dyn.info[DT_PLTGOT] += loff;
127 
128 	if (object->dyn.init && !((Elf_Addr)object->dyn.init & 2)) {
129 		Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.init,
130 		    object->dyn.pltgot);
131 #ifdef DEBUG
132 		DL_DEB(("PLABEL32: %p:%p(_init) -> 0x%x in %s\n",
133 		    object->dyn.init, object->dyn.pltgot,
134 		    addr, object->load_name));
135 #endif
136 		object->dyn.init = (void *)addr;
137 	}
138 
139 	if (object->dyn.fini && !((Elf_Addr)object->dyn.fini & 2)) {
140 		Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.fini,
141 		    object->dyn.pltgot);
142 #ifdef DEBUG
143 		DL_DEB(("PLABEL32: %p:%p(_fini) -> 0x%x in %s\n",
144 		    object->dyn.fini, object->dyn.pltgot,
145 		    addr, object->load_name));
146 #endif
147 		object->dyn.fini = (void *)addr;
148 	}
149 
150 	/*
151 	 * unprotect some segments if we need it.
152 	 */
153 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
154 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
155 			if (!(llist->prot & PROT_WRITE))
156 				_dl_mprotect(llist->start, llist->size,
157 				    PROT_READ|PROT_WRITE);
158 		}
159 	}
160 
161 	/*
162 	 * this is normally done by the crt0 code but we have to make
163 	 * sure it's set here to allow constructors to call functions
164 	 * that are overridden in the user binary (that are un-pic)
165 	 */
166 	if (object->obj_type == OBJTYPE_EXE)
167 		_hppa_dl_set_dp(object->dyn.pltgot);
168 
169 	for (i = 0; i < numrela; i++, rela++) {
170 		const elf_object_t *sobj;
171 		const Elf_Sym *sym, *this;
172 		Elf_Addr *pt, ooff;
173 		const char *symn;
174 		int type;
175 
176 		type = ELF_R_TYPE(rela->r_info);
177 		if (type == RELOC_NONE)
178 			continue;
179 
180 		sym = object->dyn.symtab + ELF_R_SYM(rela->r_info);
181 		sobj = object;
182 		symn = object->dyn.strtab + sym->st_name;
183 		pt = (Elf_Addr *)(rela->r_offset + loff);
184 
185 		ooff = 0;
186 		this = NULL;
187 		if (ELF_R_SYM(rela->r_info) && sym->st_name) {
188 			ooff = _dl_find_symbol_bysym(object,
189 			    ELF_R_SYM(rela->r_info), &this,
190 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
191 			    ((type == RELOC_IPLT) ? SYM_PLT: SYM_NOTPLT),
192 			    sym, &sobj);
193 			if (this == NULL) {
194 				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
195 					fails++;
196 				continue;
197 			}
198 		}
199 
200 #ifdef DEBUG
201 		DL_DEB(("*pt=%x r_addend=%x r_sym=%x\n",
202 		    *pt, rela->r_addend, ELF_R_SYM(rela->r_info)));
203 #endif
204 
205 		switch (type) {
206 		case RELOC_DIR32:
207 			if (ELF_R_SYM(rela->r_info) && sym->st_name) {
208 				*pt = ooff + this->st_value + rela->r_addend;
209 #ifdef DEBUG
210 				DL_DEB(("[%x]DIR32: %s:%s -> 0x%x in %s\n",
211 				    i, symn, object->load_name,
212 				    *pt, sobj->load_name));
213 #endif
214 			} else {
215 				/*
216 				 * XXX should objects ever get their
217 				 * sections loaded insequential this
218 				 * would have to get a section number
219 				 * (ELF_R_SYM(rela->r_info))-1 and then:
220 				 *    *pt = sect->addr + rela->r_addend;
221 				 */
222 				if (ELF_R_SYM(rela->r_info))
223 					*pt += loff;
224 				else
225 					*pt += loff + rela->r_addend;
226 #ifdef DEBUG
227 				DL_DEB(("[%x]DIR32: %s @ 0x%x\n", i,
228 				    object->load_name, *pt));
229 #endif
230 			}
231 			break;
232 
233 		case RELOC_PLABEL32:
234 			if (ELF_R_SYM(rela->r_info)) {
235 				if (ELF_ST_TYPE(this->st_info) != STT_FUNC) {
236 					DL_DEB(("[%x]PLABEL32: bad\n", i));
237 					break;
238 				}
239 				*pt = _dl_md_plabel(sobj->obj_base +
240 				    this->st_value + rela->r_addend,
241 				    sobj->dyn.pltgot);
242 #ifdef DEBUG
243 				DL_DEB(("[%x]PLABEL32: %s:%s -> 0x%x in %s\n",
244 				    i, symn, object->load_name,
245 				    *pt, sobj->load_name));
246 #endif
247 			} else {
248 				*pt = loff + rela->r_addend;
249 #ifdef DEBUG
250 				DL_DEB(("[%x]PLABEL32: %s @ 0x%x\n", i,
251 				    object->load_name, *pt));
252 #endif
253 			}
254 			break;
255 
256 		case RELOC_IPLT:
257 			if (ELF_R_SYM(rela->r_info)) {
258 				pt[0] = ooff + this->st_value + rela->r_addend;
259 				pt[1] = (Elf_Addr)sobj->dyn.pltgot;
260 #ifdef DEBUG
261 				DL_DEB(("[%x]IPLT: %s:%s -> 0x%x:0x%x in %s\n",
262 				    i, symn, object->load_name,
263 				    pt[0], pt[1], sobj->load_name));
264 #endif
265 			} else {
266 				pt[0] = loff + rela->r_addend;
267 				pt[1] = (Elf_Addr)object->dyn.pltgot;
268 #ifdef DEBUG
269 				DL_DEB(("[%x]IPLT: %s @ 0x%x:0x%x\n", i,
270 				    object->load_name, pt[0], pt[1]));
271 #endif
272 			}
273 			break;
274 
275 		case RELOC_COPY:
276 		{
277 			const Elf32_Sym *cpysrc = NULL;
278 			size = sym->st_size;
279 			ooff = _dl_find_symbol(symn, &cpysrc,
280 			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
281 			    sym, object, NULL);
282 			if (cpysrc) {
283 				_dl_bcopy((void *)(ooff + cpysrc->st_value),
284 				    pt, sym->st_size);
285 #ifdef DEBUG
286 				DL_DEB(("[%x]COPY: %s[%x]:%s -> %p[%x] in %s\n",
287 				    i, symn, ooff + cpysrc->st_value,
288 				    object->load_name, pt, sym->st_size,
289 				    sobj->load_name));
290 #endif
291 			} else
292 				DL_DEB(("[%x]COPY: no sym\n", i));
293 			break;
294 		}
295 		default:
296 			DL_DEB(("[%x]UNKNOWN(%d): type=%d off=0x%lx "
297 			    "addend=0x%lx rel=0x%x\n", i, type,
298 			    ELF_R_TYPE(rela->r_info), rela->r_offset,
299 			    rela->r_addend, *pt));
300 			break;
301 		}
302 	}
303 
304 	/* reprotect the unprotected segments */
305 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
306 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
307 			if (!(llist->prot & PROT_WRITE))
308 				_dl_mprotect(llist->start, llist->size,
309 				    llist->prot);
310 		}
311 	}
312 
313 	return (fails);
314 }
315 
316 extern void _dl_bind_start(void);
317 
318 #define PLT_STUB_SIZE	(7 * 4)
319 #define PLT_ENTRY_SIZE	(2 * 4)
320 #define PLT_STUB_GOTOFF	(4 * 4)
321 
322 #define PLT_STUB_MAGIC1	0x00c0ffee
323 #define PLT_STUB_MAGIC2	0xdeadbeef
324 
325 #define PLT_STUB_INSN1	0x0e801081	/* ldw	0(%r20), %r1 */
326 #define PLT_STUB_INSN2	0xe820c000	/* bv	%r0(%r1) */
327 
328 int
329 _dl_md_reloc_got(elf_object_t *object, int lazy)
330 {
331 	Elf_RelA *rela;
332 	Elf_Addr  ooff;
333 	int	i, numrela, fails = 0;
334 
335 	if (object->dyn.pltrel != DT_RELA)
336 		return (0);
337 
338 	if (object->traced)
339 		lazy = 1;
340 
341 	if (!lazy) {
342 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
343 	} else {
344 		register Elf_Addr ltp __asm ("%r19");
345 		Elf_Addr *got = NULL;
346 
347 		rela = (Elf_RelA *)(object->dyn.jmprel);
348 		numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
349 		ooff = object->obj_base;
350 
351 		/*
352 		 * Find the PLT stub by looking at all the
353 		 * relocations.  The PLT stub should be at the end of
354 		 * the .plt section so we start with the last
355 		 * relocation, since the linker should have emitted
356 		 * them in order.
357 		 */
358 		for (i = numrela - 1; i >= 0; i--) {
359 			got = (Elf_Addr *)(ooff + rela[i].r_offset +
360 			    PLT_ENTRY_SIZE + PLT_STUB_SIZE);
361 			if (got[-2] == PLT_STUB_MAGIC1 ||
362 			    got[-1] == PLT_STUB_MAGIC2)
363 				break;
364 			got = NULL;
365 		}
366 		if (got == NULL)
367 			return (1);
368 
369 		/*
370 		 * Patch up the PLT stub such that it doesn't clobber
371 		 * %r22, which is used to pass on the errno values
372 		 * from failed system calls to __cerrno() in libc.
373 		 */
374 		got[-7] = PLT_STUB_INSN1;
375 		got[-6] = PLT_STUB_INSN2;
376 		__asm volatile("fdc 0(%0)" :: "r" (&got[-7]));
377 		__asm volatile("fdc 0(%0)" :: "r" (&got[-6]));
378 		__asm volatile("sync");
379 		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-7]));
380 		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-6]));
381 		__asm volatile("sync");
382 
383 		/*
384 		 * Fill in the PLT stub such that it invokes the
385 		 * _dl_bind_start() trampoline to fix up the
386 		 * relocation.
387 		 */
388 		got[1] = (Elf_Addr)object;
389 		got[-2] = (Elf_Addr)&_dl_bind_start;
390 		got[-1] = ltp;
391 		/*
392 		 * We need the real address of the trampoline.  Get it
393 		 * from the function descriptor if that's what we got.
394 		 */
395 		if (got[-2] & 2) {
396 			hppa_plabel_t *p = (hppa_plabel_t *)(got[-2] & ~2);
397 			got[-2] = p->pc;
398 		}
399 		/*
400 		 * Even though we didn't modify any instructions it
401 		 * seems we still need to synchronize the caches.
402 		 * There may be instructions in the same cache line
403 		 * and they end up being corrupted otherwise.
404 		 */
405 		__asm volatile("fdc 0(%0)" :: "r" (&got[-2]));
406 		__asm volatile("fdc 0(%0)" :: "r" (&got[-1]));
407 		__asm volatile("sync");
408 		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-2]));
409 		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-1]));
410 		__asm volatile("sync");
411 		for (i = 0; i < numrela; i++, rela++) {
412 			Elf_Addr *r_addr = (Elf_Addr *)(ooff + rela->r_offset);
413 
414 			if (ELF_R_TYPE(rela->r_info) != RELOC_IPLT) {
415 				_dl_printf("unexpected reloc 0x%x\n",
416 				    ELF_R_TYPE(rela->r_info));
417 				return (1);
418 			}
419 
420 			if (ELF_R_SYM(rela->r_info)) {
421 				r_addr[0] = (Elf_Addr)got - PLT_STUB_GOTOFF;
422 				r_addr[1] = (Elf_Addr) (rela -
423 				    (Elf_RelA *)object->dyn.jmprel);
424 			} else {
425 				r_addr[0] = ooff + rela->r_addend;
426 				r_addr[1] = (Elf_Addr)object->dyn.pltgot;
427 			}
428 		}
429 	}
430 
431 	/* mprotect the GOT */
432 	_dl_protect_segment(object, 0, "__got_start", "__got_end",
433 	    GOT_PERMS|PROT_EXEC);
434 
435 	return (fails);
436 }
437 
438 /*
439  * Resolve a symbol at run-time.
440  */
441 uint64_t
442 _dl_bind(elf_object_t *object, int reloff)
443 {
444 	const elf_object_t *sobj;
445 	const Elf_Sym *sym, *this;
446 	Elf_Addr ooff;
447 	const char *symn;
448 	Elf_Addr value;
449 	Elf_RelA *rela;
450 	uint64_t cookie = pcookie;
451 	struct {
452 		struct __kbind param;
453 		uint64_t newval;
454 	} buf;
455 
456 	rela = (Elf_RelA *)object->dyn.jmprel + reloff;
457 
458 	sym = object->dyn.symtab;
459 	sym += ELF_R_SYM(rela->r_info);
460 	symn = object->dyn.strtab + sym->st_name;
461 
462 	this = NULL;
463 	ooff = _dl_find_symbol(symn, &this,
464 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
465 	if (this == NULL) {
466 		_dl_printf("lazy binding failed!\n");
467 		*(volatile int *)0 = 0;		/* XXX */
468 	}
469 
470 	value = ooff + this->st_value + rela->r_addend;
471 
472 	buf.newval = ((uint64_t)value << 32) | (Elf_Addr)sobj->dyn.pltgot;
473 
474 	if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn))
475 		return (buf.newval);
476 
477 	buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
478 	buf.param.kb_size = sizeof(uint64_t);
479 
480 	/* directly code the syscall, so that it's actually inline here */
481 	{
482 		register long r1 __asm__("r1") = SYSCALLGATE;
483 		register void *arg0 __asm__("r26") = &buf;
484 		register long arg1 __asm__("r25") = sizeof(buf);
485 		register long arg2 __asm__("r24") = 0xffffffff & (cookie >> 32);
486 		register long arg3 __asm__("r23") = 0xffffffff & cookie;
487 		__asm__ __volatile__ ("ble 4(%%sr7, %%r1) ! ldi %0, %%r22"
488 		    :
489 		    : "i" (SYS_kbind), "r" (r1), "r"(arg0), "r"(arg1),
490 		      "r"(arg2), "r"(arg3)
491 		    : "r22", "r28", "r29", "cc", "memory");
492 	}
493 
494 	return (buf.newval);
495 }
496