root/kern/syscall/loadelf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. load_segment
  2. load_elf

   1 /*
   2  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
   3  *      The President and Fellows of Harvard College.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the University nor the names of its contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 
  31 /*
  32  * Code to load an ELF-format executable into the current address space.
  33  *
  34  * It makes the following address space calls:
  35  *    - first, as_define_region once for each segment of the program;
  36  *    - then, as_prepare_load;
  37  *    - then it loads each chunk of the program;
  38  *    - finally, as_complete_load.
  39  *
  40  * This gives the VM code enough flexibility to deal with even grossly
  41  * mis-linked executables if that proves desirable. Under normal
  42  * circumstances, as_prepare_load and as_complete_load probably don't
  43  * need to do anything.
  44  *
  45  * If you wanted to support memory-mapped executables you would need
  46  * to rearrange this to map each segment.
  47  *
  48  * To support dynamically linked executables with shared libraries
  49  * you'd need to change this to load the "ELF interpreter" (dynamic
  50  * linker). And you'd have to write a dynamic linker...
  51  */
  52 
  53 #include <types.h>
  54 #include <kern/errno.h>
  55 #include <lib.h>
  56 #include <uio.h>
  57 #include <proc.h>
  58 #include <current.h>
  59 #include <addrspace.h>
  60 #include <vnode.h>
  61 #include <elf.h>
  62 
  63 /*
  64  * Load a segment at virtual address VADDR. The segment in memory
  65  * extends from VADDR up to (but not including) VADDR+MEMSIZE. The
  66  * segment on disk is located at file offset OFFSET and has length
  67  * FILESIZE.
  68  *
  69  * FILESIZE may be less than MEMSIZE; if so the remaining portion of
  70  * the in-memory segment should be zero-filled.
  71  *
  72  * Note that uiomove will catch it if someone tries to load an
  73  * executable whose load address is in kernel space. If you should
  74  * change this code to not use uiomove, be sure to check for this case
  75  * explicitly.
  76  */
  77 static
  78 int
  79 load_segment(struct addrspace *as, struct vnode *v,
  80              off_t offset, vaddr_t vaddr, 
  81              size_t memsize, size_t filesize,
  82              int is_executable)
  83 {
  84         struct iovec iov;
  85         struct uio u;
  86         int result;
  87 
  88         if (filesize > memsize) {
  89                 kprintf("ELF: warning: segment filesize > segment memsize\n");
  90                 filesize = memsize;
  91         }
  92 
  93         DEBUG(DB_EXEC, "ELF: Loading %lu bytes to 0x%lx\n", 
  94               (unsigned long) filesize, (unsigned long) vaddr);
  95 
  96         iov.iov_ubase = (userptr_t)vaddr;
  97         iov.iov_len = memsize;           // length of the memory space
  98         u.uio_iov = &iov;
  99         u.uio_iovcnt = 1;
 100         u.uio_resid = filesize;          // amount to read from the file
 101         u.uio_offset = offset;
 102         u.uio_segflg = is_executable ? UIO_USERISPACE : UIO_USERSPACE;
 103         u.uio_rw = UIO_READ;
 104         u.uio_space = as;
 105 
 106         result = VOP_READ(v, &u);
 107         if (result) {
 108                 return result;
 109         }
 110 
 111         if (u.uio_resid != 0) {
 112                 /* short read; problem with executable? */
 113                 kprintf("ELF: short read on segment - file truncated?\n");
 114                 return ENOEXEC;
 115         }
 116 
 117         /*
 118          * If memsize > filesize, the remaining space should be
 119          * zero-filled. There is no need to do this explicitly,
 120          * because the VM system should provide pages that do not
 121          * contain other processes' data, i.e., are already zeroed.
 122          *
 123          * During development of your VM system, it may have bugs that
 124          * cause it to (maybe only sometimes) not provide zero-filled
 125          * pages, which can cause user programs to fail in strange
 126          * ways. Explicitly zeroing program BSS may help identify such
 127          * bugs, so the following disabled code is provided as a
 128          * diagnostic tool. Note that it must be disabled again before
 129          * you submit your code for grading.
 130          */
 131 #if 0
 132         {
 133                 size_t fillamt;
 134 
 135                 fillamt = memsize - filesize;
 136                 if (fillamt > 0) {
 137                         DEBUG(DB_EXEC, "ELF: Zero-filling %lu more bytes\n", 
 138                               (unsigned long) fillamt);
 139                         u.uio_resid += fillamt;
 140                         result = uiomovezeros(fillamt, &u);
 141                 }
 142         }
 143 #endif
 144         
 145         return result;
 146 }
 147 
 148 /*
 149  * Load an ELF executable user program into the current address space.
 150  *
 151  * Returns the entry point (initial PC) for the program in ENTRYPOINT.
 152  */
 153 int
 154 load_elf(struct vnode *v, vaddr_t *entrypoint)
 155 {
 156         Elf_Ehdr eh;   /* Executable header */
 157         Elf_Phdr ph;   /* "Program header" = segment header */
 158         int result, i;
 159         struct iovec iov;
 160         struct uio ku;
 161         struct addrspace *as;
 162 
 163         as = curproc_getas();
 164 
 165         /*
 166          * Read the executable header from offset 0 in the file.
 167          */
 168 
 169         uio_kinit(&iov, &ku, &eh, sizeof(eh), 0, UIO_READ);
 170         result = VOP_READ(v, &ku);
 171         if (result) {
 172                 return result;
 173         }
 174 
 175         if (ku.uio_resid != 0) {
 176                 /* short read; problem with executable? */
 177                 kprintf("ELF: short read on header - file truncated?\n");
 178                 return ENOEXEC;
 179         }
 180 
 181         /*
 182          * Check to make sure it's a 32-bit ELF-version-1 executable
 183          * for our processor type. If it's not, we can't run it.
 184          *
 185          * Ignore EI_OSABI and EI_ABIVERSION - properly, we should
 186          * define our own, but that would require tinkering with the
 187          * linker to have it emit our magic numbers instead of the
 188          * default ones. (If the linker even supports these fields,
 189          * which were not in the original elf spec.)
 190          */
 191 
 192         if (eh.e_ident[EI_MAG0] != ELFMAG0 ||
 193             eh.e_ident[EI_MAG1] != ELFMAG1 ||
 194             eh.e_ident[EI_MAG2] != ELFMAG2 ||
 195             eh.e_ident[EI_MAG3] != ELFMAG3 ||
 196             eh.e_ident[EI_CLASS] != ELFCLASS32 ||
 197             eh.e_ident[EI_DATA] != ELFDATA2MSB ||
 198             eh.e_ident[EI_VERSION] != EV_CURRENT ||
 199             eh.e_version != EV_CURRENT ||
 200             eh.e_type!=ET_EXEC ||
 201             eh.e_machine!=EM_MACHINE) {
 202                 return ENOEXEC;
 203         }
 204 
 205         /*
 206          * Go through the list of segments and set up the address space.
 207          *
 208          * Ordinarily there will be one code segment, one read-only
 209          * data segment, and one data/bss segment, but there might
 210          * conceivably be more. You don't need to support such files
 211          * if it's unduly awkward to do so.
 212          *
 213          * Note that the expression eh.e_phoff + i*eh.e_phentsize is 
 214          * mandated by the ELF standard - we use sizeof(ph) to load,
 215          * because that's the structure we know, but the file on disk
 216          * might have a larger structure, so we must use e_phentsize
 217          * to find where the phdr starts.
 218          */
 219 
 220         for (i=0; i<eh.e_phnum; i++) {
 221                 off_t offset = eh.e_phoff + i*eh.e_phentsize;
 222                 uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);
 223 
 224                 result = VOP_READ(v, &ku);
 225                 if (result) {
 226                         return result;
 227                 }
 228 
 229                 if (ku.uio_resid != 0) {
 230                         /* short read; problem with executable? */
 231                         kprintf("ELF: short read on phdr - file truncated?\n");
 232                         return ENOEXEC;
 233                 }
 234 
 235                 switch (ph.p_type) {
 236                     case PT_NULL: /* skip */ continue;
 237                     case PT_PHDR: /* skip */ continue;
 238                     case PT_MIPS_REGINFO: /* skip */ continue;
 239                     case PT_LOAD: break;
 240                     default:
 241                         kprintf("loadelf: unknown segment type %d\n", 
 242                                 ph.p_type);
 243                         return ENOEXEC;
 244                 }
 245 
 246                 result = as_define_region(as,
 247                                           ph.p_vaddr, ph.p_memsz,
 248                                           ph.p_flags & PF_R,
 249                                           ph.p_flags & PF_W,
 250                                           ph.p_flags & PF_X);
 251                 if (result) {
 252                         return result;
 253                 }
 254         }
 255 
 256         result = as_prepare_load(as);
 257         if (result) {
 258                 return result;
 259         }
 260 
 261         /*
 262          * Now actually load each segment.
 263          */
 264 
 265         for (i=0; i<eh.e_phnum; i++) {
 266                 off_t offset = eh.e_phoff + i*eh.e_phentsize;
 267                 uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);
 268 
 269                 result = VOP_READ(v, &ku);
 270                 if (result) {
 271                         return result;
 272                 }
 273 
 274                 if (ku.uio_resid != 0) {
 275                         /* short read; problem with executable? */
 276                         kprintf("ELF: short read on phdr - file truncated?\n");
 277                         return ENOEXEC;
 278                 }
 279 
 280                 switch (ph.p_type) {
 281                     case PT_NULL: /* skip */ continue;
 282                     case PT_PHDR: /* skip */ continue;
 283                     case PT_MIPS_REGINFO: /* skip */ continue;
 284                     case PT_LOAD: break;
 285                     default:
 286                         kprintf("loadelf: unknown segment type %d\n", 
 287                                 ph.p_type);
 288                         return ENOEXEC;
 289                 }
 290 
 291                 result = load_segment(as, v, ph.p_offset, ph.p_vaddr, 
 292                                       ph.p_memsz, ph.p_filesz,
 293                                       ph.p_flags & PF_X);
 294                 if (result) {
 295                         return result;
 296                 }
 297         }
 298 
 299         result = as_complete_load(as);
 300         if (result) {
 301                 return result;
 302         }
 303 
 304         *entrypoint = eh.e_entry;
 305 
 306         return 0;
 307 }

/* [<][>][^][v][top][bottom][index][help] */