xref: /openbsd/libexec/ld.so/m88k/rtld_machine.c (revision 264ca280)
1 /*	$OpenBSD: rtld_machine.c,v 1.15 2016/06/21 15:25:37 deraadt 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 #include <sys/syscall.h>
49 #include <sys/unistd.h>
50 
51 #include <nlist.h>
52 #include <link.h>
53 
54 #include "syscall.h"
55 #include "archdep.h"
56 #include "resolve.h"
57 
58 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
59 void	_dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr);
60 
61 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
62 
63 int
64 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
65 {
66 	int	i;
67 	int	numrela;
68 	int	fails = 0;
69 	struct load_list *llist;
70 	Elf32_Addr loff;
71 	Elf32_Rela  *relas;
72 	Elf32_Addr prev_value = 0, prev_ooff = 0;
73 	const Elf32_Sym *prev_sym = NULL;
74 
75 	loff = object->obj_base;
76 	numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
77 	relas = (Elf32_Rela *)(object->Dyn.info[rel]);
78 
79 #ifdef DL_PRINTF_DEBUG
80 	_dl_printf("object relocation size %x, numrela %x\n",
81 	    object->Dyn.info[relasz], numrela);
82 #endif
83 
84 	if (relas == NULL)
85 		return(0);
86 
87 	/*
88 	 * Change protection of all write protected segments in the object
89 	 * so we can do relocations such as DISP26. After relocation,
90 	 * restore protection.
91 	 */
92 	if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) {
93 		for (llist = object->load_list; llist != NULL;
94 		    llist = llist->next) {
95 			if (!(llist->prot & PROT_WRITE)) {
96 				_dl_mprotect(llist->start, llist->size,
97 				    PROT_READ | PROT_WRITE);
98 			}
99 		}
100 	}
101 
102 	for (i = 0; i < numrela; i++, relas++) {
103 		Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
104 		Elf32_Addr ooff, addend, newval;
105 		const Elf32_Sym *sym, *this;
106 		const char *symn;
107 		int type;
108 
109 		type = ELF32_R_TYPE(relas->r_info);
110 
111 		if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
112 			continue;
113 
114 		if (type == RELOC_NONE)
115 			continue;
116 
117 		sym = object->dyn.symtab;
118 		sym += ELF32_R_SYM(relas->r_info);
119 		symn = object->dyn.strtab + sym->st_name;
120 
121 		if (type == RELOC_COPY) {
122 			/*
123 			 * we need to find a symbol, that is not in the current
124 			 * object, start looking at the beginning of the list,
125 			 * searching all objects but _not_ the current object,
126 			 * first one found wins.
127 			 */
128 			const Elf32_Sym *cpysrc = NULL;
129 			Elf32_Addr src_loff;
130 			int size;
131 
132 			src_loff = 0;
133 			src_loff = _dl_find_symbol(symn, &cpysrc,
134 			    SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
135 			    sym, object, NULL);
136 			if (cpysrc != NULL) {
137 				size = sym->st_size;
138 				if (sym->st_size != cpysrc->st_size) {
139 					/* _dl_find_symbol() has warned
140 					   about this already */
141 					size = sym->st_size < cpysrc->st_size ?
142 					    sym->st_size : cpysrc->st_size;
143 				}
144 				_dl_bcopy((void *)(src_loff + cpysrc->st_value),
145 				    r_addr, size);
146 			} else
147 				fails++;
148 
149 			continue;
150 		}
151 
152 		if (ELF32_R_SYM(relas->r_info) &&
153 		    !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
154 		    ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
155 		    sym != prev_sym) {
156 			if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
157 			    ELF32_ST_TYPE(sym->st_info) == STT_SECTION) {
158 				prev_sym = sym;
159 				prev_value = 0;
160 				prev_ooff = object->obj_base;
161 			} else {
162 				this = NULL;
163 				ooff = _dl_find_symbol_bysym(object,
164 				    ELF32_R_SYM(relas->r_info), &this,
165 				    SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
166 				    ((type == RELOC_GOTP_ENT) ?
167 				    SYM_PLT : SYM_NOTPLT), sym, NULL);
168 
169 				if (this == NULL) {
170 					if (ELF_ST_BIND(sym->st_info) !=
171 					    STB_WEAK)
172 						fails++;
173 					continue;
174 				}
175 				prev_sym = sym;
176 				prev_value = this->st_value;
177 				prev_ooff = ooff;
178 			}
179 		}
180 
181 		if (type == RELOC_GOTP_ENT) {
182 			_dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
183 			    relas->r_addend + loff,
184 			    prev_ooff + prev_value);
185 			continue;
186 		}
187 
188 		if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
189 		    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
190 		    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE))
191 			addend = relas->r_addend;
192 		else
193 			addend = prev_value + relas->r_addend;
194 
195 		switch (type) {
196 		case RELOC_16L:
197 			newval = prev_ooff + addend;
198 			*(unsigned short *)r_addr = newval & 0xffff;
199 			_dl_cacheflush((unsigned long)r_addr, 2);
200 			break;
201 		case RELOC_16H:
202 			newval = prev_ooff + addend;
203 			*(unsigned short *)r_addr = newval >> 16;
204 			_dl_cacheflush((unsigned long)r_addr, 2);
205 			break;
206 		case RELOC_DISP26:
207 			newval = prev_ooff + addend;
208 			newval -= (Elf_Addr)r_addr;
209 			if ((newval >> 28) != 0 && (newval >> 28) != 0x0f) {
210 				_dl_printf("%s: %s: out of range DISP26"
211 				    " relocation to '%s' at %x\n",
212 				    __progname, object->load_name, symn,
213 				    r_addr);
214 				_dl_exit(1);
215 			}
216 			*r_addr = (*r_addr & 0xfc000000) |
217 			    (((int32_t)newval >> 2) & 0x03ffffff);
218 			_dl_cacheflush((unsigned long)r_addr, 4);
219 			break;
220 		case RELOC_32:
221 			newval = prev_ooff + addend;
222 			*r_addr = newval;
223 			break;
224 		case RELOC_BBASED_32:
225 			newval = loff + addend;
226 			*r_addr = newval;
227 			break;
228 		default:
229 			_dl_printf("%s:"
230 			    " %s: unsupported relocation '%s' %d at %x\n",
231 			    __progname, object->load_name, symn, type, r_addr);
232 			_dl_exit(1);
233 		}
234 	}
235 
236 	/* reprotect the unprotected segments */
237 	if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) {
238 		for (llist = object->load_list; llist != NULL;
239 		    llist = llist->next) {
240 			if (!(llist->prot & PROT_WRITE))
241 				_dl_mprotect(llist->start, llist->size,
242 				    llist->prot);
243 		}
244 	}
245 
246 	return(fails);
247 }
248 
249 /*
250  * GOTP_ENT relocations are special in that they define both a .got and a
251  * .plt relocation.
252  */
253 void
254 _dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
255 {
256 	uint16_t *plt_entry = (uint16_t *)plt_addr;
257 
258 	/* .got update */
259 	*(Elf_Addr *)got_addr = val;
260 	/* .plt update */
261 	plt_entry[1] = got_addr >> 16;
262 	plt_entry[3] = got_addr & 0xffff;
263 }
264 
265 /*
266  *	Relocate the Global Offset Table (GOT).
267  *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
268  *	otherwise the lazy binding plt operation is preserved.
269  */
270 int
271 _dl_md_reloc_got(elf_object_t *object, int lazy)
272 {
273 	extern void _dl_bind_start(void);	/* XXX */
274 	int	fails = 0;
275 	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
276 	Elf_Addr ooff;
277 	Elf_Addr plt_start, plt_end;
278 	size_t plt_size;
279 	const Elf_Sym *this;
280 
281 	if (pltgot == NULL)
282 		return (0);
283 
284 	pltgot[1] = (Elf_Addr)object;
285 	pltgot[2] = (Elf_Addr)_dl_bind_start;
286 
287 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
288 		return (0);
289 
290 	if (object->traced)
291 		lazy = 1;
292 
293 	/*
294 	 * Post-5.3 binaries use dynamic tags to provide the .plt boundaries.
295 	 * If the tags are missing, fall back to the special symbol search.
296 	 */
297 	plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM];
298 	plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM];
299 	if (plt_start == 0 || plt_end == 0) {
300 		this = NULL;
301 		ooff = _dl_find_symbol("__plt_start", &this,
302 		    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL,
303 		    object, NULL);
304 		if (this != NULL)
305 			plt_start = ooff + this->st_value;
306 		else
307 			plt_start = 0;
308 
309 		this = NULL;
310 		ooff = _dl_find_symbol("__plt_end", &this,
311 		    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL,
312 		    object, NULL);
313 		if (this != NULL)
314 			plt_end = ooff + this->st_value;
315 		else
316 			plt_start = 0;		/* not enough to go on */
317 	} else {
318 		plt_start += object->obj_base;
319 		plt_end += object->obj_base;
320 	}
321 
322 	if (plt_start == 0)
323 		plt_size = 0;
324 	else {
325 		plt_start = ELF_TRUNC(plt_start, _dl_pagesz);
326 		plt_size = ELF_ROUND(plt_end, _dl_pagesz) - plt_start;
327 
328 		/*
329 		 * GOT relocation will require PLT to be writeable.
330 		 */
331 		if (!lazy || object->obj_base != 0)
332 			_dl_mprotect((void *)plt_start, plt_size,
333 			    PROT_READ | PROT_WRITE);
334 	}
335 
336 	if (!lazy) {
337 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
338 	} else {
339 		if (object->obj_base != 0) {
340 			int cnt;
341 			Elf_Addr *addr;
342 			Elf_RelA *rela;
343 
344 			cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
345 			rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
346 
347 			for (; cnt != 0; cnt--, rela++) {
348 				addr = (Elf_Addr *)(object->obj_base +
349 				    rela->r_offset);
350 				_dl_md_reloc_gotp_ent((Elf_Addr)addr,
351 				    object->obj_base + rela->r_addend,
352 				    *addr + object->obj_base);
353 			}
354 		}
355 	}
356 
357 	/* mprotect the GOT */
358 	_dl_protect_segment(object, 0, "__got_start", "__got_end", PROT_READ);
359 
360 	if (plt_size != 0) {
361 		if (!lazy || object->obj_base != 0) {
362 			/*
363 			 * Force a cache sync on the whole plt here,
364 			 * otherwise I$ might have stale information.
365 			 */
366 			_dl_cacheflush(plt_start, plt_size);
367 			_dl_mprotect((void *)plt_start, plt_size,
368 			    PROT_READ | PROT_EXEC);
369 		}
370 	}
371 
372 	return (fails);
373 }
374 
375 Elf_Addr
376 _dl_bind(elf_object_t *object, int reloff)
377 {
378 	Elf_RelA *rel;
379 	Elf_Addr ooff;
380 	const Elf_Sym *sym, *this;
381 	const char *symn;
382 	const elf_object_t *sobj;
383 	uint64_t cookie = pcookie;
384 	struct {
385 		struct __kbind param;
386 		Elf_Addr newval;
387 	} buf;
388 
389 	rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
390 
391 	sym = object->dyn.symtab;
392 	sym += ELF_R_SYM(rel->r_info);
393 	symn = object->dyn.strtab + sym->st_name;
394 
395 	this = NULL;
396 	ooff = _dl_find_symbol(symn, &this,
397 	    SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, &sobj);
398 	if (this == NULL) {
399 		_dl_printf("lazy binding failed!\n");
400 		*(volatile int *)0 = 0;		/* XXX */
401 	}
402 
403 	buf.newval = ooff + this->st_value;
404 
405 	if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn))
406 		return (buf.newval);
407 
408 	buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
409 	buf.param.kb_size = sizeof(Elf_Addr);
410 
411 	/* directly code the syscall, so that it's actually inline here */
412 	{
413 		register long syscall_num __asm("r13") = SYS_kbind;
414 		register void *arg1 __asm("r2") = &buf;
415 		register long  arg2 __asm("r3") = sizeof(buf);
416 		register long  arg3 __asm("r4") = 0xffffffff & (cookie >> 32);
417 		register long  arg4 __asm("r5") = 0xffffffff &  cookie;
418 
419 		__asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0"
420 		    : "+r" (arg1), "+r" (arg2) : "r" (syscall_num),
421 		    "r" (arg3), "r" (arg4) : "cc", "memory");
422 	}
423 
424 	return (buf.newval);
425 }
426