rtld_machine.c revision 1.5
1/*	$OpenBSD: rtld_machine.c,v 1.5 2002/11/23 06:04:13 drahn 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
81#include "syscall.h"
82#include "archdep.h"
83#include "resolve.h"
84
85void
86_dl_bcopy(const void *src, void *dest, int size)
87{
88	const unsigned char *psrc = src;
89	unsigned char *pdest = dest;
90	int i;
91
92	for (i = 0; i < size; i++)
93		pdest[i] = psrc[i];
94}
95
96/*
97 * The following table holds for each relocation type:
98 *	- the width in bits of the memory location the relocation
99 *	  applies to (not currently used)
100 *	- the number of bits the relocation value must be shifted to the
101 *	  right (i.e. discard least significant bits) to fit into
102 *	  the appropriate field in the instruction word.
103 *	- flags indicating whether
104 *		* the relocation involves a symbol
105 *		* the relocation is relative to the current position
106 *		* the relocation is for a GOT entry
107 *		* the relocation is relative to the load address
108 *
109 */
110#define _RF_S		0x80000000		/* Resolve symbol */
111#define _RF_A		0x40000000		/* Use addend */
112#define _RF_P		0x20000000		/* Location relative */
113#define _RF_G		0x10000000		/* GOT offset */
114#define _RF_B		0x08000000		/* Load address relative */
115#define _RF_U		0x04000000		/* Unaligned */
116#define _RF_SZ(s)	(((s) & 0xff) << 8)	/* memory target size */
117#define _RF_RS(s)	((s) & 0xff)		/* right shift */
118static int reloc_target_flags[] = {
119	0,							/* NONE */
120	_RF_S|_RF_A|		_RF_SZ(32) | _RF_RS(0),		/* RELOC_32*/
121	_RF_S|_RF_A|_RF_P|	_RF_SZ(32) | _RF_RS(0),		/* PC32 */
122	_RF_G|			_RF_SZ(32) | _RF_RS(00),	/* GOT32 */
123	      _RF_A|		_RF_SZ(32) | _RF_RS(0),		/* PLT32 */
124	_RF_S|			_RF_SZ(32) | _RF_RS(0),		/* COPY */
125	_RF_S|_RF_A|		_RF_SZ(32) | _RF_RS(0),		/* GLOB_DAT */
126	_RF_S|			_RF_SZ(32) | _RF_RS(0),		/* JUMP_SLOT */
127	      _RF_A|	_RF_B|	_RF_SZ(32) | _RF_RS(0),		/* RELATIVE */
128	0,							/* GOTOFF XXX */
129	0,							/* GOTPC XXX */
130	0,							/* DUMMY 11 */
131	0,							/* DUMMY 12 */
132	0,							/* DUMMY 13 */
133	0,							/* DUMMY 14 */
134	0,							/* DUMMY 15 */
135	0,							/* DUMMY 16 */
136	0,							/* DUMMY 17 */
137	0,							/* DUMMY 18 */
138	0,							/* DUMMY 19 */
139	_RF_S|_RF_A|		_RF_SZ(16) | _RF_RS(0),		/* RELOC_16 */
140	_RF_S|_RF_A|_RF_P|	_RF_SZ(16) | _RF_RS(0),		/* PC_16 */
141	_RF_S|_RF_A|		_RF_SZ(8) | _RF_RS(0),		/* RELOC_8 */
142	_RF_S|_RF_A|_RF_P|	_RF_SZ(8) | _RF_RS(0),		/* RELOC_PC8 */
143};
144
145#define RELOC_RESOLVE_SYMBOL(t)		((reloc_target_flags[t] & _RF_S) != 0)
146#define RELOC_PC_RELATIVE(t)		((reloc_target_flags[t] & _RF_P) != 0)
147#define RELOC_BASE_RELATIVE(t)		((reloc_target_flags[t] & _RF_B) != 0)
148#define RELOC_UNALIGNED(t)		((reloc_target_flags[t] & _RF_U) != 0)
149#define RELOC_USE_ADDEND(t)		((reloc_target_flags[t] & _RF_A) != 0)
150#define RELOC_TARGET_SIZE(t)		((reloc_target_flags[t] >> 8) & 0xff)
151#define RELOC_VALUE_RIGHTSHIFT(t)	(reloc_target_flags[t] & 0xff)
152
153static long reloc_target_bitmask[] = {
154#define _BM(x)	(~(-(1ULL << (x))))
155	0,		/* NONE */
156	_BM(32),	/* RELOC_32*/
157	_BM(32),	/* PC32 */
158	_BM(32),	/* GOT32 */
159	_BM(32),	/* PLT32 */
160	0,		/* COPY */
161	_BM(32),	/* GLOB_DAT */
162	_BM(32),	/* JUMP_SLOT */
163	_BM(32),	/* RELATIVE */
164	0,		/* GOTOFF XXX */
165	0,		/* GOTPC XXX */
166	0,		/* DUMMY 11 */
167	0,		/* DUMMY 12 */
168	0,		/* DUMMY 13 */
169	0,		/* DUMMY 14 */
170	0,		/* DUMMY 15 */
171	0,		/* DUMMY 16 */
172	0,		/* DUMMY 17 */
173	0,		/* DUMMY 18 */
174	0,		/* DUMMY 19 */
175	_BM(16),	/* RELOC_16 */
176	_BM(8),		/* PC_16 */
177	_BM(8),		/* RELOC_8 */
178	_BM(8),		/* RELOC_PC8 */
179#undef _BM
180};
181#define RELOC_VALUE_BITMASK(t)	(reloc_target_bitmask[t])
182
183void _dl_reloc_plt(Elf_Addr *where, Elf_Addr value);
184
185int
186_dl_md_reloc(elf_object_t *object, int rel, int relsz)
187{
188	long	i;
189	long	numrel;
190	long	fails = 0;
191	Elf_Addr loff;
192	Elf_Rel *rels;
193	struct load_list *llist;
194
195	loff = object->load_offs;
196	numrel = object->Dyn.info[relsz] / sizeof(Elf32_Rel);
197	rels = (Elf32_Rel *)(object->Dyn.info[rel]);
198	if (rels == NULL)
199		return(0);
200
201	/*
202	 * unprotect some segments if we need it.
203	 */
204	if ((rel == DT_REL || rel == DT_RELA)) {
205		for (llist = object->load_list; llist != NULL; llist = llist->next) {
206			if (!(llist->prot & PROT_WRITE))
207				_dl_mprotect(llist->start, llist->size,
208				    llist->prot|PROT_WRITE);
209		}
210	}
211
212	for (i = 0; i < numrel; i++, rels++) {
213		Elf_Addr *where, value, ooff, mask;
214		Elf_Word type;
215		const Elf_Sym *sym, *this;
216		const char *symn;
217
218		type = ELF_R_TYPE(rels->r_info);
219
220		if (type == R_TYPE(NONE))
221			continue;
222
223		if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
224			continue;
225
226		where = (Elf_Addr *)(rels->r_offset + loff);
227
228		if (RELOC_USE_ADDEND(type))
229			value = *where & RELOC_VALUE_BITMASK(type);
230		else
231			value = 0;
232
233		sym = NULL;
234		symn = NULL;
235		if (RELOC_RESOLVE_SYMBOL(type)) {
236			sym = object->dyn.symtab;
237			sym += ELF_R_SYM(rels->r_info);
238			symn = object->dyn.strtab + sym->st_name;
239
240			if (sym->st_shndx != SHN_UNDEF &&
241			    ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
242				value += loff;
243			} else {
244				this = NULL;
245				ooff = _dl_find_symbol(symn, _dl_objects,
246				    &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
247				    ((type == R_TYPE(JUMP_SLOT))?
248					SYM_PLT:SYM_NOTPLT),
249				    sym->st_size, object->load_name);
250				if (this == NULL) {
251resolve_failed:
252					_dl_printf("%s: %s: can't resolve "
253					    "reference '%s'\n",
254					    _dl_progname, object->load_name,
255					    symn);
256					fails++;
257					continue;
258				}
259				value += (Elf_Addr)(ooff + this->st_value);
260			}
261		}
262
263		if (type == R_TYPE(JUMP_SLOT)) {
264			_dl_reloc_plt((Elf_Word *)where, value);
265			continue;
266		}
267
268		if (type == R_TYPE(COPY)) {
269			void *dstaddr = where;
270			const void *srcaddr;
271			const Elf_Sym *dstsym = sym, *srcsym = NULL;
272			size_t size = dstsym->st_size;
273			Elf_Addr soff;
274
275			soff = _dl_find_symbol(symn, object->next, &srcsym,
276			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
277			    ((type == R_TYPE(JUMP_SLOT)) ? SYM_PLT:SYM_NOTPLT),
278			    size, object->load_name);
279			if (srcsym == NULL)
280				goto resolve_failed;
281
282			srcaddr = (void *)(soff + srcsym->st_value);
283			_dl_bcopy(srcaddr, dstaddr, size);
284			continue;
285		}
286
287		if (RELOC_PC_RELATIVE(type))
288			value -= (Elf_Addr)where;
289		if (RELOC_BASE_RELATIVE(type))
290			value += loff;
291
292		mask = RELOC_VALUE_BITMASK(type);
293		value >>= RELOC_VALUE_RIGHTSHIFT(type);
294		value &= mask;
295
296		if (RELOC_UNALIGNED(type)) {
297			/* Handle unaligned relocations. */
298			Elf_Addr tmp = 0;
299			char *ptr = (char *)where;
300			int i, size = RELOC_TARGET_SIZE(type)/8;
301
302			/* Read it in one byte at a time. */
303			for (i=0; i<size; i++)
304				tmp = (tmp << 8) | ptr[i];
305
306			tmp &= ~mask;
307			tmp |= value;
308
309			/* Write it back out. */
310			for (i=0; i<size; i++)
311				ptr[i] = ((tmp >> (8*i)) & 0xff);
312		} else if (RELOC_TARGET_SIZE(type) > 32) {
313			*where &= ~mask;
314			*where |= value;
315		} else {
316			Elf32_Addr *where32 = (Elf32_Addr *)where;
317
318			*where32 &= ~mask;
319			*where32 |= value;
320		}
321	}
322
323	/* reprotect the unprotected segments */
324	if ((rel == DT_REL || rel == DT_RELA)) {
325		for (llist = object->load_list; llist != NULL; llist = llist->next) {
326			if (!(llist->prot & PROT_WRITE))
327				_dl_mprotect(llist->start, llist->size,
328				    llist->prot);
329		}
330	}
331
332	return (fails);
333}
334
335struct jmpslot {
336	u_short opcode;
337	u_short addr[2];
338	u_short reloc_index;
339#define JMPSLOT_RELOC_MASK              0xffff
340};
341#define JUMP    0xe990          /* NOP + JMP opcode */
342
343void
344_dl_reloc_plt(Elf_Addr *where, Elf_Addr value)
345{
346	*where = value;
347}
348
349/*
350 * Resolve a symbol at run-time.
351 */
352Elf_Addr
353_dl_bind(elf_object_t *object, int index)
354{
355	Elf_Rel *rel;
356	Elf_Word *addr;
357	const Elf_Sym *sym, *this;
358	const char *symn;
359	Elf_Addr ooff;
360
361	rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
362
363	rel += index/sizeof(Elf_Rel);
364
365	sym = object->dyn.symtab;
366	sym += ELF_R_SYM(rel->r_info);
367	symn = object->dyn.strtab + sym->st_name;
368
369	addr = (Elf_Word *)(object->load_offs + rel->r_offset);
370	this = NULL;
371	ooff = _dl_find_symbol(symn, _dl_objects, &this,
372	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, 0, object->load_name);
373	if (this == NULL) {
374		_dl_printf("lazy binding failed!\n");
375		*((int *)0) = 0;        /* XXX */
376	}
377
378	_dl_reloc_plt(addr, ooff + this->st_value);
379
380	return((Elf_Addr)ooff + this->st_value);
381}
382
383void
384_dl_md_reloc_got(elf_object_t *object, int lazy)
385{
386	extern void _dl_bind_start(void);       /* XXX */
387	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
388	int i, num;
389	Elf_Rel *rel;
390	struct load_list *llist;
391
392	if (pltgot == NULL)
393		return; /* it is possible to have no PLT/GOT relocations */
394
395	pltgot[1] = (Elf_Addr)object;
396	pltgot[2] = (Elf_Addr)&_dl_bind_start;
397
398	if (object->Dyn.info[DT_PLTREL] != DT_REL)
399		return;
400
401	if (!lazy) {
402		_dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
403		return;
404	}
405
406	rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
407	num = (object->Dyn.info[DT_PLTRELSZ]);
408	for (llist = object->load_list; llist != NULL; llist = llist->next) {
409		if (!(llist->prot & PROT_WRITE))
410			_dl_mprotect(llist->start, llist->size,
411			    llist->prot|PROT_WRITE);
412	}
413	for (i = 0; i < num/sizeof(Elf_Rel); i++, rel++) {
414		Elf_Addr *where;
415		where = (Elf_Addr *)(rel->r_offset + object->load_offs);
416		*where += object->load_offs;
417	}
418	for (llist = object->load_list; llist != NULL; llist = llist->next) {
419		if (!(llist->prot & PROT_WRITE))
420			_dl_mprotect(llist->start, llist->size,
421			    llist->prot);
422	}
423}
424