rtld_machine.c revision 1.3
1/*	$OpenBSD: rtld_machine.c,v 1.3 2013/01/23 19:01:44 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 PC32. 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
104		type = ELF32_R_TYPE(relas->r_info);
105
106		if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
107			continue;
108
109		if (type == RELOC_NONE)
110			continue;
111
112		sym = object->dyn.symtab;
113		sym += ELF32_R_SYM(relas->r_info);
114		symn = object->dyn.strtab + sym->st_name;
115
116		if (type == RELOC_COPY) {
117			/*
118			 * we need to find a symbol, that is not in the current
119			 * object, start looking at the beginning of the list,
120			 * searching all objects but _not_ the current object,
121			 * first one found wins.
122			 */
123			const Elf32_Sym *cpysrc = NULL;
124			Elf32_Addr src_loff;
125			int size;
126
127			src_loff = 0;
128			src_loff = _dl_find_symbol(symn, &cpysrc,
129			    SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
130			    sym, object, NULL);
131			if (cpysrc != NULL) {
132				size = sym->st_size;
133				if (sym->st_size != cpysrc->st_size) {
134					/* _dl_find_symbol() has warned
135					   about this already */
136					size = sym->st_size < cpysrc->st_size ?
137					    sym->st_size : cpysrc->st_size;
138				}
139				_dl_bcopy((void *)(src_loff + cpysrc->st_value),
140				    r_addr, size);
141			} else
142				fails++;
143
144			continue;
145		}
146
147		ooff = 0;
148		this = NULL;
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			ooff = _dl_find_symbol_bysym(object,
153			    ELF32_R_SYM(relas->r_info), &this,
154			    SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
155			    ((type == RELOC_GOTP_ENT) ? SYM_PLT : SYM_NOTPLT),
156			    sym, NULL);
157
158			if (this == NULL) {
159				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
160					fails++;
161				continue;
162			}
163		}
164
165		if (type == RELOC_GOTP_ENT) {
166			_dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
167			    relas->r_addend + loff,
168			    ooff + this->st_value);
169			continue;
170		}
171
172		if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
173		    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
174		    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE))
175			addend = relas->r_addend;
176		else
177			addend = this->st_value + relas->r_addend;
178
179		switch (type) {
180		case RELOC_32:
181			newval = ooff + addend;
182			*r_addr = newval;
183			break;
184		case RELOC_BBASED_32:
185			newval = loff + addend;
186			*r_addr = newval;
187			break;
188		default:
189			_dl_printf("%s:"
190			    " %s: unsupported relocation '%s' %d at %x\n",
191			    _dl_progname, object->load_name, symn, type,
192			    r_addr);
193			_dl_exit(1);
194		}
195	}
196
197	/* reprotect the unprotected segments */
198	if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) {
199		for (llist = object->load_list; llist != NULL;
200		    llist = llist->next) {
201			if (!(llist->prot & PROT_WRITE))
202				_dl_mprotect(llist->start, llist->size,
203				    llist->prot);
204		}
205	}
206
207	return(fails);
208}
209
210/*
211 * GOTP_ENT relocations are special in that they define both a .got and a
212 * .plt relocation.
213 */
214void
215_dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
216{
217	uint16_t *plt_entry = (uint16_t *)plt_addr;
218
219	/* .got update */
220	*(Elf_Addr *)got_addr = val;
221	/* .plt update */
222	plt_entry[1] = got_addr >> 16;
223	plt_entry[3] = got_addr & 0xffff;
224}
225
226/*
227 *	Relocate the Global Offset Table (GOT).
228 *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
229 *	otherwise the lazy binding plt operation is preserved.
230 */
231int
232_dl_md_reloc_got(elf_object_t *object, int lazy)
233{
234	extern void _dl_bind_start(void);	/* XXX */
235	int	fails = 0;
236	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
237	Elf_Addr ooff;
238	Elf_Addr plt_addr;
239	const Elf_Sym *this;
240
241	if (pltgot == NULL)
242		return (0);
243
244	pltgot[1] = (Elf_Addr)object;
245	pltgot[2] = (Elf_Addr)_dl_bind_start;
246
247	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
248		return (0);
249
250	object->got_addr = 0;
251	object->got_size = 0;
252	this = NULL;
253	ooff = _dl_find_symbol("__got_start", &this,
254	    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
255	if (this != NULL)
256		object->got_addr = ooff + this->st_value;
257
258	this = NULL;
259	ooff = _dl_find_symbol("__got_end", &this,
260	    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
261	if (this != NULL)
262		object->got_size = ooff + this->st_value  - object->got_addr;
263
264	if (object->got_addr == 0)
265		object->got_start = 0;
266	else {
267		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
268		object->got_size += object->got_addr - object->got_start;
269		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
270	}
271
272	plt_addr = 0;
273	object->plt_size = 0;
274	this = NULL;
275	ooff = _dl_find_symbol("__plt_start", &this,
276	    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
277	if (this != NULL)
278		plt_addr = ooff + this->st_value;
279
280	this = NULL;
281	ooff = _dl_find_symbol("__plt_end", &this,
282	    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
283	if (this != NULL)
284		object->plt_size = ooff + this->st_value  - plt_addr;
285
286	if (plt_addr == 0)
287		object->plt_start = 0;
288	else {
289		object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
290		object->plt_size += plt_addr - object->plt_start;
291		object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
292
293		/*
294		 * GOT relocation will require PLT to be writeable.
295		 */
296		if (!lazy || object->obj_base != 0)
297			_dl_mprotect((void*)object->plt_start, object->plt_size,
298			    PROT_READ | PROT_WRITE);
299	}
300
301	if (!lazy) {
302		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
303	} else {
304		if (object->obj_base != 0) {
305			int cnt;
306			Elf_Addr *addr;
307			Elf_RelA *rela;
308
309			cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
310			rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
311
312			for (; cnt != 0; cnt--, rela++) {
313				addr = (Elf_Addr *)(object->obj_base +
314				    rela->r_offset);
315				_dl_md_reloc_gotp_ent((Elf_Addr)addr,
316				    object->obj_base + rela->r_addend,
317				    *addr + object->obj_base);
318			}
319			/*
320			 * Force a cache sync on the whole plt here,
321			 * otherwise I$ might have stale information.
322			 */
323			_dl_cacheflush(object->plt_start, object->plt_size);
324		}
325	}
326
327	if (object->got_size != 0) {
328		_dl_mprotect((void*)object->got_start, object->got_size,
329		    PROT_READ);
330	}
331	if (object->plt_size != 0) {
332		if (!lazy || object->obj_base != 0)
333			_dl_mprotect((void*)object->plt_start, object->plt_size,
334			    PROT_READ | PROT_EXEC);
335	}
336
337	return (fails);
338}
339
340Elf_Addr
341_dl_bind(elf_object_t *object, int reloff)
342{
343	Elf_RelA *rel;
344	Elf_Addr *r_addr, ooff, value;
345	const Elf_Sym *sym, *this;
346	const char *symn;
347	sigset_t savedmask;
348
349	rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
350
351	sym = object->dyn.symtab;
352	sym += ELF_R_SYM(rel->r_info);
353	symn = object->dyn.strtab + sym->st_name;
354
355	r_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
356	this = NULL;
357	ooff = _dl_find_symbol(symn, &this,
358	    SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, NULL);
359	if (this == NULL) {
360		_dl_printf("lazy binding failed!\n");
361		*((int *)0) = 0;	/* XXX */
362	}
363
364	value = ooff + this->st_value;
365
366	/* if GOT is protected, allow the write */
367	if (object->got_size != 0)  {
368		_dl_thread_bind_lock(0, &savedmask);
369		_dl_mprotect((void*)object->got_start, object->got_size,
370		    PROT_READ | PROT_WRITE);
371	}
372
373	*r_addr = value;
374
375	/* put the GOT back to RO */
376	if (object->got_size != 0) {
377		_dl_mprotect((void*)object->got_start, object->got_size,
378		    PROT_READ);
379		_dl_thread_bind_lock(1, &savedmask);
380	}
381
382	return (value);
383}
384