]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/mips/elf_machdep.c
Update Subversion to 1.14.0 LTS. See contrib/subversion/CHANGES for a
[FreeBSD/FreeBSD.git] / sys / mips / mips / elf_machdep.c
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_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
95 static __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
107 SYSINIT(elf, SI_SUB_EXEC, SI_ORDER_ANY,
108     (sysinit_cfunc_t) __elfN(insert_brand_entry),
109     &freebsd_brand_info);
110
111 void
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
169 struct mips_tmp_reloc;
170 struct mips_tmp_reloc {
171         struct mips_tmp_reloc *next;
172
173         Elf_Addr ahl;
174         Elf32_Addr *where_hi16;
175 };
176
177 static struct mips_tmp_reloc *ml = NULL;
178
179 /*
180  * Add a temporary relocation (ie, a HI16 reloc type.)
181  */
182 static int
183 mips_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  */
208 static void
209 mips_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  */
225 static struct mips_tmp_reloc *
226 mips_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  */
240 static void
241 mips_tmp_reloc_free(struct mips_tmp_reloc *r)
242 {
243
244         free(r, M_TEMP);
245 }
246
247 bool
248 elf_is_ifunc_reloc(Elf_Size r_info __unused)
249 {
250
251         return (false);
252 }
253
254 /* Process one elf relocation with addend. */
255 static int
256 elf_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
467 int
468 elf_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
475 int
476 elf_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
483 int
484 elf_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
498 int
499 elf_cpu_unload_file(linker_file_t lf __unused)
500 {
501
502         return (0);
503 }
504
505 int
506 elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused)
507 {
508
509         return (0);
510 }