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
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
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
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 *
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
242 mips_tmp_reloc_free(struct mips_tmp_reloc *r)
243 {
244
245 free(r, M_TEMP);
246 }
247
248 bool
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
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
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
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
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
500 elf_cpu_unload_file(linker_file_t lf __unused)
501 {
502
503 return (0);
504 }
505
506 int
507 elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused)
508 {
509
510 return (0);
511 }
Cache object: a720b7bffd80e1875dea822353334cd2
|