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