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