xref: /openbsd-src/libexec/ld.so/m88k/rtld_machine.c (revision b722ba42570161220f25c5d789b5bec8a0166743)
1 /*	$OpenBSD: rtld_machine.c,v 1.31 2022/01/08 06:49:42 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/exec_elf.h>
48 #include <sys/syscall.h>
49 #include <sys/unistd.h>
50 
51 #include <machine/reloc.h>
52 
53 #include "util.h"
54 #include "resolve.h"
55 
56 int	_dl_cacheflush(unsigned long, size_t);
57 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
58 void	_dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr);
59 
60 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
61 
62 int
_dl_md_reloc(elf_object_t * object,int rel,int relasz)63 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
64 {
65 	int	i;
66 	int	numrela;
67 	int	relrela;
68 	int	fails = 0;
69 	Elf_Addr loff;
70 	Elf_RelA  *relas;
71 	Elf_Addr prev_value = 0, prev_ooff = 0;
72 	const Elf_Sym *prev_sym = NULL;
73 
74 	loff = object->obj_base;
75 	numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
76 	relrela = rel == DT_RELA ? object->relacount : 0;
77 
78 	relas = (Elf_RelA *)(object->Dyn.info[rel]);
79 
80 	if (relas == NULL)
81 		return 0;
82 
83 	if (relrela > numrela)
84 		_dl_die("relacount > numrel: %d > %d", relrela, numrela);
85 
86 	/* tight loop for leading RELATIVE relocs */
87 	for (i = 0; i < relrela; i++, relas++) {
88 		Elf_Addr *r_addr;
89 
90 		r_addr = (Elf_Addr *)(relas->r_offset + loff);
91 		*r_addr = relas->r_addend + loff;
92 	}
93 	for (; i < numrela; i++, relas++) {
94 		Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
95 		Elf_Addr addend, newval;
96 		const Elf_Sym *sym;
97 		const char *symn;
98 		int type;
99 
100 		type = ELF_R_TYPE(relas->r_info);
101 
102 		if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
103 			continue;
104 
105 		if (type == RELOC_NONE)
106 			continue;
107 
108 		sym = object->dyn.symtab;
109 		sym += ELF_R_SYM(relas->r_info);
110 		symn = object->dyn.strtab + sym->st_name;
111 
112 		if (type == RELOC_COPY) {
113 			/*
114 			 * we need to find a symbol, that is not in the current
115 			 * object, start looking at the beginning of the list,
116 			 * searching all objects but _not_ the current object,
117 			 * first one found wins.
118 			 */
119 			struct sym_res sr;
120 
121 			sr = _dl_find_symbol(symn,
122 			    SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
123 			    sym, object);
124 			if (sr.sym != NULL) {
125 				_dl_bcopy((void *)(sr.obj->obj_base +
126 				    sr.sym->st_value), r_addr, sym->st_size);
127 			} else
128 				fails++;
129 
130 			continue;
131 		}
132 
133 		if (ELF_R_SYM(relas->r_info) &&
134 		    !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
135 		    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
136 		    sym != prev_sym) {
137 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
138 			    ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
139 				prev_sym = sym;
140 				prev_value = 0;
141 				prev_ooff = object->obj_base;
142 			} else {
143 				struct sym_res sr;
144 
145 				sr = _dl_find_symbol(symn,
146 				    SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
147 				    ((type == RELOC_GOTP_ENT) ?
148 				    SYM_PLT : SYM_NOTPLT), sym, object);
149 
150 				if (sr.sym == NULL) {
151 					if (ELF_ST_BIND(sym->st_info) !=
152 					    STB_WEAK)
153 						fails++;
154 					continue;
155 				}
156 				prev_sym = sym;
157 				prev_value = sr.sym->st_value;
158 				prev_ooff = sr.obj->obj_base;
159 			}
160 		}
161 
162 		if (type == RELOC_GOTP_ENT) {
163 			_dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
164 			    relas->r_addend + loff,
165 			    prev_ooff + prev_value);
166 			continue;
167 		}
168 
169 		if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
170 		    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
171 		    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE))
172 			addend = relas->r_addend;
173 		else
174 			addend = prev_value + relas->r_addend;
175 
176 		switch (type) {
177 		case RELOC_16L:
178 			newval = prev_ooff + addend;
179 			*(unsigned short *)r_addr = newval & 0xffff;
180 			_dl_cacheflush((unsigned long)r_addr, 2);
181 			break;
182 		case RELOC_16H:
183 			newval = prev_ooff + addend;
184 			*(unsigned short *)r_addr = newval >> 16;
185 			_dl_cacheflush((unsigned long)r_addr, 2);
186 			break;
187 		case RELOC_DISP26:
188 			newval = prev_ooff + addend;
189 			newval -= (Elf_Addr)r_addr;
190 			if ((newval >> 28) != 0 && (newval >> 28) != 0x0f)
191 				_dl_die("%s: out of range DISP26"
192 				    " relocation to '%s' at %p\n",
193 				    object->load_name, symn, (void *)r_addr);
194 			*r_addr = (*r_addr & 0xfc000000) |
195 			    (((int32_t)newval >> 2) & 0x03ffffff);
196 			_dl_cacheflush((unsigned long)r_addr, 4);
197 			break;
198 		case RELOC_32:
199 			newval = prev_ooff + addend;
200 			*r_addr = newval;
201 			break;
202 		case RELOC_BBASED_32:
203 			newval = loff + addend;
204 			*r_addr = newval;
205 			break;
206 		default:
207 			_dl_die("%s: unsupported relocation '%s' %d at %p\n",
208 			    object->load_name, symn, type, (void *)r_addr);
209 		}
210 	}
211 
212 	return fails;
213 }
214 
215 /*
216  * GOTP_ENT relocations are special in that they define both a .got and a
217  * .plt relocation.
218  */
219 void
_dl_md_reloc_gotp_ent(Elf_Addr got_addr,Elf_Addr plt_addr,Elf_Addr val)220 _dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
221 {
222 	uint16_t *plt_entry = (uint16_t *)plt_addr;
223 
224 	/* .got update */
225 	*(Elf_Addr *)got_addr = val;
226 	/* .plt update */
227 	plt_entry[1] = got_addr >> 16;
228 	plt_entry[3] = got_addr & 0xffff;
229 }
230 
231 /*
232  *	Relocate the Global Offset Table (GOT).
233  *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
234  *	otherwise the lazy binding plt operation is preserved.
235  */
236 int
_dl_md_reloc_got(elf_object_t * object,int lazy)237 _dl_md_reloc_got(elf_object_t *object, int lazy)
238 {
239 	extern void _dl_bind_start(void);	/* XXX */
240 	int	fails = 0;
241 	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
242 	Elf_Addr plt_start, plt_end;
243 
244 	if (pltgot == NULL)
245 		return 0;
246 
247 	pltgot[1] = (Elf_Addr)object;
248 	pltgot[2] = (Elf_Addr)_dl_bind_start;
249 
250 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
251 		return 0;
252 
253 	if (!lazy) {
254 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
255 	} else {
256 		if (object->obj_base != 0) {
257 			int cnt;
258 			Elf_Addr *addr;
259 			Elf_RelA *rela;
260 
261 			cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
262 			rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
263 
264 			for (; cnt != 0; cnt--, rela++) {
265 				addr = (Elf_Addr *)(object->obj_base +
266 				    rela->r_offset);
267 				_dl_md_reloc_gotp_ent((Elf_Addr)addr,
268 				    object->obj_base + rela->r_addend,
269 				    *addr + object->obj_base);
270 			}
271 		}
272 	}
273 
274 	/*
275 	 * Force a cache sync here on the whole PLT if we updated it
276 	 * (and have the DT entries to find what we need to flush),
277 	 * otherwise I$ might have stale information.
278 	 */
279 	plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM];
280 	plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM];
281 	if ((!lazy || object->obj_base != 0) && plt_start != 0 &&
282 	    plt_end != 0) {
283 		size_t plt_size = plt_end - plt_start;
284 		if (plt_size != 0)
285 			_dl_cacheflush(plt_start + object->obj_base, plt_size);
286 	}
287 
288 	return fails;
289 }
290 
291 Elf_Addr
_dl_bind(elf_object_t * object,int reloff)292 _dl_bind(elf_object_t *object, int reloff)
293 {
294 	Elf_RelA *rel;
295 	struct sym_res sr;
296 	const Elf_Sym *sym;
297 	const char *symn;
298 	uint64_t cookie = pcookie;
299 	struct {
300 		struct __kbind param;
301 		Elf_Addr newval;
302 	} buf;
303 
304 	rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
305 
306 	sym = object->dyn.symtab;
307 	sym += ELF_R_SYM(rel->r_info);
308 	symn = object->dyn.strtab + sym->st_name;
309 
310 	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
311 	    sym, object);
312 	if (sr.sym == NULL)
313 		_dl_die("lazy binding failed!");
314 
315 	buf.newval = sr.obj->obj_base + sr.sym->st_value;
316 
317 	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
318 		return buf.newval;
319 
320 	buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
321 	buf.param.kb_size = sizeof(Elf_Addr);
322 
323 	/* directly code the syscall, so that it's actually inline here */
324 	{
325 		register long syscall_num __asm("r13") = SYS_kbind;
326 		register void *arg1 __asm("r2") = &buf;
327 		register long  arg2 __asm("r3") = sizeof(buf);
328 		register long  arg3 __asm("r4") = 0xffffffff & (cookie >> 32);
329 		register long  arg4 __asm("r5") = 0xffffffff &  cookie;
330 
331 		__asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0"
332 		    : "+r" (arg1), "+r" (arg2) : "r" (syscall_num),
333 		    "r" (arg3), "r" (arg4) : "memory");
334 	}
335 
336 	return buf.newval;
337 }
338