xref: /openbsd-src/libexec/ld.so/m88k/rtld_machine.c (revision b722ba42570161220f25c5d789b5bec8a0166743)
1*b722ba42Sguenther /*	$OpenBSD: rtld_machine.c,v 1.31 2022/01/08 06:49:42 guenther Exp $	*/
2d3d281f4Smiod 
3d3d281f4Smiod /*
4d3d281f4Smiod  * Copyright (c) 2013 Miodrag Vallat.
5d3d281f4Smiod  *
6d3d281f4Smiod  * Permission to use, copy, modify, and distribute this software for any
7d3d281f4Smiod  * purpose with or without fee is hereby granted, provided that the above
8d3d281f4Smiod  * copyright notice and this permission notice appear in all copies.
9d3d281f4Smiod  *
10d3d281f4Smiod  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d3d281f4Smiod  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d3d281f4Smiod  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d3d281f4Smiod  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d3d281f4Smiod  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d3d281f4Smiod  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d3d281f4Smiod  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d3d281f4Smiod  */
18d3d281f4Smiod /*
19d3d281f4Smiod  * Copyright (c) 1999 Dale Rahn
20d3d281f4Smiod  *
21d3d281f4Smiod  * Redistribution and use in source and binary forms, with or without
22d3d281f4Smiod  * modification, are permitted provided that the following conditions
23d3d281f4Smiod  * are met:
24d3d281f4Smiod  * 1. Redistributions of source code must retain the above copyright
25d3d281f4Smiod  *    notice, this list of conditions and the following disclaimer.
26d3d281f4Smiod  * 2. Redistributions in binary form must reproduce the above copyright
27d3d281f4Smiod  *    notice, this list of conditions and the following disclaimer in the
28d3d281f4Smiod  *    documentation and/or other materials provided with the distribution.
29d3d281f4Smiod  *
30d3d281f4Smiod  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
31d3d281f4Smiod  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32d3d281f4Smiod  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33d3d281f4Smiod  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
34d3d281f4Smiod  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35d3d281f4Smiod  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36d3d281f4Smiod  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37d3d281f4Smiod  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38d3d281f4Smiod  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39d3d281f4Smiod  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40d3d281f4Smiod  * SUCH DAMAGE.
41d3d281f4Smiod  *
42d3d281f4Smiod  */
43d3d281f4Smiod 
44d3d281f4Smiod #define _DYN_LOADER
45d3d281f4Smiod 
46d3d281f4Smiod #include <sys/types.h>
47*b722ba42Sguenther #include <sys/exec_elf.h>
487ad3219aSguenther #include <sys/syscall.h>
497ad3219aSguenther #include <sys/unistd.h>
50d3d281f4Smiod 
51*b722ba42Sguenther #include <machine/reloc.h>
52d3d281f4Smiod 
53*b722ba42Sguenther #include "util.h"
54d3d281f4Smiod #include "resolve.h"
55d3d281f4Smiod 
561d60349dSguenther int	_dl_cacheflush(unsigned long, size_t);
57d3d281f4Smiod Elf_Addr _dl_bind(elf_object_t *object, int reloff);
58d3d281f4Smiod void	_dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr);
59d3d281f4Smiod 
607ad3219aSguenther int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
617ad3219aSguenther 
62d3d281f4Smiod int
_dl_md_reloc(elf_object_t * object,int rel,int relasz)63d3d281f4Smiod _dl_md_reloc(elf_object_t *object, int rel, int relasz)
64d3d281f4Smiod {
65d3d281f4Smiod 	int	i;
66d3d281f4Smiod 	int	numrela;
671bacec36Sguenther 	int	relrela;
68d3d281f4Smiod 	int	fails = 0;
69e3b0f1d9Sguenther 	Elf_Addr loff;
70e3b0f1d9Sguenther 	Elf_RelA  *relas;
71e3b0f1d9Sguenther 	Elf_Addr prev_value = 0, prev_ooff = 0;
72e3b0f1d9Sguenther 	const Elf_Sym *prev_sym = NULL;
73d3d281f4Smiod 
74d3d281f4Smiod 	loff = object->obj_base;
75e3b0f1d9Sguenther 	numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
761bacec36Sguenther 	relrela = rel == DT_RELA ? object->relacount : 0;
771bacec36Sguenther 
78e3b0f1d9Sguenther 	relas = (Elf_RelA *)(object->Dyn.info[rel]);
79d3d281f4Smiod 
80d3d281f4Smiod 	if (relas == NULL)
81e3b0f1d9Sguenther 		return 0;
82d3d281f4Smiod 
833b50b772Sguenther 	if (relrela > numrela)
843b50b772Sguenther 		_dl_die("relacount > numrel: %d > %d", relrela, numrela);
851bacec36Sguenther 
861bacec36Sguenther 	/* tight loop for leading RELATIVE relocs */
871bacec36Sguenther 	for (i = 0; i < relrela; i++, relas++) {
88e3b0f1d9Sguenther 		Elf_Addr *r_addr;
891bacec36Sguenther 
90e3b0f1d9Sguenther 		r_addr = (Elf_Addr *)(relas->r_offset + loff);
911bacec36Sguenther 		*r_addr = relas->r_addend + loff;
921bacec36Sguenther 	}
931bacec36Sguenther 	for (; i < numrela; i++, relas++) {
94e3b0f1d9Sguenther 		Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
95e3b0f1d9Sguenther 		Elf_Addr addend, newval;
96e3b0f1d9Sguenther 		const Elf_Sym *sym;
97d3d281f4Smiod 		const char *symn;
98d3d281f4Smiod 		int type;
99d3d281f4Smiod 
100e3b0f1d9Sguenther 		type = ELF_R_TYPE(relas->r_info);
101d3d281f4Smiod 
102d3d281f4Smiod 		if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
103d3d281f4Smiod 			continue;
104d3d281f4Smiod 
105d3d281f4Smiod 		if (type == RELOC_NONE)
106d3d281f4Smiod 			continue;
107d3d281f4Smiod 
108d3d281f4Smiod 		sym = object->dyn.symtab;
109e3b0f1d9Sguenther 		sym += ELF_R_SYM(relas->r_info);
110d3d281f4Smiod 		symn = object->dyn.strtab + sym->st_name;
111d3d281f4Smiod 
112d3d281f4Smiod 		if (type == RELOC_COPY) {
113d3d281f4Smiod 			/*
114d3d281f4Smiod 			 * we need to find a symbol, that is not in the current
115d3d281f4Smiod 			 * object, start looking at the beginning of the list,
116d3d281f4Smiod 			 * searching all objects but _not_ the current object,
117d3d281f4Smiod 			 * first one found wins.
118d3d281f4Smiod 			 */
119143e5accSguenther 			struct sym_res sr;
120d3d281f4Smiod 
121143e5accSguenther 			sr = _dl_find_symbol(symn,
122d3d281f4Smiod 			    SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
123e83568d5Saoyama 			    sym, object);
124143e5accSguenther 			if (sr.sym != NULL) {
125143e5accSguenther 				_dl_bcopy((void *)(sr.obj->obj_base +
126143e5accSguenther 				    sr.sym->st_value), r_addr, sym->st_size);
127d3d281f4Smiod 			} else
128d3d281f4Smiod 				fails++;
129d3d281f4Smiod 
130d3d281f4Smiod 			continue;
131d3d281f4Smiod 		}
132d3d281f4Smiod 
133e3b0f1d9Sguenther 		if (ELF_R_SYM(relas->r_info) &&
134e3b0f1d9Sguenther 		    !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
135e3b0f1d9Sguenther 		    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
1360c76560fSmiod 		    sym != prev_sym) {
137e3b0f1d9Sguenther 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
138e3b0f1d9Sguenther 			    ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
13960930782Smiod 				prev_sym = sym;
14060930782Smiod 				prev_value = 0;
14160930782Smiod 				prev_ooff = object->obj_base;
14260930782Smiod 			} else {
143143e5accSguenther 				struct sym_res sr;
144143e5accSguenther 
145143e5accSguenther 				sr = _dl_find_symbol(symn,
146d3d281f4Smiod 				    SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
14760930782Smiod 				    ((type == RELOC_GOTP_ENT) ?
148e83568d5Saoyama 				    SYM_PLT : SYM_NOTPLT), sym, object);
149d3d281f4Smiod 
150143e5accSguenther 				if (sr.sym == NULL) {
15160930782Smiod 					if (ELF_ST_BIND(sym->st_info) !=
15260930782Smiod 					    STB_WEAK)
153d3d281f4Smiod 						fails++;
154d3d281f4Smiod 					continue;
155d3d281f4Smiod 				}
15688098a4dSguenther 				prev_sym = sym;
157143e5accSguenther 				prev_value = sr.sym->st_value;
158143e5accSguenther 				prev_ooff = sr.obj->obj_base;
159d3d281f4Smiod 			}
16060930782Smiod 		}
161d3d281f4Smiod 
162d3d281f4Smiod 		if (type == RELOC_GOTP_ENT) {
163d3d281f4Smiod 			_dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
164d3d281f4Smiod 			    relas->r_addend + loff,
16588098a4dSguenther 			    prev_ooff + prev_value);
166d3d281f4Smiod 			continue;
167d3d281f4Smiod 		}
168d3d281f4Smiod 
169e3b0f1d9Sguenther 		if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
170e3b0f1d9Sguenther 		    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
171e3b0f1d9Sguenther 		    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE))
172d3d281f4Smiod 			addend = relas->r_addend;
173d3d281f4Smiod 		else
17488098a4dSguenther 			addend = prev_value + relas->r_addend;
175d3d281f4Smiod 
176d3d281f4Smiod 		switch (type) {
17760930782Smiod 		case RELOC_16L:
17860930782Smiod 			newval = prev_ooff + addend;
17960930782Smiod 			*(unsigned short *)r_addr = newval & 0xffff;
18060930782Smiod 			_dl_cacheflush((unsigned long)r_addr, 2);
18160930782Smiod 			break;
18260930782Smiod 		case RELOC_16H:
18360930782Smiod 			newval = prev_ooff + addend;
18460930782Smiod 			*(unsigned short *)r_addr = newval >> 16;
18560930782Smiod 			_dl_cacheflush((unsigned long)r_addr, 2);
18660930782Smiod 			break;
18760930782Smiod 		case RELOC_DISP26:
18860930782Smiod 			newval = prev_ooff + addend;
18960930782Smiod 			newval -= (Elf_Addr)r_addr;
1903b50b772Sguenther 			if ((newval >> 28) != 0 && (newval >> 28) != 0x0f)
1913b50b772Sguenther 				_dl_die("%s: out of range DISP26"
1922f3c3df9Sguenther 				    " relocation to '%s' at %p\n",
1932f3c3df9Sguenther 				    object->load_name, symn, (void *)r_addr);
19460930782Smiod 			*r_addr = (*r_addr & 0xfc000000) |
19560930782Smiod 			    (((int32_t)newval >> 2) & 0x03ffffff);
19660930782Smiod 			_dl_cacheflush((unsigned long)r_addr, 4);
19760930782Smiod 			break;
198d3d281f4Smiod 		case RELOC_32:
19988098a4dSguenther 			newval = prev_ooff + addend;
200d3d281f4Smiod 			*r_addr = newval;
201d3d281f4Smiod 			break;
202d3d281f4Smiod 		case RELOC_BBASED_32:
203d3d281f4Smiod 			newval = loff + addend;
204d3d281f4Smiod 			*r_addr = newval;
205d3d281f4Smiod 			break;
206d3d281f4Smiod 		default:
2072f3c3df9Sguenther 			_dl_die("%s: unsupported relocation '%s' %d at %p\n",
2082f3c3df9Sguenther 			    object->load_name, symn, type, (void *)r_addr);
209d3d281f4Smiod 		}
210d3d281f4Smiod 	}
211d3d281f4Smiod 
212e3b0f1d9Sguenther 	return fails;
213d3d281f4Smiod }
214d3d281f4Smiod 
215d3d281f4Smiod /*
216d3d281f4Smiod  * GOTP_ENT relocations are special in that they define both a .got and a
217d3d281f4Smiod  * .plt relocation.
218d3d281f4Smiod  */
219d3d281f4Smiod void
_dl_md_reloc_gotp_ent(Elf_Addr got_addr,Elf_Addr plt_addr,Elf_Addr val)220d3d281f4Smiod _dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
221d3d281f4Smiod {
222d3d281f4Smiod 	uint16_t *plt_entry = (uint16_t *)plt_addr;
223d3d281f4Smiod 
224d3d281f4Smiod 	/* .got update */
225d3d281f4Smiod 	*(Elf_Addr *)got_addr = val;
226d3d281f4Smiod 	/* .plt update */
227d3d281f4Smiod 	plt_entry[1] = got_addr >> 16;
228d3d281f4Smiod 	plt_entry[3] = got_addr & 0xffff;
229d3d281f4Smiod }
230d3d281f4Smiod 
231d3d281f4Smiod /*
232d3d281f4Smiod  *	Relocate the Global Offset Table (GOT).
233d3d281f4Smiod  *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
234d3d281f4Smiod  *	otherwise the lazy binding plt operation is preserved.
235d3d281f4Smiod  */
236d3d281f4Smiod int
_dl_md_reloc_got(elf_object_t * object,int lazy)237d3d281f4Smiod _dl_md_reloc_got(elf_object_t *object, int lazy)
238d3d281f4Smiod {
239d3d281f4Smiod 	extern void _dl_bind_start(void);	/* XXX */
240d3d281f4Smiod 	int	fails = 0;
241d3d281f4Smiod 	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
2420b305a9bSmiod 	Elf_Addr plt_start, plt_end;
243d3d281f4Smiod 
244d3d281f4Smiod 	if (pltgot == NULL)
245e3b0f1d9Sguenther 		return 0;
246d3d281f4Smiod 
247d3d281f4Smiod 	pltgot[1] = (Elf_Addr)object;
248d3d281f4Smiod 	pltgot[2] = (Elf_Addr)_dl_bind_start;
249d3d281f4Smiod 
250d3d281f4Smiod 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
251e3b0f1d9Sguenther 		return 0;
252d3d281f4Smiod 
253d3d281f4Smiod 	if (!lazy) {
254d3d281f4Smiod 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
255d3d281f4Smiod 	} else {
256d3d281f4Smiod 		if (object->obj_base != 0) {
257d3d281f4Smiod 			int cnt;
258d3d281f4Smiod 			Elf_Addr *addr;
259d3d281f4Smiod 			Elf_RelA *rela;
260d3d281f4Smiod 
261d3d281f4Smiod 			cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
262d3d281f4Smiod 			rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
263d3d281f4Smiod 
264d3d281f4Smiod 			for (; cnt != 0; cnt--, rela++) {
265d3d281f4Smiod 				addr = (Elf_Addr *)(object->obj_base +
266d3d281f4Smiod 				    rela->r_offset);
267d3d281f4Smiod 				_dl_md_reloc_gotp_ent((Elf_Addr)addr,
268d3d281f4Smiod 				    object->obj_base + rela->r_addend,
269d3d281f4Smiod 				    *addr + object->obj_base);
270d3d281f4Smiod 			}
271d3d281f4Smiod 		}
272d3d281f4Smiod 	}
273d3d281f4Smiod 
2741a711874Smiod 	/*
27525205068Sguenther 	 * Force a cache sync here on the whole PLT if we updated it
27625205068Sguenther 	 * (and have the DT entries to find what we need to flush),
2771a711874Smiod 	 * otherwise I$ might have stale information.
2781a711874Smiod 	 */
27925205068Sguenther 	plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM];
28025205068Sguenther 	plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM];
28125205068Sguenther 	if ((!lazy || object->obj_base != 0) && plt_start != 0 &&
28225205068Sguenther 	    plt_end != 0) {
28325205068Sguenther 		size_t plt_size = plt_end - plt_start;
28425205068Sguenther 		if (plt_size != 0)
28525205068Sguenther 			_dl_cacheflush(plt_start + object->obj_base, plt_size);
286d3d281f4Smiod 	}
287d3d281f4Smiod 
288e3b0f1d9Sguenther 	return fails;
289d3d281f4Smiod }
290d3d281f4Smiod 
291d3d281f4Smiod Elf_Addr
_dl_bind(elf_object_t * object,int reloff)292d3d281f4Smiod _dl_bind(elf_object_t *object, int reloff)
293d3d281f4Smiod {
294d3d281f4Smiod 	Elf_RelA *rel;
295143e5accSguenther 	struct sym_res sr;
296143e5accSguenther 	const Elf_Sym *sym;
297d3d281f4Smiod 	const char *symn;
2987ad3219aSguenther 	uint64_t cookie = pcookie;
2997ad3219aSguenther 	struct {
3007ad3219aSguenther 		struct __kbind param;
3017ad3219aSguenther 		Elf_Addr newval;
3027ad3219aSguenther 	} buf;
303d3d281f4Smiod 
304d3d281f4Smiod 	rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
305d3d281f4Smiod 
306d3d281f4Smiod 	sym = object->dyn.symtab;
307d3d281f4Smiod 	sym += ELF_R_SYM(rel->r_info);
308d3d281f4Smiod 	symn = object->dyn.strtab + sym->st_name;
309d3d281f4Smiod 
310143e5accSguenther 	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
311143e5accSguenther 	    sym, object);
312143e5accSguenther 	if (sr.sym == NULL)
3133b50b772Sguenther 		_dl_die("lazy binding failed!");
314d3d281f4Smiod 
315143e5accSguenther 	buf.newval = sr.obj->obj_base + sr.sym->st_value;
316d3d281f4Smiod 
317143e5accSguenther 	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
318e3b0f1d9Sguenther 		return buf.newval;
319ae398163Smiod 
3207ad3219aSguenther 	buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
3217ad3219aSguenther 	buf.param.kb_size = sizeof(Elf_Addr);
3227ad3219aSguenther 
3237ad3219aSguenther 	/* directly code the syscall, so that it's actually inline here */
3247ad3219aSguenther 	{
3257ad3219aSguenther 		register long syscall_num __asm("r13") = SYS_kbind;
3267ad3219aSguenther 		register void *arg1 __asm("r2") = &buf;
3277ad3219aSguenther 		register long  arg2 __asm("r3") = sizeof(buf);
3287ad3219aSguenther 		register long  arg3 __asm("r4") = 0xffffffff & (cookie >> 32);
3297ad3219aSguenther 		register long  arg4 __asm("r5") = 0xffffffff &  cookie;
3307ad3219aSguenther 
3317ad3219aSguenther 		__asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0"
3327ad3219aSguenther 		    : "+r" (arg1), "+r" (arg2) : "r" (syscall_num),
333d031e57eSmiod 		    "r" (arg3), "r" (arg4) : "memory");
334d3d281f4Smiod 	}
335d3d281f4Smiod 
336e3b0f1d9Sguenther 	return buf.newval;
337d3d281f4Smiod }
338