xref: /freebsd-13.1/sys/mips/mips/elf_machdep.c (revision 39f259b1)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 1996-1998 John D. Polstra.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  *	from: src/sys/i386/i386/elf_machdep.c,v 1.20 2004/08/11 02:35:05 marcel
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/exec.h>
37 #include <sys/imgact.h>
38 #include <sys/linker.h>
39 #include <sys/sysent.h>
40 #include <sys/imgact_elf.h>
41 #include <sys/proc.h>
42 #include <sys/syscall.h>
43 #include <sys/signalvar.h>
44 #include <sys/vnode.h>
45 
46 #include <vm/vm.h>
47 #include <vm/pmap.h>
48 #include <vm/vm_param.h>
49 
50 #include <machine/elf.h>
51 #include <machine/md_var.h>
52 #include <machine/cache.h>
53 
54 static struct sysentvec elf_freebsd_sysvec = {
55 	.sv_size	= SYS_MAXSYSCALL,
56 	.sv_table	= sysent,
57 	.sv_transtrap	= NULL,
58 	.sv_fixup	= __elfN(freebsd_fixup),
59 	.sv_sendsig	= sendsig,
60 	.sv_sigcode	= sigcode,
61 	.sv_szsigcode	= &szsigcode,
62 #ifdef __mips_n64
63 	.sv_name	= "FreeBSD ELF64",
64 #else
65 	.sv_name	= "FreeBSD ELF32",
66 #endif
67 	.sv_coredump	= __elfN(coredump),
68 	.sv_imgact_try	= NULL,
69 	.sv_minsigstksz	= MINSIGSTKSZ,
70 	.sv_minuser	= VM_MIN_ADDRESS,
71 	.sv_maxuser	= VM_MAXUSER_ADDRESS,
72 	.sv_usrstack	= USRSTACK,
73 	.sv_psstrings	= PS_STRINGS,
74 	.sv_stackprot	= VM_PROT_ALL,
75 	.sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs),
76 	.sv_copyout_strings = exec_copyout_strings,
77 	.sv_setregs	= exec_setregs,
78 	.sv_fixlimit	= NULL,
79 	.sv_maxssiz	= NULL,
80 	.sv_flags	= SV_ABI_FREEBSD | SV_ASLR | SV_RNG_SEED_VER |
81 #ifdef __mips_n64
82 	    SV_LP64,
83 #else
84 	    SV_ILP32,
85 #endif
86 	.sv_set_syscall_retval = cpu_set_syscall_retval,
87 	.sv_fetch_syscall_args = cpu_fetch_syscall_args,
88 	.sv_syscallnames = syscallnames,
89 	.sv_schedtail	= NULL,
90 	.sv_thread_detach = NULL,
91 	.sv_trap	= NULL,
92 	.sv_onexec_old	= exec_onexec_old,
93 	.sv_onexit	= exit_onexit,
94 };
95 
96 static __ElfN(Brandinfo) freebsd_brand_info = {
97 	.brand		= ELFOSABI_FREEBSD,
98 	.machine	= EM_MIPS,
99 	.compat_3_brand	= "FreeBSD",
100 	.emul_path	= NULL,
101 	.interp_path	= "/libexec/ld-elf.so.1",
102 	.sysvec		= &elf_freebsd_sysvec,
103 	.interp_newpath	= NULL,
104 	.brand_note	= &__elfN(freebsd_brandnote),
105 	.flags		= BI_CAN_EXEC_DYN | BI_BRAND_NOTE
106 };
107 
108 SYSINIT(elf, SI_SUB_EXEC, SI_ORDER_ANY,
109     (sysinit_cfunc_t) __elfN(insert_brand_entry),
110     &freebsd_brand_info);
111 
112 void
__elfN(dump_thread)113 __elfN(dump_thread)(struct thread *td __unused, void *dst __unused,
114     size_t *off __unused)
115 {
116 }
117 
118 /*
119  * The following MIPS relocation code for tracking multiple
120  * consecutive HI32/LO32 entries is because of the following:
121  *
122  * https://dmz-portal.mips.com/wiki/MIPS_relocation_types
123  *
124  * ===
125  *
126  * + R_MIPS_HI16
127  *
128  * An R_MIPS_HI16 must be followed eventually by an associated R_MIPS_LO16
129  * relocation record in the same SHT_REL section. The contents of the two
130  * fields to be relocated are combined to form a full 32-bit addend AHL.
131  * An R_MIPS_LO16 entry which does not immediately follow a R_MIPS_HI16 is
132  * combined with the most recent one encountered, i.e. multiple R_MIPS_LO16
133  * entries may be associated with a single R_MIPS_HI16. Use of these
134  * relocation types in a SHT_REL section is discouraged and may be
135  * forbidden to avoid this complication.
136  *
137  * A GNU extension allows multiple R_MIPS_HI16 records to share the same
138  * R_MIPS_LO16 relocation record(s). The association works like this within
139  * a single relocation section:
140  *
141  * + From the beginning of the section moving to the end of the section,
142  *   until R_MIPS_LO16 is not found each found R_MIPS_HI16 relocation will
143  *   be associated with the first R_MIPS_LO16.
144  *
145  * + Until another R_MIPS_HI16 record is found all found R_MIPS_LO16
146  *   relocations found are associated with the last R_MIPS_HI16.
147  *
148  * ===
149  *
150  * This is so gcc can do dead code detection/removal without having to
151  * generate HI/LO pairs even if one of them would be deleted.
152  *
153  * So, the summary is:
154  *
155  * + A HI16 entry must occur before any LO16 entries;
156  * + Multiple consecutive HI16 RELA entries need to be buffered until the
157  *   first LO16 RELA entry occurs - and then all HI16 RELA relocations use
158  *   the offset in the LOW16 RELA for calculating their offsets;
159  * + The last HI16 RELA entry before a LO16 RELA entry is used (the AHL)
160  *   for the first subsequent LO16 calculation;
161  * + If multiple consecutive LO16 RELA entries occur, only the first
162  *   LO16 RELA entry triggers an update of buffered HI16 RELA entries;
163  *   any subsequent LO16 RELA entry before another HI16 RELA entry will
164  *   not cause any further updates to the HI16 RELA entries.
165  *
166  * Additionally, flush out any outstanding HI16 entries that don't have
167  * a LO16 entry in case some garbage entries are left in the file.
168  */
169 
170 struct mips_tmp_reloc;
171 struct mips_tmp_reloc {
172 	struct mips_tmp_reloc *next;
173 
174 	Elf_Addr ahl;
175 	Elf32_Addr *where_hi16;
176 };
177 
178 static struct mips_tmp_reloc *ml = NULL;
179 
180 /*
181  * Add a temporary relocation (ie, a HI16 reloc type.)
182  */
183 static int
mips_tmp_reloc_add(Elf_Addr ahl,Elf32_Addr * where_hi16)184 mips_tmp_reloc_add(Elf_Addr ahl, Elf32_Addr *where_hi16)
185 {
186 	struct mips_tmp_reloc *r;
187 
188 	r = malloc(sizeof(struct mips_tmp_reloc), M_TEMP, M_NOWAIT);
189 	if (r == NULL) {
190 		printf("%s: failed to malloc\n", __func__);
191 		return (0);
192 	}
193 
194 	r->ahl = ahl;
195 	r->where_hi16 = where_hi16;
196 	r->next = ml;
197 	ml = r;
198 
199 	return (1);
200 }
201 
202 /*
203  * Flush the temporary relocation list.
204  *
205  * This should be done after a file is completely loaded
206  * so no stale relocations exist to confuse the next
207  * load.
208  */
209 static void
mips_tmp_reloc_flush(void)210 mips_tmp_reloc_flush(void)
211 {
212 	struct mips_tmp_reloc *r, *rn;
213 
214 	r = ml;
215 	ml = NULL;
216 	while (r != NULL) {
217 		rn = r->next;
218 		free(r, M_TEMP);
219 		r = rn;
220 	}
221 }
222 
223 /*
224  * Get an entry from the reloc list; or NULL if we've run out.
225  */
226 static struct mips_tmp_reloc *
mips_tmp_reloc_get(void)227 mips_tmp_reloc_get(void)
228 {
229 	struct mips_tmp_reloc *r;
230 
231 	r = ml;
232 	if (r == NULL)
233 		return (NULL);
234 	ml = ml->next;
235 	return (r);
236 }
237 
238 /*
239  * Free a relocation entry.
240  */
241 static void
mips_tmp_reloc_free(struct mips_tmp_reloc * r)242 mips_tmp_reloc_free(struct mips_tmp_reloc *r)
243 {
244 
245 	free(r, M_TEMP);
246 }
247 
248 bool
elf_is_ifunc_reloc(Elf_Size r_info __unused)249 elf_is_ifunc_reloc(Elf_Size r_info __unused)
250 {
251 
252 	return (false);
253 }
254 
255 /* Process one elf relocation with addend. */
256 static int
elf_reloc_internal(linker_file_t lf,Elf_Addr relocbase,const void * data,int type,int local,elf_lookup_fn lookup)257 elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
258     int type, int local, elf_lookup_fn lookup)
259 {
260 	Elf32_Addr *where = (Elf32_Addr *)NULL;
261 	Elf_Addr addr;
262 	Elf_Addr addend = (Elf_Addr)0;
263 	Elf_Word rtype = (Elf_Word)0, symidx;
264 	struct mips_tmp_reloc *r;
265 	const Elf_Rel *rel = NULL;
266 	const Elf_Rela *rela = NULL;
267 	int error;
268 
269 	/* Store the last seen ahl from a HI16 for LO16 processing */
270 	static Elf_Addr last_ahl;
271 
272 	switch (type) {
273 	case ELF_RELOC_REL:
274 		rel = (const Elf_Rel *)data;
275 		where = (Elf32_Addr *) (relocbase + rel->r_offset);
276 		rtype = ELF_R_TYPE(rel->r_info);
277 		symidx = ELF_R_SYM(rel->r_info);
278 		switch (rtype) {
279 		case R_MIPS_64:
280 			addend = *(Elf64_Addr *)where;
281 			break;
282 		default:
283 			addend = *where;
284 			break;
285 		}
286 
287 		break;
288 	case ELF_RELOC_RELA:
289 		rela = (const Elf_Rela *)data;
290 		where = (Elf32_Addr *) (relocbase + rela->r_offset);
291 		addend = rela->r_addend;
292 		rtype = ELF_R_TYPE(rela->r_info);
293 		symidx = ELF_R_SYM(rela->r_info);
294 		break;
295 	default:
296 		panic("unknown reloc type %d\n", type);
297 	}
298 
299 	switch (rtype) {
300 	case R_MIPS_NONE:	/* none */
301 		break;
302 
303 	case R_MIPS_32:		/* S + A */
304 		error = lookup(lf, symidx, 1, &addr);
305 		if (error != 0)
306 			return (-1);
307 		addr += addend;
308 		if (*where != addr)
309 			*where = (Elf32_Addr)addr;
310 		break;
311 
312 	case R_MIPS_26:		/* ((A << 2) | (P & 0xf0000000) + S) >> 2 */
313 		error = lookup(lf, symidx, 1, &addr);
314 		if (error != 0)
315 			return (-1);
316 
317 		addend &= 0x03ffffff;
318 		/*
319 		 * Addendum for .rela R_MIPS_26 is not shifted right
320 		 */
321 		if (rela == NULL)
322 			addend <<= 2;
323 
324 		addr += ((Elf_Addr)where & 0xf0000000) | addend;
325 		addr >>= 2;
326 
327 		*where &= ~0x03ffffff;
328 		*where |= addr & 0x03ffffff;
329 		break;
330 
331 	case R_MIPS_64:		/* S + A */
332 		error = lookup(lf, symidx, 1, &addr);
333 		if (error != 0)
334 			return (-1);
335 		addr += addend;
336 		if (*(Elf64_Addr*)where != addr)
337 			*(Elf64_Addr*)where = addr;
338 		break;
339 
340 	/*
341 	 * Handle the two GNU extension cases:
342 	 *
343 	 * + Multiple HI16s followed by a LO16, and
344 	 * + A HI16 followed by multiple LO16s.
345 	 *
346 	 * The former is tricky - the HI16 relocations need
347 	 * to be buffered until a LO16 occurs, at which point
348 	 * each HI16 is replayed against the LO16 relocation entry
349 	 * (with the relevant overflow information.)
350 	 *
351 	 * The latter should be easy to handle - when the
352 	 * first LO16 is seen, write out and flush the
353 	 * HI16 buffer.  Any subsequent LO16 entries will
354 	 * find a blank relocation buffer.
355 	 *
356 	 */
357 
358 	case R_MIPS_HI16:	/* ((AHL + S) - ((short)(AHL + S)) >> 16 */
359 		if (rela != NULL) {
360 			error = lookup(lf, symidx, 1, &addr);
361 			if (error != 0)
362 				return (-1);
363 			addr += addend;
364 			*where &= 0xffff0000;
365 			*where |= ((((long long) addr + 0x8000LL) >> 16) & 0xffff);
366 		} else {
367 			/*
368 			 * Add a temporary relocation to the list;
369 			 * will pop it off / free the list when
370 			 * we've found a suitable HI16.
371 			 */
372 			if (mips_tmp_reloc_add(addend << 16, where) == 0)
373 				return (-1);
374 			/*
375 			 * Track the last seen HI16 AHL for use by
376 			 * the first LO16 AHL calculation.
377 			 *
378 			 * The assumption is any intermediary deleted
379 			 * LO16's were optimised out, so the last
380 			 * HI16 before the LO16 is the "true" relocation
381 			 * entry to use for that LO16 write.
382 			 */
383 			last_ahl = addend << 16;
384 		}
385 		break;
386 
387 	case R_MIPS_LO16:	/* AHL + S */
388 		if (rela != NULL) {
389 			error = lookup(lf, symidx, 1, &addr);
390 			if (error != 0)
391 				return (-1);
392 			addr += addend;
393 			*where &= 0xffff0000;
394 			*where |= addr & 0xffff;
395 		} else {
396 			Elf_Addr tmp_ahl;
397 			Elf_Addr tmp_addend;
398 
399 			tmp_ahl = last_ahl + (int16_t) addend;
400 			error = lookup(lf, symidx, 1, &addr);
401 			if (error != 0)
402 				return (-1);
403 
404 			tmp_addend = addend & 0xffff0000;
405 
406 			/* Use the last seen ahl for calculating addend */
407 			tmp_addend |= (uint16_t)(tmp_ahl + addr);
408 			*where = tmp_addend;
409 
410 			/*
411 			 * This logic implements the "we saw multiple HI16
412 			 * before a LO16" assignment /and/ "we saw multiple
413 			 * LO16s".
414 			 *
415 			 * Multiple LO16s will be handled as a blank
416 			 * relocation list.
417 			 *
418 			 * Multple HI16's are iterated over here.
419 			 */
420 			while ((r = mips_tmp_reloc_get()) != NULL) {
421 				Elf_Addr rahl;
422 
423 				/*
424 				 * We have the ahl from the HI16 entry, so
425 				 * offset it by the 16 bits of the low ahl.
426 				 */
427 				rahl = r->ahl;
428 				rahl += (int16_t) addend;
429 
430 				tmp_addend = *(r->where_hi16);
431 				tmp_addend &= 0xffff0000;
432 				tmp_addend |= ((rahl + addr) -
433 				    (int16_t)(rahl + addr)) >> 16;
434 				*(r->where_hi16) = tmp_addend;
435 				mips_tmp_reloc_free(r);
436 			}
437 		}
438 
439 		break;
440 
441 	case R_MIPS_HIGHER:	/* %higher(A+S) */
442 		error = lookup(lf, symidx, 1, &addr);
443 		if (error != 0)
444 			return (-1);
445 		addr += addend;
446 		*where &= 0xffff0000;
447 		*where |= (((long long)addr + 0x80008000LL) >> 32) & 0xffff;
448 		break;
449 
450 	case R_MIPS_HIGHEST:	/* %highest(A+S) */
451 		error = lookup(lf, symidx, 1, &addr);
452 		if (error != 0)
453 			return (-1);
454 		addr += addend;
455 		*where &= 0xffff0000;
456 		*where |= (((long long)addr + 0x800080008000LL) >> 48) & 0xffff;
457 		break;
458 
459 	default:
460 		printf("kldload: unexpected relocation type %d, "
461 		    "symbol index %d\n", rtype, symidx);
462 		return (-1);
463 	}
464 
465 	return (0);
466 }
467 
468 int
elf_reloc(linker_file_t lf,Elf_Addr relocbase,const void * data,int type,elf_lookup_fn lookup)469 elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
470     elf_lookup_fn lookup)
471 {
472 
473 	return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup));
474 }
475 
476 int
elf_reloc_local(linker_file_t lf,Elf_Addr relocbase,const void * data,int type,elf_lookup_fn lookup)477 elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data,
478     int type, elf_lookup_fn lookup)
479 {
480 
481 	return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup));
482 }
483 
484 int
elf_cpu_load_file(linker_file_t lf __unused)485 elf_cpu_load_file(linker_file_t lf __unused)
486 {
487 
488 	/*
489 	 * Sync the I and D caches to make sure our relocations are visible.
490 	 */
491 	mips_icache_sync_all();
492 
493 	/* Flush outstanding relocations */
494 	mips_tmp_reloc_flush();
495 
496 	return (0);
497 }
498 
499 int
elf_cpu_unload_file(linker_file_t lf __unused)500 elf_cpu_unload_file(linker_file_t lf __unused)
501 {
502 
503 	return (0);
504 }
505 
506 int
elf_cpu_parse_dynamic(caddr_t loadbase __unused,Elf_Dyn * dynamic __unused)507 elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused)
508 {
509 
510 	return (0);
511 }
512