rtld_machine.c revision 1.7
1/*	$OpenBSD: rtld_machine.c,v 1.7 2003/02/02 16:57:58 deraadt Exp $ */
2
3/*
4 * Copyright (c) 2002 Dale Rahn
5 * Copyright (c) 2001 Niklas Hallqvist
6 * Copyright (c) 2001 Artur Grabowski
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed under OpenBSD by
19 *	Dale Rahn.
20 * 4. The name of the author may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
24 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35/*-
36 * Copyright (c) 2000 Eduardo Horvath.
37 * Copyright (c) 1999 The NetBSD Foundation, Inc.
38 * All rights reserved.
39 *
40 * This code is derived from software contributed to The NetBSD Foundation
41 * by Paul Kranenburg.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 *    notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 *    notice, this list of conditions and the following disclaimer in the
50 *    documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 *    must display the following acknowledgement:
53 *	This product includes software developed by the NetBSD
54 *	Foundation, Inc. and its contributors.
55 * 4. Neither the name of The NetBSD Foundation nor the names of its
56 *    contributors may be used to endorse or promote products derived
57 *    from this software without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
60 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
61 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
63 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
64 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
65 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
66 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
67 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
68 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
69 * POSSIBILITY OF SUCH DAMAGE.
70 */
71
72#define _DYN_LOADER
73
74#include <sys/types.h>
75#include <sys/cdefs.h>
76#include <sys/mman.h>
77
78#include <nlist.h>
79#include <link.h>
80#include <signal.h>
81
82#include "syscall.h"
83#include "archdep.h"
84#include "resolve.h"
85
86void
87_dl_bcopy(const void *src, void *dest, int size)
88{
89	const unsigned char *psrc = src;
90	unsigned char *pdest = dest;
91	int i;
92
93	for (i = 0; i < size; i++)
94		pdest[i] = psrc[i];
95}
96
97/*
98 * The following table holds for each relocation type:
99 *	- the width in bits of the memory location the relocation
100 *	  applies to (not currently used)
101 *	- the number of bits the relocation value must be shifted to the
102 *	  right (i.e. discard least significant bits) to fit into
103 *	  the appropriate field in the instruction word.
104 *	- flags indicating whether
105 *		* the relocation involves a symbol
106 *		* the relocation is relative to the current position
107 *		* the relocation is for a GOT entry
108 *		* the relocation is relative to the load address
109 *
110 */
111#define _RF_S		0x80000000		/* Resolve symbol */
112#define _RF_A		0x40000000		/* Use addend */
113#define _RF_P		0x20000000		/* Location relative */
114#define _RF_G		0x10000000		/* GOT offset */
115#define _RF_B		0x08000000		/* Load address relative */
116#define _RF_U		0x04000000		/* Unaligned */
117#define _RF_SZ(s)	(((s) & 0xff) << 8)	/* memory target size */
118#define _RF_RS(s)	((s) & 0xff)		/* right shift */
119static int reloc_target_flags[] = {
120	0,							/* NONE */
121	_RF_S|_RF_A|		_RF_SZ(32) | _RF_RS(0),		/* RELOC_32*/
122	_RF_S|_RF_A|_RF_P|	_RF_SZ(32) | _RF_RS(0),		/* PC32 */
123	_RF_G|			_RF_SZ(32) | _RF_RS(00),	/* GOT32 */
124	      _RF_A|		_RF_SZ(32) | _RF_RS(0),		/* PLT32 */
125	_RF_S|			_RF_SZ(32) | _RF_RS(0),		/* COPY */
126	_RF_S|_RF_A|		_RF_SZ(32) | _RF_RS(0),		/* GLOB_DAT */
127	_RF_S|			_RF_SZ(32) | _RF_RS(0),		/* JUMP_SLOT */
128	      _RF_A|	_RF_B|	_RF_SZ(32) | _RF_RS(0),		/* RELATIVE */
129	0,							/* GOTOFF XXX */
130	0,							/* GOTPC XXX */
131	0,							/* DUMMY 11 */
132	0,							/* DUMMY 12 */
133	0,							/* DUMMY 13 */
134	0,							/* DUMMY 14 */
135	0,							/* DUMMY 15 */
136	0,							/* DUMMY 16 */
137	0,							/* DUMMY 17 */
138	0,							/* DUMMY 18 */
139	0,							/* DUMMY 19 */
140	_RF_S|_RF_A|		_RF_SZ(16) | _RF_RS(0),		/* RELOC_16 */
141	_RF_S|_RF_A|_RF_P|	_RF_SZ(16) | _RF_RS(0),		/* PC_16 */
142	_RF_S|_RF_A|		_RF_SZ(8) | _RF_RS(0),		/* RELOC_8 */
143	_RF_S|_RF_A|_RF_P|	_RF_SZ(8) | _RF_RS(0),		/* RELOC_PC8 */
144};
145
146#define RELOC_RESOLVE_SYMBOL(t)		((reloc_target_flags[t] & _RF_S) != 0)
147#define RELOC_PC_RELATIVE(t)		((reloc_target_flags[t] & _RF_P) != 0)
148#define RELOC_BASE_RELATIVE(t)		((reloc_target_flags[t] & _RF_B) != 0)
149#define RELOC_UNALIGNED(t)		((reloc_target_flags[t] & _RF_U) != 0)
150#define RELOC_USE_ADDEND(t)		((reloc_target_flags[t] & _RF_A) != 0)
151#define RELOC_TARGET_SIZE(t)		((reloc_target_flags[t] >> 8) & 0xff)
152#define RELOC_VALUE_RIGHTSHIFT(t)	(reloc_target_flags[t] & 0xff)
153
154static long reloc_target_bitmask[] = {
155#define _BM(x)	(~(-(1ULL << (x))))
156	0,		/* NONE */
157	_BM(32),	/* RELOC_32*/
158	_BM(32),	/* PC32 */
159	_BM(32),	/* GOT32 */
160	_BM(32),	/* PLT32 */
161	0,		/* COPY */
162	_BM(32),	/* GLOB_DAT */
163	_BM(32),	/* JUMP_SLOT */
164	_BM(32),	/* RELATIVE */
165	0,		/* GOTOFF XXX */
166	0,		/* GOTPC XXX */
167	0,		/* DUMMY 11 */
168	0,		/* DUMMY 12 */
169	0,		/* DUMMY 13 */
170	0,		/* DUMMY 14 */
171	0,		/* DUMMY 15 */
172	0,		/* DUMMY 16 */
173	0,		/* DUMMY 17 */
174	0,		/* DUMMY 18 */
175	0,		/* DUMMY 19 */
176	_BM(16),	/* RELOC_16 */
177	_BM(8),		/* PC_16 */
178	_BM(8),		/* RELOC_8 */
179	_BM(8),		/* RELOC_PC8 */
180#undef _BM
181};
182#define RELOC_VALUE_BITMASK(t)	(reloc_target_bitmask[t])
183
184void _dl_reloc_plt(Elf_Addr *where, Elf_Addr value);
185
186int
187_dl_md_reloc(elf_object_t *object, int rel, int relsz)
188{
189	long	i;
190	long	numrel;
191	long	fails = 0;
192	Elf_Addr loff;
193	Elf_Rel *rels;
194	struct load_list *llist;
195
196	loff = object->load_offs;
197	numrel = object->Dyn.info[relsz] / sizeof(Elf32_Rel);
198	rels = (Elf32_Rel *)(object->Dyn.info[rel]);
199	if (rels == NULL)
200		return(0);
201
202	/*
203	 * unprotect some segments if we need it.
204	 */
205	if ((rel == DT_REL || rel == DT_RELA)) {
206		for (llist = object->load_list; llist != NULL; llist = llist->next) {
207			if (!(llist->prot & PROT_WRITE))
208				_dl_mprotect(llist->start, llist->size,
209				    llist->prot|PROT_WRITE);
210		}
211	}
212
213	for (i = 0; i < numrel; i++, rels++) {
214		Elf_Addr *where, value, ooff, mask;
215		Elf_Word type;
216		const Elf_Sym *sym, *this;
217		const char *symn;
218
219		type = ELF_R_TYPE(rels->r_info);
220
221		if (type == R_TYPE(NONE))
222			continue;
223
224		if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
225			continue;
226
227		where = (Elf_Addr *)(rels->r_offset + loff);
228
229		if (RELOC_USE_ADDEND(type))
230			value = *where & RELOC_VALUE_BITMASK(type);
231		else
232			value = 0;
233
234		sym = NULL;
235		symn = NULL;
236		if (RELOC_RESOLVE_SYMBOL(type)) {
237			sym = object->dyn.symtab;
238			sym += ELF_R_SYM(rels->r_info);
239			symn = object->dyn.strtab + sym->st_name;
240
241			if (sym->st_shndx != SHN_UNDEF &&
242			    ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
243				value += loff;
244			} else {
245				this = NULL;
246				ooff = _dl_find_symbol(symn, _dl_objects,
247				    &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
248				    ((type == R_TYPE(JUMP_SLOT))?
249					SYM_PLT:SYM_NOTPLT),
250				    sym->st_size, object->load_name);
251				if (this == NULL) {
252resolve_failed:
253					_dl_printf("%s: %s: can't resolve "
254					    "reference '%s'\n",
255					    _dl_progname, object->load_name,
256					    symn);
257					fails++;
258					continue;
259				}
260				value += (Elf_Addr)(ooff + this->st_value);
261			}
262		}
263
264		if (type == R_TYPE(JUMP_SLOT)) {
265			_dl_reloc_plt((Elf_Word *)where, value);
266			continue;
267		}
268
269		if (type == R_TYPE(COPY)) {
270			void *dstaddr = where;
271			const void *srcaddr;
272			const Elf_Sym *dstsym = sym, *srcsym = NULL;
273			size_t size = dstsym->st_size;
274			Elf_Addr soff;
275
276			soff = _dl_find_symbol(symn, object->next, &srcsym,
277			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
278			    ((type == R_TYPE(JUMP_SLOT)) ? SYM_PLT:SYM_NOTPLT),
279			    size, object->load_name);
280			if (srcsym == NULL)
281				goto resolve_failed;
282
283			srcaddr = (void *)(soff + srcsym->st_value);
284			_dl_bcopy(srcaddr, dstaddr, size);
285			continue;
286		}
287
288		if (RELOC_PC_RELATIVE(type))
289			value -= (Elf_Addr)where;
290		if (RELOC_BASE_RELATIVE(type))
291			value += loff;
292
293		mask = RELOC_VALUE_BITMASK(type);
294		value >>= RELOC_VALUE_RIGHTSHIFT(type);
295		value &= mask;
296
297		if (RELOC_UNALIGNED(type)) {
298			/* Handle unaligned relocations. */
299			Elf_Addr tmp = 0;
300			char *ptr = (char *)where;
301			int i, size = RELOC_TARGET_SIZE(type)/8;
302
303			/* Read it in one byte at a time. */
304			for (i=0; i<size; i++)
305				tmp = (tmp << 8) | ptr[i];
306
307			tmp &= ~mask;
308			tmp |= value;
309
310			/* Write it back out. */
311			for (i=0; i<size; i++)
312				ptr[i] = ((tmp >> (8*i)) & 0xff);
313		} else if (RELOC_TARGET_SIZE(type) > 32) {
314			*where &= ~mask;
315			*where |= value;
316		} else {
317			Elf32_Addr *where32 = (Elf32_Addr *)where;
318
319			*where32 &= ~mask;
320			*where32 |= value;
321		}
322	}
323
324	/* reprotect the unprotected segments */
325	if ((rel == DT_REL || rel == DT_RELA)) {
326		for (llist = object->load_list; llist != NULL; llist = llist->next) {
327			if (!(llist->prot & PROT_WRITE))
328				_dl_mprotect(llist->start, llist->size,
329				    llist->prot);
330		}
331	}
332
333	return (fails);
334}
335
336#if 0
337struct jmpslot {
338	u_short opcode;
339	u_short addr[2];
340	u_short reloc_index;
341#define JMPSLOT_RELOC_MASK              0xffff
342};
343#define JUMP    0xe990          /* NOP + JMP opcode */
344#endif
345
346void
347_dl_reloc_plt(Elf_Addr *where, Elf_Addr value)
348{
349	*where = value;
350}
351
352/*
353 * Resolve a symbol at run-time.
354 */
355Elf_Addr
356_dl_bind(elf_object_t *object, int index)
357{
358	Elf_Rel *rel;
359	Elf_Word *addr;
360	const Elf_Sym *sym, *this;
361	const char *symn;
362	Elf_Addr ooff;
363	sigset_t omask, nmask;
364
365	rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
366
367	rel += index/sizeof(Elf_Rel);
368
369	sym = object->dyn.symtab;
370	sym += ELF_R_SYM(rel->r_info);
371	symn = object->dyn.strtab + sym->st_name;
372
373	addr = (Elf_Word *)(object->load_offs + rel->r_offset);
374	this = NULL;
375	ooff = _dl_find_symbol(symn, _dl_objects, &this,
376	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, 0, object->load_name);
377	if (this == NULL) {
378		_dl_printf("lazy binding failed!\n");
379		*((int *)0) = 0;        /* XXX */
380	}
381
382	/* if GOT is protected, allow the write */
383	if (object->got_addr != NULL && object->got_size != 0) {
384		sigfillset(&nmask);
385		_dl_sigprocmask(SIG_BLOCK, &nmask, &omask);
386		_dl_mprotect((void*)object->got_addr, object->got_size,
387		    PROT_READ|PROT_WRITE);
388	}
389
390	_dl_reloc_plt(addr, ooff + this->st_value);
391
392	/* put the GOT back to RO */
393	if (object->got_addr != NULL && object->got_size != 0) {
394		_dl_mprotect((void*)object->got_addr, object->got_size,
395		    PROT_READ);
396		_dl_sigprocmask(SIG_SETMASK, &omask, NULL);
397	}
398
399	return((Elf_Addr)ooff + this->st_value);
400}
401
402void
403_dl_md_reloc_got(elf_object_t *object, int lazy)
404{
405	extern void _dl_bind_start(void);       /* XXX */
406	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
407	int i, num;
408	Elf_Rel *rel;
409	struct load_list *llist;
410	Elf_Addr ooff;
411	const Elf_Sym *this;
412
413	if (pltgot == NULL)
414		return; /* it is possible to have no PLT/GOT relocations */
415
416	pltgot[1] = (Elf_Addr)object;
417	pltgot[2] = (Elf_Addr)&_dl_bind_start;
418
419	if (object->Dyn.info[DT_PLTREL] != DT_REL)
420		return;
421
422	this = NULL;
423	ooff = _dl_find_symbol("__got_start", object, &this,
424	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, SYM_NOTPLT,
425	    NULL);
426	if (this != NULL)
427		object->got_addr = ooff + this->st_value;
428
429	this = NULL;
430	ooff = _dl_find_symbol("__got_end", object, &this,
431	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, SYM_NOTPLT,
432	    NULL);
433	if (this != NULL)
434		object->got_size = ooff + this->st_value  - object->got_addr;
435
436	this = NULL;
437	ooff = _dl_find_symbol("__plt_start", object, &this,
438	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, SYM_NOTPLT,
439	    NULL);
440	if (this != NULL)
441		object->plt_addr = ooff + this->st_value;
442
443	this = NULL;
444	ooff = _dl_find_symbol("__plt_end", object, &this,
445	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, SYM_NOTPLT,
446	    NULL);
447	if (this != NULL)
448		object->plt_size = ooff + this->st_value  - object->plt_addr;
449
450	if (!lazy) {
451		_dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
452		return;
453	}
454
455	rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
456	num = (object->Dyn.info[DT_PLTRELSZ]);
457	for (llist = object->load_list; llist != NULL; llist = llist->next) {
458		if (!(llist->prot & PROT_WRITE))
459			_dl_mprotect(llist->start, llist->size,
460			    llist->prot|PROT_WRITE);
461	}
462	for (i = 0; i < num/sizeof(Elf_Rel); i++, rel++) {
463		Elf_Addr *where;
464		where = (Elf_Addr *)(rel->r_offset + object->load_offs);
465		*where += object->load_offs;
466	}
467	for (llist = object->load_list; llist != NULL; llist = llist->next) {
468		if (!(llist->prot & PROT_WRITE))
469			_dl_mprotect(llist->start, llist->size,
470			    llist->prot);
471	}
472
473	/* PLT is already RO on i386, no point in mprotecting it, just GOT */
474	if (object->got_addr != NULL && object->got_size != 0)
475		_dl_mprotect((void*)object->got_addr, object->got_size,
476		    PROT_READ);
477}
478