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