The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mips/mips/elf_machdep.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.