xref: /openbsd/libexec/ld.so/arm/rtld_machine.c (revision cca36db2)
1 /*	$OpenBSD: rtld_machine.c,v 1.16 2011/04/06 11:36:25 miod Exp $ */
2 
3 /*
4  * Copyright (c) 2004 Dale Rahn
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #define _DYN_LOADER
30 
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 
34 #include <nlist.h>
35 #include <link.h>
36 #include <signal.h>
37 
38 #include "syscall.h"
39 #include "archdep.h"
40 #include "resolve.h"
41 
42 void _dl_bind_start(void); /* XXX */
43 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
44 #define _RF_S		0x80000000		/* Resolve symbol */
45 #define _RF_A		0x40000000		/* Use addend */
46 #define _RF_P		0x20000000		/* Location relative */
47 #define _RF_G		0x10000000		/* GOT offset */
48 #define _RF_B		0x08000000		/* Load address relative */
49 #define _RF_U		0x04000000		/* Unaligned */
50 #define _RF_E		0x02000000		/* ERROR */
51 #define _RF_SZ(s)	(((s) & 0xff) << 8)	/* memory target size */
52 #define _RF_RS(s)	((s) & 0xff)		/* right shift */
53 static int reloc_target_flags[] = {
54 	0,						/*  0 NONE */
55 	_RF_S|_RF_P|_RF_A|	_RF_SZ(32) | _RF_RS(0),	/*  1 PC24 */
56 	_RF_S|_RF_A|		_RF_SZ(32) | _RF_RS(0),	/*  2 ABS32 */
57 	_RF_S|_RF_P|_RF_A|	_RF_SZ(32) | _RF_RS(0),	/*  3 REL32 */
58 	_RF_S|_RF_P|_RF_A|	_RF_E,			/*  4 REL13 */
59 	_RF_S|_RF_A|		_RF_E,			/*  5 ABS16 */
60 	_RF_S|_RF_A|		_RF_E,			/*  6 ABS12 */
61 	_RF_S|_RF_A|		_RF_E,			/*  7 T_ABS5 */
62 	_RF_S|_RF_A|		_RF_E,			/*  8 ABS8 */
63 	_RF_S|_RF_B|_RF_A|	_RF_E,			/*  9 SBREL32 */
64 	_RF_S|_RF_P|_RF_A|	_RF_E,			/* 10 T_PC22 */
65 	_RF_S|_RF_P|_RF_A|	_RF_E,			/* 11 T_PC8 */
66 	_RF_E,						/* 12 Reserved */
67 	_RF_S|_RF_A|		_RF_E,			/* 13 SWI24 */
68 	_RF_S|_RF_A|		_RF_E,			/* 14 T_SWI8 */
69 	_RF_E,						/* 15 OBSL */
70 	_RF_E,						/* 16 OBSL */
71 	_RF_E,						/* 17 UNUSED */
72 	_RF_E,						/* 18 UNUSED */
73 	_RF_E,						/* 19 UNUSED */
74 	_RF_S|			_RF_SZ(32) | _RF_RS(0),	/* 20 COPY */
75 	_RF_S|_RF_A|		_RF_SZ(32) | _RF_RS(0),	/* 21 GLOB_DAT */
76 	_RF_S|			_RF_SZ(32) | _RF_RS(0),	/* 22 JUMP_SLOT */
77 	      _RF_A|	_RF_B|	_RF_SZ(32) | _RF_RS(0),	/* 23 RELATIVE */
78 	_RF_E,						/* 24 GOTOFF */
79 	_RF_E,						/* 25 GOTPC */
80 	_RF_E,						/* 26 GOT32 */
81 	_RF_E,						/* 27 PLT32 */
82 	_RF_E,						/* 28 UNUSED */
83 	_RF_E,						/* 29 UNUSED */
84 	_RF_E,						/* 30 UNUSED */
85 	_RF_E,						/* 31 UNUSED */
86 	_RF_E,						/* 32 A_PCR 0 */
87 	_RF_E,						/* 33 A_PCR 8 */
88 	_RF_E,						/* 34 A_PCR 16 */
89 	_RF_E,						/* 35 B_PCR 0 */
90 	_RF_E,						/* 36 B_PCR 12 */
91 	_RF_E,						/* 37 B_PCR 20 */
92 	_RF_E,						/* 38 RELAB32 */
93 	_RF_E,						/* 39 ROSGREL32 */
94 	_RF_E,						/* 40 V4BX */
95 	_RF_E,						/* 41 STKCHK */
96 	_RF_E						/* 42 TSTKCHK */
97 };
98 
99 #define RELOC_RESOLVE_SYMBOL(t)		((reloc_target_flags[t] & _RF_S) != 0)
100 #define RELOC_PC_RELATIVE(t)		((reloc_target_flags[t] & _RF_P) != 0)
101 #define RELOC_BASE_RELATIVE(t)		((reloc_target_flags[t] & _RF_B) != 0)
102 #define RELOC_UNALIGNED(t)		((reloc_target_flags[t] & _RF_U) != 0)
103 #define RELOC_USE_ADDEND(t)		((reloc_target_flags[t] & _RF_A) != 0)
104 #define RELOC_TARGET_SIZE(t)		((reloc_target_flags[t] >> 8) & 0xff)
105 #define RELOC_VALUE_RIGHTSHIFT(t)	(reloc_target_flags[t] & 0xff)
106 static int reloc_target_bitmask[] = {
107 #define _BM(x)  (x == 32? ~0 : ~(-(1UL << (x))))
108 	_BM(0),		/*  0 NONE */
109 	_BM(24),	/*  1 PC24 */
110 	_BM(32),	/*  2 ABS32 */
111 	_BM(32),	/*  3 REL32 */
112 	_BM(0),		/*  4 REL13 */
113 	_BM(0),		/*  5 ABS16 */
114 	_BM(0),		/*  6 ABS12 */
115 	_BM(0),		/*  7 T_ABS5 */
116 	_BM(0),		/*  8 ABS8 */
117 	_BM(32),	/*  9 SBREL32 */
118 	_BM(0),		/* 10 T_PC22 */
119 	_BM(0),		/* 11 T_PC8 */
120 	_BM(0),		/* 12 Reserved */
121 	_BM(0),		/* 13 SWI24 */
122 	_BM(0),		/* 14 T_SWI8 */
123 	_BM(0),		/* 15 OBSL */
124 	_BM(0),		/* 16 OBSL */
125 	_BM(0),		/* 17 UNUSED */
126 	_BM(0),		/* 18 UNUSED */
127 	_BM(0),		/* 19 UNUSED */
128 	_BM(32),	/* 20 COPY */
129 	_BM(32),	/* 21 GLOB_DAT */
130 	_BM(32),	/* 22 JUMP_SLOT */
131 	_BM(32),	/* 23 RELATIVE */
132 	_BM(0),		/* 24 GOTOFF */
133 	_BM(0),		/* 25 GOTPC */
134 	_BM(0),		/* 26 GOT32 */
135 	_BM(0),		/* 27 PLT32 */
136 	_BM(0),		/* 28 UNUSED */
137 	_BM(0),		/* 29 UNUSED */
138 	_BM(0),		/* 30 UNUSED */
139 	_BM(0),		/* 31 UNUSED */
140 	_BM(0),		/* 32 A_PCR 0 */
141 	_BM(0),		/* 33 A_PCR 8 */
142 	_BM(0),		/* 34 A_PCR 16 */
143 	_BM(0),		/* 35 B_PCR 0 */
144 	_BM(0),		/* 36 B_PCR 12 */
145 	_BM(0),		/* 37 B_PCR 20 */
146 	_BM(0),		/* 38 RELAB32 */
147 	_BM(0),		/* 39 ROSGREL32 */
148 	_BM(0),		/* 40 V4BX */
149 	_BM(0),		/* 41 STKCHK */
150 	_BM(0)		/* 42 TSTKCHK */
151 #undef _BM
152 };
153 #define RELOC_VALUE_BITMASK(t)	(reloc_target_bitmask[t])
154 
155 #define R_TYPE(x) R_ARM_ ## x
156 
157 void _dl_reloc_plt(Elf_Word *where, Elf_Addr value, Elf_Rel *rel);
158 
159 int
160 _dl_md_reloc(elf_object_t *object, int rel, int relsz)
161 {
162 	long	i;
163 	long	numrel;
164 	int	fails = 0;
165 	Elf_Addr loff;
166 	Elf_Rel *rels;
167 	struct load_list *llist;
168 
169 	loff = object->obj_base;
170 	numrel = object->Dyn.info[relsz] / sizeof(Elf_Rel);
171 	rels = (Elf_Rel *)(object->Dyn.info[rel]);
172 
173 	if (rels == NULL)
174 		return(0);
175 
176 	/*
177 	 * unprotect some segments if we need it.
178 	 */
179 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
180 		for (llist = object->load_list;
181 		    llist != NULL;
182 		    llist = llist->next) {
183 			if (!(llist->prot & PROT_WRITE))
184 				_dl_mprotect(llist->start, llist->size,
185 				    llist->prot|PROT_WRITE);
186 		}
187 	}
188 
189 	for (i = 0; i < numrel; i++, rels++) {
190 		Elf_Addr *where, value, ooff, mask;
191 		Elf_Word type;
192 		const Elf_Sym *sym, *this;
193 		const char *symn;
194 
195 		type = ELF_R_TYPE(rels->r_info);
196 
197 		if (reloc_target_flags[type] & _RF_E) {
198 			_dl_printf(" bad relocation %d %d\n", i, type);
199 			_dl_exit(1);
200 		}
201 		if (type == R_TYPE(NONE))
202 			continue;
203 
204 		if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
205 			continue;
206 
207 		where = (Elf_Addr *)(rels->r_offset + loff);
208 
209 		if (RELOC_USE_ADDEND(type))
210 #ifdef LDSO_ARCH_IS_RELA_
211 			value = rels->r_addend;
212 #else
213 			value = *where & RELOC_VALUE_BITMASK(type);
214 #endif
215 		else
216 			value = 0;
217 
218 		sym = NULL;
219 		symn = NULL;
220 		if (RELOC_RESOLVE_SYMBOL(type)) {
221 			sym = object->dyn.symtab;
222 			sym += ELF_R_SYM(rels->r_info);
223 			symn = object->dyn.strtab + sym->st_name;
224 
225 			if (sym->st_shndx != SHN_UNDEF &&
226 			    ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
227 				value += loff;
228 			} else {
229 				this = NULL;
230 				ooff = _dl_find_symbol_bysym(object,
231 				    ELF_R_SYM(rels->r_info), &this,
232 				    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
233 				    ((type == R_TYPE(JUMP_SLOT)) ?
234 					SYM_PLT : SYM_NOTPLT),
235 				    sym, NULL);
236 				if (this == NULL) {
237 resolve_failed:
238 					if (ELF_ST_BIND(sym->st_info) !=
239 					    STB_WEAK)
240 						fails++;
241 					continue;
242 				}
243 				value += (Elf_Addr)(ooff + this->st_value);
244 			}
245 		}
246 
247 		if (type == R_TYPE(JUMP_SLOT)) {
248 			/*
249 			_dl_reloc_plt((Elf_Word *)where, value, rels);
250 			*/
251 			*where = value;
252 			continue;
253 		}
254 
255 		if (type == R_TYPE(COPY)) {
256 			void *dstaddr = where;
257 			const void *srcaddr;
258 			const Elf_Sym *dstsym = sym, *srcsym = NULL;
259 			Elf_Addr soff;
260 
261 			soff = _dl_find_symbol(symn, &srcsym,
262 			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
263 			    dstsym, object, NULL);
264 			if (srcsym == NULL)
265 				goto resolve_failed;
266 
267 			srcaddr = (void *)(soff + srcsym->st_value);
268 			_dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
269 			continue;
270 		}
271 
272 		if (RELOC_PC_RELATIVE(type))
273 			value -= (Elf_Addr)where;
274 		if (RELOC_BASE_RELATIVE(type))
275 			value += loff;
276 
277 		mask = RELOC_VALUE_BITMASK(type);
278 		value >>= RELOC_VALUE_RIGHTSHIFT(type);
279 		value &= mask;
280 
281 		if (RELOC_UNALIGNED(type)) {
282 			/* Handle unaligned relocations. */
283 			Elf_Addr tmp = 0;
284 			char *ptr = (char *)where;
285 			int i, size = RELOC_TARGET_SIZE(type)/8;
286 
287 			/* Read it in one byte at a time. */
288 			for (i=0; i<size; i++)
289 				tmp = (tmp << 8) | ptr[i];
290 
291 			tmp &= ~mask;
292 			tmp |= value;
293 
294 			/* Write it back out. */
295 			for (i=0; i<size; i++)
296 				ptr[i] = ((tmp >> (8*i)) & 0xff);
297 		} else {
298 			*where &= ~mask;
299 			*where |= value;
300 		}
301 	}
302 
303 	/* reprotect the unprotected segments */
304 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
305 		for (llist = object->load_list;
306 		    llist != NULL;
307 		    llist = llist->next) {
308 			if (!(llist->prot & PROT_WRITE))
309 				_dl_mprotect(llist->start, llist->size,
310 				    llist->prot);
311 		}
312 	}
313 
314 	return (fails);
315 }
316 
317 /*
318  *	Relocate the Global Offset Table (GOT).
319  *	This is done by calling _dl_md_reloc on DT_JUMPREL for DL_BIND_NOW,
320  *	otherwise the lazy binding plt initialization is performed.
321  */
322 int
323 _dl_md_reloc_got(elf_object_t *object, int lazy)
324 {
325 	int	fails = 0;
326 	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
327 	Elf_Addr ooff;
328 	const Elf_Sym *this;
329 	int i, num;
330 	Elf_Rel *rel;
331 
332 	if (object->Dyn.info[DT_PLTREL] != DT_REL)
333 		return (0);
334 
335 	object->got_addr = 0;
336 	object->got_size = 0;
337 	this = NULL;
338 	ooff = _dl_find_symbol("__got_start", &this,
339 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
340 	if (this != NULL)
341 		object->got_addr = ooff + this->st_value;
342 
343 	this = NULL;
344 	ooff = _dl_find_symbol("__got_end", &this,
345 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
346 	if (this != NULL)
347 		object->got_size = ooff + this->st_value  - object->got_addr;
348 
349 	object->plt_size = 0;	/* Text PLT on ARM */
350 
351 	if (object->got_addr == 0)
352 		object->got_start = 0;
353 	else {
354 		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
355 		object->got_size += object->got_addr - object->got_start;
356 		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
357 	}
358 	object->plt_start = 0;
359 
360 	if (!lazy) {
361 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
362 	} else {
363 		rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
364 		num = (object->Dyn.info[DT_PLTRELSZ]);
365 
366 		for (i = 0; i < num/sizeof(Elf_Rel); i++, rel++) {
367 			Elf_Addr *where;
368 			where = (Elf_Addr *)(rel->r_offset + object->obj_base);
369 			*where += object->obj_base;
370 		}
371 
372 		pltgot[1] = (Elf_Addr)object;
373 		pltgot[2] = (Elf_Addr)_dl_bind_start;
374 	}
375 	if (object->got_size != 0)
376 		_dl_mprotect((void*)object->got_start, object->got_size,
377 		    PROT_READ);
378 	if (object->plt_size != 0)
379 		_dl_mprotect((void*)object->plt_start, object->plt_size,
380 		    PROT_READ|PROT_EXEC);
381 
382 	return (fails);
383 }
384 
385 Elf_Addr
386 _dl_bind(elf_object_t *object, int relidx)
387 {
388 	Elf_Rel *rel;
389 	Elf_Word *addr;
390 	const Elf_Sym *sym, *this;
391 	const char *symn;
392 	Elf_Addr ooff, newval;
393 	sigset_t savedmask;
394 
395 	rel = ((Elf_Rel *)object->Dyn.info[DT_JMPREL]) + (relidx);
396 
397 	sym = object->dyn.symtab;
398 	sym += ELF_R_SYM(rel->r_info);
399 	symn = object->dyn.strtab + sym->st_name;
400 
401 	this = NULL;
402 	ooff = _dl_find_symbol(symn,  &this,
403 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
404 	    object, NULL);
405 	if (this == NULL) {
406 		_dl_printf("lazy binding failed!\n");
407 		*((int *)0) = 0;	/* XXX */
408 	}
409 
410 	addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
411 	newval = ooff + this->st_value;
412 
413 	/* if GOT is protected, allow the write */
414 	if (object->got_size != 0) {
415 		_dl_thread_bind_lock(0, &savedmask);
416 		_dl_mprotect((void*)object->got_start, object->got_size,
417 		    PROT_READ|PROT_WRITE);
418 	}
419 
420 	if (*addr != newval)
421 		*addr = newval;
422 
423 	/* put the GOT back to RO */
424 	if (object->got_size != 0) {
425 		_dl_mprotect((void*)object->got_start, object->got_size,
426 		    PROT_READ);
427 		_dl_thread_bind_lock(1, &savedmask);
428 	}
429 	return newval;
430 }
431