rtld_machine.c revision 1.8
1/*	$OpenBSD: rtld_machine.c,v 1.8 2013/06/01 09:57:58 miod 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
49#include <nlist.h>
50#include <link.h>
51#include <signal.h>
52
53#include "syscall.h"
54#include "archdep.h"
55#include "resolve.h"
56
57Elf_Addr _dl_bind(elf_object_t *object, int reloff);
58void	_dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr);
59
60int
61_dl_md_reloc(elf_object_t *object, int rel, int relasz)
62{
63	int	i;
64	int	numrela;
65	int	fails = 0;
66	struct load_list *llist;
67	Elf32_Addr loff;
68	Elf32_Rela  *relas;
69
70	loff = object->obj_base;
71	numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
72	relas = (Elf32_Rela *)(object->Dyn.info[rel]);
73
74#ifdef DL_PRINTF_DEBUG
75	_dl_printf("object relocation size %x, numrela %x\n",
76	    object->Dyn.info[relasz], numrela);
77#endif
78
79	if (relas == NULL)
80		return(0);
81
82	/*
83	 * Change protection of all write protected segments in the object
84	 * so we can do relocations such as DISP26. After relocation,
85	 * restore protection.
86	 */
87	if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) {
88		for (llist = object->load_list; llist != NULL;
89		    llist = llist->next) {
90			if (!(llist->prot & PROT_WRITE)) {
91				_dl_mprotect(llist->start, llist->size,
92				    llist->prot|PROT_WRITE);
93			}
94		}
95	}
96
97	for (i = 0; i < numrela; i++, relas++) {
98		Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
99		Elf32_Addr ooff, addend, newval;
100		const Elf32_Sym *sym, *this;
101		const char *symn;
102		int type;
103		Elf32_Addr prev_value = 0, prev_ooff = 0;
104		const Elf32_Sym *prev_sym = NULL;
105
106		type = ELF32_R_TYPE(relas->r_info);
107
108		if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
109			continue;
110
111		if (type == RELOC_NONE)
112			continue;
113
114		sym = object->dyn.symtab;
115		sym += ELF32_R_SYM(relas->r_info);
116		symn = object->dyn.strtab + sym->st_name;
117
118		if (type == RELOC_COPY) {
119			/*
120			 * we need to find a symbol, that is not in the current
121			 * object, start looking at the beginning of the list,
122			 * searching all objects but _not_ the current object,
123			 * first one found wins.
124			 */
125			const Elf32_Sym *cpysrc = NULL;
126			Elf32_Addr src_loff;
127			int size;
128
129			src_loff = 0;
130			src_loff = _dl_find_symbol(symn, &cpysrc,
131			    SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
132			    sym, object, NULL);
133			if (cpysrc != NULL) {
134				size = sym->st_size;
135				if (sym->st_size != cpysrc->st_size) {
136					/* _dl_find_symbol() has warned
137					   about this already */
138					size = sym->st_size < cpysrc->st_size ?
139					    sym->st_size : cpysrc->st_size;
140				}
141				_dl_bcopy((void *)(src_loff + cpysrc->st_value),
142				    r_addr, size);
143			} else
144				fails++;
145
146			continue;
147		}
148
149		if (ELF32_R_SYM(relas->r_info) &&
150		    !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
151		    ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
152		    sym != prev_sym) {
153			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
154			    ELF32_ST_TYPE(sym->st_info) == STT_SECTION) {
155				prev_sym = sym;
156				prev_value = 0;
157				prev_ooff = object->obj_base;
158			} else {
159				this = NULL;
160				ooff = _dl_find_symbol_bysym(object,
161				    ELF32_R_SYM(relas->r_info), &this,
162				    SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
163				    ((type == RELOC_GOTP_ENT) ?
164				    SYM_PLT : SYM_NOTPLT), sym, NULL);
165
166				if (this == NULL) {
167					if (ELF_ST_BIND(sym->st_info) !=
168					    STB_WEAK)
169						fails++;
170					continue;
171				}
172				prev_sym = sym;
173				prev_value = this->st_value;
174				prev_ooff = ooff;
175			}
176		}
177
178		if (type == RELOC_GOTP_ENT) {
179			_dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
180			    relas->r_addend + loff,
181			    prev_ooff + prev_value);
182			continue;
183		}
184
185		if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
186		    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
187		    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE))
188			addend = relas->r_addend;
189		else
190			addend = prev_value + relas->r_addend;
191
192		switch (type) {
193		case RELOC_16L:
194			newval = prev_ooff + addend;
195			*(unsigned short *)r_addr = newval & 0xffff;
196			_dl_cacheflush((unsigned long)r_addr, 2);
197			break;
198		case RELOC_16H:
199			newval = prev_ooff + addend;
200			*(unsigned short *)r_addr = newval >> 16;
201			_dl_cacheflush((unsigned long)r_addr, 2);
202			break;
203		case RELOC_DISP26:
204			newval = prev_ooff + addend;
205			newval -= (Elf_Addr)r_addr;
206			if ((newval >> 28) != 0 && (newval >> 28) != 0x0f) {
207				_dl_printf("%s: %s: out of range DISP26"
208				    " relocation to '%s' at %x\n",
209				    _dl_progname, object->load_name, symn,
210				    r_addr);
211				_dl_exit(1);
212			}
213			*r_addr = (*r_addr & 0xfc000000) |
214			    (((int32_t)newval >> 2) & 0x03ffffff);
215			_dl_cacheflush((unsigned long)r_addr, 4);
216			break;
217		case RELOC_32:
218			newval = prev_ooff + addend;
219			*r_addr = newval;
220			break;
221		case RELOC_BBASED_32:
222			newval = loff + addend;
223			*r_addr = newval;
224			break;
225		default:
226			_dl_printf("%s:"
227			    " %s: unsupported relocation '%s' %d at %x\n",
228			    _dl_progname, object->load_name, symn, type,
229			    r_addr);
230			_dl_exit(1);
231		}
232	}
233
234	/* reprotect the unprotected segments */
235	if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) {
236		for (llist = object->load_list; llist != NULL;
237		    llist = llist->next) {
238			if (!(llist->prot & PROT_WRITE))
239				_dl_mprotect(llist->start, llist->size,
240				    llist->prot);
241		}
242	}
243
244	return(fails);
245}
246
247/*
248 * GOTP_ENT relocations are special in that they define both a .got and a
249 * .plt relocation.
250 */
251void
252_dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
253{
254	uint16_t *plt_entry = (uint16_t *)plt_addr;
255
256	/* .got update */
257	*(Elf_Addr *)got_addr = val;
258	/* .plt update */
259	plt_entry[1] = got_addr >> 16;
260	plt_entry[3] = got_addr & 0xffff;
261}
262
263/*
264 *	Relocate the Global Offset Table (GOT).
265 *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
266 *	otherwise the lazy binding plt operation is preserved.
267 */
268int
269_dl_md_reloc_got(elf_object_t *object, int lazy)
270{
271	extern void _dl_bind_start(void);	/* XXX */
272	int	fails = 0;
273	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
274	Elf_Addr ooff;
275	Elf_Addr plt_start, plt_end;
276	const Elf_Sym *this;
277
278	if (pltgot == NULL)
279		return (0);
280
281	pltgot[1] = (Elf_Addr)object;
282	pltgot[2] = (Elf_Addr)_dl_bind_start;
283
284	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
285		return (0);
286
287	if (object->traced)
288		lazy = 1;
289
290	object->got_addr = 0;
291	object->got_size = 0;
292	this = NULL;
293	ooff = _dl_find_symbol("__got_start", &this,
294	    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
295	if (this != NULL)
296		object->got_addr = ooff + this->st_value;
297
298	this = NULL;
299	ooff = _dl_find_symbol("__got_end", &this,
300	    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
301	if (this != NULL)
302		object->got_size = ooff + this->st_value  - object->got_addr;
303
304	if (object->got_addr == 0)
305		object->got_start = 0;
306	else {
307		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
308		object->got_size += object->got_addr - object->got_start;
309		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
310	}
311
312	/*
313	 * Post-5.3 binaries use dynamic tags to provide the .plt boundaries.
314	 * If the tags are missing, fall back to the special symbol search.
315	 */
316	plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM];
317	plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM];
318	if (plt_start == 0 || plt_end == 0) {
319		this = NULL;
320		ooff = _dl_find_symbol("__plt_start", &this,
321		    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL,
322		    object, NULL);
323		if (this != NULL)
324			plt_start = ooff + this->st_value;
325		else
326			plt_start = 0;
327
328		this = NULL;
329		ooff = _dl_find_symbol("__plt_end", &this,
330		    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL,
331		    object, NULL);
332		if (this != NULL)
333			plt_end = ooff + this->st_value;
334		else
335			plt_end = 0;
336	} else {
337		plt_start += object->obj_base;
338		plt_end += object->obj_base;
339	}
340
341	if (plt_start == 0) {
342		object->plt_start = 0;
343		object->plt_size = 0;
344	} else {
345		object->plt_start = ELF_TRUNC(plt_start, _dl_pagesz);
346		object->plt_size =
347		    ELF_ROUND(plt_end, _dl_pagesz) - object->plt_start;
348
349		/*
350		 * GOT relocation will require PLT to be writeable.
351		 */
352		if (!lazy || object->obj_base != 0)
353			_dl_mprotect((void*)object->plt_start, object->plt_size,
354			    PROT_READ | PROT_WRITE);
355	}
356
357	if (!lazy) {
358		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
359	} else {
360		if (object->obj_base != 0) {
361			int cnt;
362			Elf_Addr *addr;
363			Elf_RelA *rela;
364
365			cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
366			rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
367
368			for (; cnt != 0; cnt--, rela++) {
369				addr = (Elf_Addr *)(object->obj_base +
370				    rela->r_offset);
371				_dl_md_reloc_gotp_ent((Elf_Addr)addr,
372				    object->obj_base + rela->r_addend,
373				    *addr + object->obj_base);
374			}
375		}
376	}
377
378	if (object->got_size != 0) {
379		_dl_mprotect((void*)object->got_start, object->got_size,
380		    PROT_READ);
381	}
382	if (object->plt_size != 0) {
383		if (!lazy || object->obj_base != 0) {
384			/*
385			 * Force a cache sync on the whole plt here,
386			 * otherwise I$ might have stale information.
387			 */
388			_dl_cacheflush(object->plt_start, object->plt_size);
389			_dl_mprotect((void*)object->plt_start, object->plt_size,
390			    PROT_READ | PROT_EXEC);
391		}
392	}
393
394	return (fails);
395}
396
397Elf_Addr
398_dl_bind(elf_object_t *object, int reloff)
399{
400	Elf_RelA *rel;
401	Elf_Addr *r_addr, ooff, value;
402	const Elf_Sym *sym, *this;
403	const char *symn;
404	const elf_object_t *sobj;
405	sigset_t savedmask;
406
407	rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
408
409	sym = object->dyn.symtab;
410	sym += ELF_R_SYM(rel->r_info);
411	symn = object->dyn.strtab + sym->st_name;
412
413	r_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
414	this = NULL;
415	ooff = _dl_find_symbol(symn, &this,
416	    SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, &sobj);
417	if (this == NULL) {
418		_dl_printf("lazy binding failed!\n");
419		*((int *)0) = 0;	/* XXX */
420	}
421
422	value = ooff + this->st_value;
423
424	if (sobj->traced && _dl_trace_plt(sobj, symn))
425		return value;
426
427	/* if GOT is protected, allow the write */
428	if (object->got_size != 0)  {
429		_dl_thread_bind_lock(0, &savedmask);
430		_dl_mprotect((void*)object->got_start, object->got_size,
431		    PROT_READ | PROT_WRITE);
432	}
433
434	*r_addr = value;
435
436	/* put the GOT back to RO */
437	if (object->got_size != 0) {
438		_dl_mprotect((void*)object->got_start, object->got_size,
439		    PROT_READ);
440		_dl_thread_bind_lock(1, &savedmask);
441	}
442
443	return (value);
444}
445