xref: /openbsd/libexec/ld.so/alpha/rtld_machine.c (revision db3296cf)
1 /*	$OpenBSD: rtld_machine.c,v 1.25 2003/07/28 03:11:00 drahn 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/cdefs.h>
35 #include <sys/mman.h>
36 #include <sys/exec.h>
37 
38 #include <machine/elf_machdep.h>
39 
40 #include <nlist.h>
41 #include <link.h>
42 #include <signal.h>
43 
44 #include "syscall.h"
45 #include "archdep.h"
46 #include "resolve.h"
47 
48 void
49 _dl_bcopy(const void *src, void *dest, int size)
50 {
51 	unsigned const char *psrc = src;
52 	unsigned char *pdest = dest;
53 	int i;
54 
55 	for (i = 0; i < size; i++) {
56 		pdest[i] = psrc[i];
57 	}
58 }
59 
60 int
61 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
62 {
63 	long	i;
64 	long	numrela;
65 	long	fails = 0;
66 	Elf64_Addr loff;
67 	Elf64_Rela  *relas;
68 	struct load_list *llist;
69 
70 	loff = object->load_offs;
71 	numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela);
72 	relas = (Elf64_Rela *)(object->Dyn.info[rel]);
73 
74 	if (relas == NULL)
75 		return(0);
76 
77 	/*
78 	 * unprotect some segments if we need it.
79 	 * XXX - we unprotect way to much. only the text can have cow
80 	 * relocations.
81 	 */
82 	if ((rel == DT_REL || rel == DT_RELA)) {
83 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
84 			if (!(llist->prot & PROT_WRITE)) {
85 				_dl_mprotect(llist->start, llist->size,
86 				    llist->prot|PROT_WRITE);
87 			}
88 		}
89 	}
90 
91 	for (i = 0; i < numrela; i++, relas++) {
92 		Elf64_Addr *r_addr;
93 		Elf64_Addr ooff;
94 		const Elf64_Sym *sym, *this;
95 		const char *symn;
96 
97 		r_addr = (Elf64_Addr *)(relas->r_offset + loff);
98 
99 		if (ELF64_R_SYM(relas->r_info) == 0xffffffff)
100 			continue;
101 
102 
103 		sym = object->dyn.symtab;
104 		sym += ELF64_R_SYM(relas->r_info);
105 		symn = object->dyn.strtab + sym->st_name;
106 
107 		this = NULL;
108 		switch (ELF64_R_TYPE(relas->r_info)) {
109 		case R_TYPE(REFQUAD):
110 			ooff = _dl_find_symbol(symn, _dl_objects, &this,
111 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
112 			    sym->st_size, object->load_name);
113 			if (this == NULL)
114 				goto resolve_failed;
115 			*r_addr += ooff + this->st_value + relas->r_addend;
116 			break;
117 		case R_TYPE(RELATIVE):
118 			/*
119 			 * There is a lot of unaligned RELATIVE
120 			 * relocs generated by gcc in the exception handlers.
121 			 */
122 			if ((((Elf_Addr) r_addr) & 0x7) != 0) {
123 				Elf_Addr tmp;
124 #if 0
125 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr,
126     ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff);
127 #endif
128 				_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
129 				tmp += loff;
130 				_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
131 			} else
132 				*r_addr += loff;
133 			break;
134 		case R_TYPE(JMP_SLOT):
135 			ooff = _dl_find_symbol(symn, _dl_objects, &this,
136 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
137 			    sym->st_size, object->load_name);
138 			if (this == NULL)
139 				goto resolve_failed;
140 			*r_addr = ooff + this->st_value + relas->r_addend;
141 			break;
142 		case R_TYPE(GLOB_DAT):
143 			ooff = _dl_find_symbol(symn, _dl_objects, &this,
144 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
145 			    sym->st_size, object->load_name);
146 			if (this == NULL)
147 				goto resolve_failed;
148 			*r_addr = ooff + this->st_value + relas->r_addend;
149 			break;
150 		case R_TYPE(NONE):
151 			break;
152 		default:
153 			_dl_printf("%s:"
154 			    " %s: unsupported relocation '%s' %d at %lx\n",
155 			    _dl_progname, object->load_name, symn,
156 			    ELF64_R_TYPE(relas->r_info), r_addr );
157 			_dl_exit(1);
158 		}
159 		continue;
160 resolve_failed:
161 		_dl_printf("%s: %s :can't resolve reference '%s'\n",
162 		    _dl_progname, object->load_name, symn);
163 		fails++;
164 	}
165 	__asm __volatile("imb" : : : "memory");
166 
167 	/* reprotect the unprotected segments */
168 	if ((rel == DT_REL || rel == DT_RELA)) {
169 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
170 			if (!(llist->prot & PROT_WRITE))
171 				_dl_mprotect(llist->start, llist->size,
172 				    llist->prot);
173 		}
174 	}
175 	return (fails);
176 }
177 
178 /*
179  * Resolve a symbol at run-time.
180  */
181 Elf_Addr
182 _dl_bind(elf_object_t *object, int reloff)
183 {
184 	Elf_RelA *rela;
185 	Elf_Addr *addr, ooff;
186 	const Elf_Sym *sym, *this;
187 	const char *symn;
188 	sigset_t omask, nmask;
189 
190 	rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
191 
192 	sym = object->dyn.symtab;
193 	sym += ELF64_R_SYM(rela->r_info);
194 	symn = object->dyn.strtab + sym->st_name;
195 
196 	addr = (Elf_Addr *)(object->load_offs + rela->r_offset);
197 	this = NULL;
198 	ooff = _dl_find_symbol(symn, _dl_objects, &this,
199 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym->st_size,
200 	    object->load_name);
201 	if (this == NULL) {
202 		_dl_printf("lazy binding failed!\n");
203 		*((int *)0) = 0;	/* XXX */
204 	}
205 	/* if PLT is protected, allow the write */
206 	if (object->plt_size != 0) {
207 		sigfillset(&nmask);
208 		_dl_sigprocmask(SIG_BLOCK, &nmask, &omask);
209 		_dl_mprotect(addr, sizeof(Elf_Addr),
210 		    PROT_READ|PROT_WRITE|PROT_EXEC);
211 	}
212 
213 	*addr = ooff + this->st_value + rela->r_addend;
214 
215 	/* if PLT is (to be protected, change back to RO/X  */
216 	if (object->plt_size != 0) {
217 		_dl_mprotect(addr, sizeof(Elf_Addr),
218 		    PROT_READ|PROT_EXEC);
219 		_dl_sigprocmask(SIG_SETMASK, &omask, NULL);
220 	}
221 
222 	return *addr;
223 }
224 
225 /*
226  *	Relocate the Global Offset Table (GOT).
227  */
228 void
229 _dl_md_reloc_got(elf_object_t *object, int lazy)
230 {
231 	Elf_Addr *pltgot;
232 	extern void _dl_bind_start(void);	/* XXX */
233 	Elf_Addr ooff;
234 	Elf_Addr plt_addr;
235 	const Elf_Sym *this;
236 
237 
238 	pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
239 
240 	object->got_addr = NULL;
241 	object->got_size = 0;
242 	this = NULL;
243 	ooff = _dl_find_symbol("__got_start", object, &this,
244 	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL);
245 	if (this != NULL)
246 		object->got_addr = ooff + this->st_value;
247 
248 	this = NULL;
249 	ooff = _dl_find_symbol("__got_end", object, &this,
250 	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL);
251 	if (this != NULL)
252 		object->got_size = ooff + this->st_value  - object->got_addr;
253 
254 	plt_addr = NULL;
255 	object->plt_size = 0;
256 	this = NULL;
257 	ooff = _dl_find_symbol("__plt_start", object, &this,
258 	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL);
259 	if (this != NULL)
260 		plt_addr = ooff + this->st_value;
261 
262 	this = NULL;
263 	ooff = _dl_find_symbol("__plt_end", object, &this,
264 	    SYM_SEARCH_SELF|SYM_NOWARNNOTFOUND|SYM_PLT, 0, NULL);
265 	if (this != NULL)
266 		object->plt_size = ooff + this->st_value  - plt_addr;
267 
268 	if (object->got_addr == NULL)
269 		object->got_start = NULL;
270 	else {
271 		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
272 		object->got_size += object->got_addr - object->got_start;
273 		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
274 	}
275 	if (plt_addr == NULL)
276 		object->plt_start = NULL;
277 	else {
278 		object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
279 		object->plt_size += plt_addr - object->plt_start;
280 		object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
281 	}
282 
283 	if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) {
284 		_dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
285 		return;
286 	}
287 
288 	if (object->obj_type != OBJTYPE_EXE) {
289 		int i, size;
290 		Elf_Addr *addr;
291 		Elf_RelA *rela;
292 
293 		size = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
294 		rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
295 
296 		for (i = 0; i < size; i++) {
297 			addr = (Elf_Addr *)(object->load_offs + rela[i].r_offset);
298 			*addr += object->load_offs;
299 		}
300 	}
301 
302 	pltgot[2] = (Elf_Addr)_dl_bind_start;
303 	pltgot[3] = (Elf_Addr)object;
304 	if (object->got_size != 0)
305 		_dl_mprotect((void*)object->got_addr, object->got_size,
306 		    PROT_READ);
307 	if (object->plt_size != 0)
308 		_dl_mprotect((void*)object->plt_start, object->plt_size,
309 		    PROT_READ|PROT_EXEC);
310 }
311 
312 /* relocate the GOT early */
313 
314 void	_reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase);
315 
316 void
317 _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase)
318 {
319 	const Elf_RelA *rela = 0, *relalim;
320 	Elf_Addr relasz = 0;
321 	Elf_Addr *where;
322 
323 	for (; dynp->d_tag != DT_NULL; dynp++) {
324 		switch (dynp->d_tag) {
325 		case DT_RELA:
326 			rela = (const Elf_RelA *)(relocbase + dynp->d_un.d_ptr);
327 			break;
328 		case DT_RELASZ:
329 			relasz = dynp->d_un.d_val;
330 			break;
331 		}
332 	}
333 	relalim = (const Elf_RelA *)((caddr_t)rela + relasz);
334 	for (; rela < relalim; rela++) {
335 		where = (Elf_Addr *)(relocbase + rela->r_offset);
336 		/* XXX For some reason I see a few GLOB_DAT relocs here. */
337 		*where += (Elf_Addr)relocbase;
338 	}
339 }
340