123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- /*
- * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
- * The President and Fellows of Harvard College.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- /*
- * Code to load an ELF-format executable into the current address space.
- *
- * It makes the following address space calls:
- * - first, as_define_region once for each segment of the program;
- * - then, as_prepare_load;
- * - then it loads each chunk of the program;
- * - finally, as_complete_load.
- *
- * This gives the VM code enough flexibility to deal with even grossly
- * mis-linked executables if that proves desirable. Under normal
- * circumstances, as_prepare_load and as_complete_load probably don't
- * need to do anything.
- *
- * If you wanted to support memory-mapped executables you would need
- * to rearrange this to map each segment.
- *
- * To support dynamically linked executables with shared libraries
- * you'd need to change this to load the "ELF interpreter" (dynamic
- * linker). And you'd have to write a dynamic linker...
- */
- #include <types.h>
- #include <kern/errno.h>
- #include <lib.h>
- #include <uio.h>
- #include <proc.h>
- #include <current.h>
- #include <addrspace.h>
- #include <vnode.h>
- #include <elf.h>
- /*
- * Load a segment at virtual address VADDR. The segment in memory
- * extends from VADDR up to (but not including) VADDR+MEMSIZE. The
- * segment on disk is located at file offset OFFSET and has length
- * FILESIZE.
- *
- * FILESIZE may be less than MEMSIZE; if so the remaining portion of
- * the in-memory segment should be zero-filled.
- *
- * Note that uiomove will catch it if someone tries to load an
- * executable whose load address is in kernel space. If you should
- * change this code to not use uiomove, be sure to check for this case
- * explicitly.
- */
- static
- int
- load_segment(struct addrspace *as, struct vnode *v,
- off_t offset, vaddr_t vaddr,
- size_t memsize, size_t filesize,
- int is_executable)
- {
- struct iovec iov;
- struct uio u;
- int result;
- if (filesize > memsize) {
- kprintf("ELF: warning: segment filesize > segment memsize\n");
- filesize = memsize;
- }
- DEBUG(DB_EXEC, "ELF: Loading %lu bytes to 0x%lx\n",
- (unsigned long) filesize, (unsigned long) vaddr);
- iov.iov_ubase = (userptr_t)vaddr;
- iov.iov_len = memsize; // length of the memory space
- u.uio_iov = &iov;
- u.uio_iovcnt = 1;
- u.uio_resid = filesize; // amount to read from the file
- u.uio_offset = offset;
- u.uio_segflg = is_executable ? UIO_USERISPACE : UIO_USERSPACE;
- u.uio_rw = UIO_READ;
- u.uio_space = as;
- result = VOP_READ(v, &u);
- if (result) {
- return result;
- }
- if (u.uio_resid != 0) {
- /* short read; problem with executable? */
- kprintf("ELF: short read on segment - file truncated?\n");
- return ENOEXEC;
- }
- /*
- * If memsize > filesize, the remaining space should be
- * zero-filled. There is no need to do this explicitly,
- * because the VM system should provide pages that do not
- * contain other processes' data, i.e., are already zeroed.
- *
- * During development of your VM system, it may have bugs that
- * cause it to (maybe only sometimes) not provide zero-filled
- * pages, which can cause user programs to fail in strange
- * ways. Explicitly zeroing program BSS may help identify such
- * bugs, so the following disabled code is provided as a
- * diagnostic tool. Note that it must be disabled again before
- * you submit your code for grading.
- */
- #if 0
- {
- size_t fillamt;
- fillamt = memsize - filesize;
- if (fillamt > 0) {
- DEBUG(DB_EXEC, "ELF: Zero-filling %lu more bytes\n",
- (unsigned long) fillamt);
- u.uio_resid += fillamt;
- result = uiomovezeros(fillamt, &u);
- }
- }
- #endif
-
- return result;
- }
- /*
- * Load an ELF executable user program into the current address space.
- *
- * Returns the entry point (initial PC) for the program in ENTRYPOINT.
- */
- int
- load_elf(struct vnode *v, vaddr_t *entrypoint)
- {
- Elf_Ehdr eh; /* Executable header */
- Elf_Phdr ph; /* "Program header" = segment header */
- int result, i;
- struct iovec iov;
- struct uio ku;
- struct addrspace *as;
- as = curproc_getas();
- /*
- * Read the executable header from offset 0 in the file.
- */
- uio_kinit(&iov, &ku, &eh, sizeof(eh), 0, UIO_READ);
- result = VOP_READ(v, &ku);
- if (result) {
- return result;
- }
- if (ku.uio_resid != 0) {
- /* short read; problem with executable? */
- kprintf("ELF: short read on header - file truncated?\n");
- return ENOEXEC;
- }
- /*
- * Check to make sure it's a 32-bit ELF-version-1 executable
- * for our processor type. If it's not, we can't run it.
- *
- * Ignore EI_OSABI and EI_ABIVERSION - properly, we should
- * define our own, but that would require tinkering with the
- * linker to have it emit our magic numbers instead of the
- * default ones. (If the linker even supports these fields,
- * which were not in the original elf spec.)
- */
- if (eh.e_ident[EI_MAG0] != ELFMAG0 ||
- eh.e_ident[EI_MAG1] != ELFMAG1 ||
- eh.e_ident[EI_MAG2] != ELFMAG2 ||
- eh.e_ident[EI_MAG3] != ELFMAG3 ||
- eh.e_ident[EI_CLASS] != ELFCLASS32 ||
- eh.e_ident[EI_DATA] != ELFDATA2MSB ||
- eh.e_ident[EI_VERSION] != EV_CURRENT ||
- eh.e_version != EV_CURRENT ||
- eh.e_type!=ET_EXEC ||
- eh.e_machine!=EM_MACHINE) {
- return ENOEXEC;
- }
- /*
- * Go through the list of segments and set up the address space.
- *
- * Ordinarily there will be one code segment, one read-only
- * data segment, and one data/bss segment, but there might
- * conceivably be more. You don't need to support such files
- * if it's unduly awkward to do so.
- *
- * Note that the expression eh.e_phoff + i*eh.e_phentsize is
- * mandated by the ELF standard - we use sizeof(ph) to load,
- * because that's the structure we know, but the file on disk
- * might have a larger structure, so we must use e_phentsize
- * to find where the phdr starts.
- */
- for (i=0; i<eh.e_phnum; i++) {
- off_t offset = eh.e_phoff + i*eh.e_phentsize;
- uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);
- result = VOP_READ(v, &ku);
- if (result) {
- return result;
- }
- if (ku.uio_resid != 0) {
- /* short read; problem with executable? */
- kprintf("ELF: short read on phdr - file truncated?\n");
- return ENOEXEC;
- }
- switch (ph.p_type) {
- case PT_NULL: /* skip */ continue;
- case PT_PHDR: /* skip */ continue;
- case PT_MIPS_REGINFO: /* skip */ continue;
- case PT_LOAD: break;
- default:
- kprintf("loadelf: unknown segment type %d\n",
- ph.p_type);
- return ENOEXEC;
- }
- result = as_define_region(as,
- ph.p_vaddr, ph.p_memsz,
- ph.p_flags & PF_R,
- ph.p_flags & PF_W,
- ph.p_flags & PF_X);
- if (result) {
- return result;
- }
- }
- result = as_prepare_load(as);
- if (result) {
- return result;
- }
- /*
- * Now actually load each segment.
- */
- for (i=0; i<eh.e_phnum; i++) {
- off_t offset = eh.e_phoff + i*eh.e_phentsize;
- uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);
- result = VOP_READ(v, &ku);
- if (result) {
- return result;
- }
- if (ku.uio_resid != 0) {
- /* short read; problem with executable? */
- kprintf("ELF: short read on phdr - file truncated?\n");
- return ENOEXEC;
- }
- switch (ph.p_type) {
- case PT_NULL: /* skip */ continue;
- case PT_PHDR: /* skip */ continue;
- case PT_MIPS_REGINFO: /* skip */ continue;
- case PT_LOAD: break;
- default:
- kprintf("loadelf: unknown segment type %d\n",
- ph.p_type);
- return ENOEXEC;
- }
- result = load_segment(as, v, ph.p_offset, ph.p_vaddr,
- ph.p_memsz, ph.p_filesz,
- ph.p_flags & PF_X);
- if (result) {
- return result;
- }
- }
- result = as_complete_load(as);
- if (result) {
- return result;
- }
- *entrypoint = eh.e_entry;
- as->loading = false;
- as_activate();
- return 0;
- }
|