xref: /openbsd-src/libexec/ld.so/alpha/rtld_machine.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: rtld_machine.c,v 1.59 2016/06/21 15:25:36 deraadt 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 #include <sys/syscall.h>
37 #include <sys/unistd.h>
38 #include <machine/pal.h>
39 
40 #include <nlist.h>
41 #include <link.h>
42 
43 #include "syscall.h"
44 #include "archdep.h"
45 #include "resolve.h"
46 
47 #define	DT_PROC(n)	((n) - DT_LOPROC + DT_NUM)
48 
49 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
50 
51 int
52 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
53 {
54 	long	i;
55 	long	numrela;
56 	long	relrel;
57 	int	fails = 0;
58 	Elf64_Addr loff;
59 	Elf64_Addr prev_value = 0;
60 	const Elf_Sym *prev_sym = NULL;
61 	Elf64_Rela  *relas;
62 	struct load_list *llist;
63 
64 	loff = object->obj_base;
65 	numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela);
66 	relrel = rel == DT_RELA ? object->relacount : 0;
67 	relas = (Elf64_Rela *)(object->Dyn.info[rel]);
68 
69 	if (relas == NULL)
70 		return(0);
71 
72 	if (relrel > numrela) {
73 		_dl_printf("relacount > numrel: %ld > %ld\n", relrel, numrela);
74 		_dl_exit(20);
75 	}
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 ((object->dyn.textrel == 1) && (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 				    PROT_READ | PROT_WRITE);
87 			}
88 		}
89 	}
90 
91 	/* tight loop for leading RELATIVE relocs */
92 	for (i = 0; i < relrel; i++, relas++) {
93 		Elf_Addr *r_addr;
94 
95 #ifdef DEBUG
96 		if (ELF64_R_TYPE(relas->r_info) != R_TYPE(RELATIVE)) {
97 			_dl_printf("RELACOUNT wrong\n");
98 			_dl_exit(20);
99 		}
100 #endif
101 
102 		r_addr = (Elf64_Addr *)(relas->r_offset + loff);
103 
104 		/* Handle unaligned RELATIVE relocs */
105 		if ((((Elf_Addr)r_addr) & 0x7) != 0) {
106 			Elf_Addr tmp;
107 			_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
108 			tmp += loff;
109 			_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
110 		} else
111 			*r_addr += loff;
112 	}
113 	for (; i < numrela; i++, relas++) {
114 		Elf64_Addr *r_addr;
115 		Elf64_Addr ooff;
116 		const Elf64_Sym *sym, *this;
117 		const char *symn;
118 
119 		r_addr = (Elf64_Addr *)(relas->r_offset + loff);
120 
121 		if (ELF64_R_SYM(relas->r_info) == 0xffffffff)
122 			continue;
123 
124 
125 		sym = object->dyn.symtab;
126 		sym += ELF64_R_SYM(relas->r_info);
127 		symn = object->dyn.strtab + sym->st_name;
128 
129 		this = NULL;
130 		switch (ELF64_R_TYPE(relas->r_info)) {
131 		case R_TYPE(REFQUAD):
132 			ooff =  _dl_find_symbol_bysym(object,
133 			    ELF64_R_SYM(relas->r_info), &this,
134 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
135 			    sym, NULL);
136 			if (this == NULL)
137 				goto resolve_failed;
138 			*r_addr += ooff + this->st_value + relas->r_addend;
139 			break;
140 		case R_TYPE(RELATIVE):
141 			/*
142 			 * There is a lot of unaligned RELATIVE
143 			 * relocs generated by gcc in the exception handlers.
144 			 */
145 			if ((((Elf_Addr) r_addr) & 0x7) != 0) {
146 				Elf_Addr tmp;
147 #if 0
148 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr,
149     ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff);
150 #endif
151 				_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
152 				tmp += loff;
153 				_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
154 			} else
155 				*r_addr += loff;
156 			break;
157 		case R_TYPE(JMP_SLOT):
158 			ooff = _dl_find_symbol(symn, &this,
159 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
160 			    sym, object, NULL);
161 			if (this == NULL)
162 				goto resolve_failed;
163 			*r_addr = ooff + this->st_value + relas->r_addend;
164 			break;
165 		case R_TYPE(GLOB_DAT):
166 			if (sym == prev_sym) {
167 				*r_addr = prev_value + relas->r_addend;
168 				break;
169 			}
170 			ooff =  _dl_find_symbol_bysym(object,
171 			    ELF64_R_SYM(relas->r_info), &this,
172 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
173 			    sym, NULL);
174 			if (this == NULL)
175 				goto resolve_failed;
176 			prev_sym = sym;
177 			prev_value = ooff + this->st_value;
178 			*r_addr = prev_value + relas->r_addend;
179 			break;
180 		case R_TYPE(NONE):
181 			break;
182 		default:
183 			_dl_printf("%s:"
184 			    " %s: unsupported relocation '%s' %d at %lx\n",
185 			    __progname, object->load_name, symn,
186 			    ELF64_R_TYPE(relas->r_info), r_addr );
187 			_dl_exit(1);
188 		}
189 		continue;
190 resolve_failed:
191 		if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
192 			fails++;
193 	}
194 	__asm volatile("imb" : : : "memory");
195 
196 	/* reprotect the unprotected segments */
197 	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
198 		for (llist = object->load_list; llist != NULL; llist = llist->next) {
199 			if (!(llist->prot & PROT_WRITE))
200 				_dl_mprotect(llist->start, llist->size,
201 				    llist->prot);
202 		}
203 	}
204 	return (fails);
205 }
206 
207 /*
208  * Resolve a symbol at run-time.
209  */
210 Elf_Addr
211 _dl_bind(elf_object_t *object, int reloff)
212 {
213 	Elf_RelA *rela;
214 	Elf_Addr ooff;
215 	const Elf_Sym *sym, *this;
216 	const char *symn;
217 	const elf_object_t *sobj;
218 	uint64_t cookie = pcookie;
219 	struct {
220 		struct __kbind param;
221 		Elf_Addr newval;
222 	} buf;
223 
224 	rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
225 
226 	sym = object->dyn.symtab;
227 	sym += ELF64_R_SYM(rela->r_info);
228 	symn = object->dyn.strtab + sym->st_name;
229 
230 	this = NULL;
231 	ooff = _dl_find_symbol(symn, &this,
232 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
233 	if (this == NULL) {
234 		_dl_printf("lazy binding failed!\n");
235 		*(volatile int *)0 = 0;		/* XXX */
236 	}
237 
238 	buf.newval = ooff + this->st_value + rela->r_addend;
239 
240 	if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn))
241 		return (buf.newval);
242 
243 	buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
244 	buf.param.kb_size = sizeof(Elf_Addr);
245 
246 	/* directly code the syscall, so that it's actually inline here */
247 	{
248 		register long syscall_num __asm("$0") /* v0 */ = SYS_kbind;
249 		register void *arg1 __asm("$16") /* a0 */ = &buf;
250 		register long  arg2 __asm("$17") /* a1 */ = sizeof(buf);
251 		register long  arg3 __asm("$18") /* a2 */ = cookie;
252 
253 		__asm volatile( "call_pal %1" : "+r" (syscall_num)
254 		    : "i" (PAL_OSF1_callsys), "r" (arg1), "r" (arg2),
255 		    "r" (arg3) : "$19", "$20", "memory");
256 	}
257 
258 	return (buf.newval);
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 	extern void _dl_bind_secureplt(void);	/* XXX */
271 	Elf_Addr seg_start;
272 	u_long pltro;
273 
274 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
275 		return (0);
276 
277 	pltro = object->Dyn.info[DT_PROC(DT_ALPHA_PLTRO)];
278 	pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
279 
280 	if (object->traced)
281 		lazy = 1;
282 
283 	if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) {
284 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
285 	} else {
286 		if (object->obj_base != 0) {
287 			int i, size;
288 			Elf_Addr *addr;
289 			Elf_RelA *rela;
290 
291 			size = object->Dyn.info[DT_PLTRELSZ] /
292 			    sizeof(Elf_RelA);
293 			rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
294 
295 			for (i = 0; i < size; i++) {
296 				addr = (Elf_Addr *)(object->obj_base +
297 				    rela[i].r_offset);
298 				*addr += object->obj_base;
299 			}
300 		}
301 		if (pltro == 0) {
302 			pltgot[2] = (Elf_Addr)_dl_bind_start;
303 			pltgot[3] = (Elf_Addr)object;
304 		} else {
305 			pltgot[0] = (Elf_Addr)_dl_bind_secureplt;
306 			pltgot[1] = (Elf_Addr)object;
307 		}
308 	}
309 
310 	/* mprotect the GOT */
311 	seg_start = 0;
312 	if (pltro != 0)
313 		seg_start = (Elf_Addr)pltgot;
314 	_dl_protect_segment(object, seg_start, "__got_start", "__got_end",
315 	    PROT_READ);
316 
317 	/* mprotect the PLT, if it isn't already read-only */
318 	if (pltro == 0)
319 		_dl_protect_segment(object, (Elf_Addr)pltgot, "__plt_start",
320 		    "__plt_end", PROT_READ|PROT_EXEC);
321 
322 	return (fails);
323 }
324 
325 /* relocate the GOT early */
326 
327 void	_reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase);
328 
329 void
330 _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase)
331 {
332 	const Elf_RelA *rela = NULL, *relalim;
333 	Elf_Addr relasz = 0;
334 	Elf_Addr *where;
335 
336 	for (; dynp->d_tag != DT_NULL; dynp++) {
337 		switch (dynp->d_tag) {
338 		case DT_RELA:
339 			rela = (const Elf_RelA *)(relocbase + dynp->d_un.d_ptr);
340 			break;
341 		case DT_RELASZ:
342 			relasz = dynp->d_un.d_val;
343 			break;
344 		}
345 	}
346 	relalim = (const Elf_RelA *)((caddr_t)rela + relasz);
347 	for (; rela < relalim; rela++) {
348 		if (ELF64_R_TYPE(rela->r_info) != RELOC_RELATIVE)
349 			continue;
350 		where = (Elf_Addr *)(relocbase + rela->r_offset);
351 		*where += (Elf_Addr)relocbase;
352 	}
353 }
354