xref: /openbsd/libexec/ld.so/alpha/rtld_machine.c (revision 8529ddd3)
1 /*	$OpenBSD: rtld_machine.c,v 1.52 2014/12/27 13:13:25 kettenis Exp $ */
2 
3 /*
4  * Copyright (c) 1999 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  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30 
31 #define _DYN_LOADER
32 
33 #include <sys/types.h>
34 #include <sys/mman.h>
35 #include <sys/exec.h>
36 
37 #include <nlist.h>
38 #include <link.h>
39 #include <signal.h>
40 
41 #include "syscall.h"
42 #include "archdep.h"
43 #include "resolve.h"
44 
45 int
46 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
47 {
48 	long	i;
49 	long	numrela;
50 	long	relrel;
51 	int	fails = 0;
52 	Elf64_Addr loff;
53 	Elf64_Addr prev_value = 0;
54 	const Elf_Sym *prev_sym = NULL;
55 	Elf64_Rela  *relas;
56 	struct load_list *llist;
57 
58 	loff = object->obj_base;
59 	numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela);
60 	relrel = rel == DT_RELA ? object->relacount : 0;
61 	relas = (Elf64_Rela *)(object->Dyn.info[rel]);
62 
63 	if (relas == NULL)
64 		return(0);
65 
66 	if (relrel > numrela) {
67 		_dl_printf("relacount > numrel: %ld > %ld\n", relrel, numrela);
68 		_dl_exit(20);
69 	}
70 
71 	/*
72 	 * unprotect some segments if we need it.
73 	 * XXX - we unprotect way to much. only the text can have cow
74 	 * relocations.
75 	 */
76 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
77 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
78 			if (!(llist->prot & PROT_WRITE)) {
79 				_dl_mprotect(llist->start, llist->size,
80 				    llist->prot|PROT_WRITE);
81 			}
82 		}
83 	}
84 
85 	/* tight loop for leading RELATIVE relocs */
86 	for (i = 0; i < relrel; i++, relas++) {
87 		Elf_Addr *r_addr;
88 
89 #ifdef DEBUG
90 		if (ELF64_R_TYPE(relas->r_info) != R_TYPE(RELATIVE)) {
91 			_dl_printf("RELACOUNT wrong\n");
92 			_dl_exit(20);
93 		}
94 #endif
95 
96 		r_addr = (Elf64_Addr *)(relas->r_offset + loff);
97 
98 		/* Handle unaligned RELATIVE relocs */
99 		if ((((Elf_Addr)r_addr) & 0x7) != 0) {
100 			Elf_Addr tmp;
101 			_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
102 			tmp += loff;
103 			_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
104 		} else
105 			*r_addr += loff;
106 	}
107 	for (; i < numrela; i++, relas++) {
108 		Elf64_Addr *r_addr;
109 		Elf64_Addr ooff;
110 		const Elf64_Sym *sym, *this;
111 		const char *symn;
112 
113 		r_addr = (Elf64_Addr *)(relas->r_offset + loff);
114 
115 		if (ELF64_R_SYM(relas->r_info) == 0xffffffff)
116 			continue;
117 
118 
119 		sym = object->dyn.symtab;
120 		sym += ELF64_R_SYM(relas->r_info);
121 		symn = object->dyn.strtab + sym->st_name;
122 
123 		this = NULL;
124 		switch (ELF64_R_TYPE(relas->r_info)) {
125 		case R_TYPE(REFQUAD):
126 			ooff =  _dl_find_symbol_bysym(object,
127 			    ELF64_R_SYM(relas->r_info), &this,
128 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
129 			    sym, NULL);
130 			if (this == NULL)
131 				goto resolve_failed;
132 			*r_addr += ooff + this->st_value + relas->r_addend;
133 			break;
134 		case R_TYPE(RELATIVE):
135 			/*
136 			 * There is a lot of unaligned RELATIVE
137 			 * relocs generated by gcc in the exception handlers.
138 			 */
139 			if ((((Elf_Addr) r_addr) & 0x7) != 0) {
140 				Elf_Addr tmp;
141 #if 0
142 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr,
143     ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff);
144 #endif
145 				_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
146 				tmp += loff;
147 				_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
148 			} else
149 				*r_addr += loff;
150 			break;
151 		case R_TYPE(JMP_SLOT):
152 			ooff = _dl_find_symbol(symn, &this,
153 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
154 			    sym, object, NULL);
155 			if (this == NULL)
156 				goto resolve_failed;
157 			*r_addr = ooff + this->st_value + relas->r_addend;
158 			break;
159 		case R_TYPE(GLOB_DAT):
160 			if (sym == prev_sym) {
161 				*r_addr = prev_value + relas->r_addend;
162 				break;
163 			}
164 			ooff =  _dl_find_symbol_bysym(object,
165 			    ELF64_R_SYM(relas->r_info), &this,
166 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
167 			    sym, NULL);
168 			if (this == NULL)
169 				goto resolve_failed;
170 			prev_sym = sym;
171 			prev_value = ooff + this->st_value;
172 			*r_addr = prev_value + relas->r_addend;
173 			break;
174 		case R_TYPE(NONE):
175 			break;
176 		default:
177 			_dl_printf("%s:"
178 			    " %s: unsupported relocation '%s' %d at %lx\n",
179 			    _dl_progname, object->load_name, symn,
180 			    ELF64_R_TYPE(relas->r_info), r_addr );
181 			_dl_exit(1);
182 		}
183 		continue;
184 resolve_failed:
185 		if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
186 			fails++;
187 	}
188 	__asm volatile("imb" : : : "memory");
189 
190 	/* reprotect the unprotected segments */
191 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
192 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
193 			if (!(llist->prot & PROT_WRITE))
194 				_dl_mprotect(llist->start, llist->size,
195 				    llist->prot);
196 		}
197 	}
198 	return (fails);
199 }
200 
201 /*
202  * Resolve a symbol at run-time.
203  */
204 Elf_Addr
205 _dl_bind(elf_object_t *object, int reloff)
206 {
207 	Elf_RelA *rela;
208 	Elf_Addr *addr, ooff;
209 	const Elf_Sym *sym, *this;
210 	const char *symn;
211 	const elf_object_t *sobj;
212 	sigset_t savedmask;
213 
214 	rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
215 
216 	addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
217 	if (object->plt_size != 0 && !(*addr >=  object->plt_start &&
218 	    *addr < (object->plt_start + object->plt_size ))) {
219 		/* something is broken, relocation has already occurred */
220 #if 0
221 		DL_DEB(("*addr doesn't point into plt %p obj %s\n",
222 		    *addr, object->load_name));
223 #endif
224 		return *addr;
225 	}
226 
227 	sym = object->dyn.symtab;
228 	sym += ELF64_R_SYM(rela->r_info);
229 	symn = object->dyn.strtab + sym->st_name;
230 
231 	this = NULL;
232 	ooff = _dl_find_symbol(symn, &this,
233 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
234 	if (this == NULL) {
235 		_dl_printf("lazy binding failed!\n");
236 		*(volatile int *)0 = 0;		/* XXX */
237 	}
238 
239 	if (sobj->traced && _dl_trace_plt(sobj, symn))
240 		return ooff + this->st_value + rela->r_addend;
241 
242 	/* if PLT is protected, allow the write */
243 	if (object->plt_size != 0) {
244 		_dl_thread_bind_lock(0, &savedmask);
245 		_dl_mprotect(addr, sizeof(Elf_Addr),
246 		    PROT_READ|PROT_WRITE);
247 	}
248 
249 	*addr = ooff + this->st_value + rela->r_addend;
250 
251 	/* if PLT is (to be protected, change back to RO/X  */
252 	if (object->plt_size != 0) {
253 		_dl_mprotect(addr, sizeof(Elf_Addr),
254 		    PROT_READ);
255 		_dl_thread_bind_lock(1, &savedmask);
256 	}
257 
258 	return *addr;
259 }
260 
261 /*
262  *	Relocate the Global Offset Table (GOT).
263  */
264 int
265 _dl_md_reloc_got(elf_object_t *object, int lazy)
266 {
267 	int	fails = 0;
268 	Elf_Addr *pltgot;
269 	extern void _dl_bind_start(void);	/* XXX */
270 	Elf_Addr ooff;
271 	Elf_Addr plt_addr;
272 	const Elf_Sym *this;
273 
274 	pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
275 
276 	object->got_addr = 0;
277 	object->got_size = 0;
278 	this = NULL;
279 	ooff = _dl_find_symbol("__got_start", &this,
280 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
281 	if (this != NULL)
282 		object->got_addr = ooff + this->st_value;
283 
284 	this = NULL;
285 	ooff = _dl_find_symbol("__got_end", &this,
286 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
287 	if (this != NULL)
288 		object->got_size = ooff + this->st_value  - object->got_addr;
289 
290 	plt_addr = 0;
291 	object->plt_size = 0;
292 	this = NULL;
293 	ooff = _dl_find_symbol("__plt_start", &this,
294 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
295 	if (this != NULL)
296 		plt_addr = ooff + this->st_value;
297 
298 	this = NULL;
299 	ooff = _dl_find_symbol("__plt_end", &this,
300 	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
301 	if (this != NULL)
302 		object->plt_size = ooff + this->st_value  - plt_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 	if (plt_addr == 0)
312 		object->plt_start = 0;
313 	else {
314 		object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
315 		object->plt_size += plt_addr - object->plt_start;
316 		object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
317 	}
318 
319 	if (object->traced)
320 		lazy = 1;
321 
322 	if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) {
323 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
324 	} else {
325 		if (object->obj_base != 0) {
326 			int i, size;
327 			Elf_Addr *addr;
328 			Elf_RelA *rela;
329 
330 			size = object->Dyn.info[DT_PLTRELSZ] /
331 			    sizeof(Elf_RelA);
332 			rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
333 
334 			for (i = 0; i < size; i++) {
335 				addr = (Elf_Addr *)(object->obj_base +
336 				    rela[i].r_offset);
337 				*addr += object->obj_base;
338 			}
339 		}
340 	}
341 	if (pltgot != NULL) {
342 		pltgot[2] = (Elf_Addr)_dl_bind_start;
343 		pltgot[3] = (Elf_Addr)object;
344 	}
345 	if (object->got_size != 0)
346 		_dl_mprotect((void*)object->got_start, object->got_size,
347 		    PROT_READ);
348 	if (object->plt_size != 0)
349 		_dl_mprotect((void*)object->plt_start, object->plt_size,
350 		    PROT_READ|PROT_EXEC);
351 
352 	return (fails);
353 }
354 
355 /* relocate the GOT early */
356 
357 void	_reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase);
358 
359 void
360 _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase)
361 {
362 	const Elf_RelA *rela = 0, *relalim;
363 	Elf_Addr relasz = 0;
364 	Elf_Addr *where;
365 
366 	for (; dynp->d_tag != DT_NULL; dynp++) {
367 		switch (dynp->d_tag) {
368 		case DT_RELA:
369 			rela = (const Elf_RelA *)(relocbase + dynp->d_un.d_ptr);
370 			break;
371 		case DT_RELASZ:
372 			relasz = dynp->d_un.d_val;
373 			break;
374 		}
375 	}
376 	relalim = (const Elf_RelA *)((caddr_t)rela + relasz);
377 	for (; rela < relalim; rela++) {
378 		if (ELF64_R_TYPE(rela->r_info) != RELOC_RELATIVE)
379 			continue;
380 		where = (Elf_Addr *)(relocbase + rela->r_offset);
381 		*where += (Elf_Addr)relocbase;
382 	}
383 }
384