/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/282930/*31* Code to load an ELF-format executable into the current address space.32*33* It makes the following address space calls:34* - first, as_define_region once for each segment of the program;35* - then, as_prepare_load;36* - then it loads each chunk of the program;37* - finally, as_complete_load.38*39* This gives the VM code enough flexibility to deal with even grossly40* mis-linked executables if that proves desirable. Under normal41* circumstances, as_prepare_load and as_complete_load probably don't42* need to do anything.43*44* If you wanted to support memory-mapped executables you would need45* to rearrange this to map each segment.46*47* To support dynamically linked executables with shared libraries48* you'd need to change this to load the "ELF interpreter" (dynamic49* linker). And you'd have to write a dynamic linker...50*/5152#include <types.h>53#include <kern/errno.h>54#include <lib.h>55#include <uio.h>56#include <thread.h>57#include <current.h>58#include <addrspace.h>59#include <vnode.h>60#include <elf.h>6162/*63* Load a segment at virtual address VADDR. The segment in memory64* extends from VADDR up to (but not including) VADDR+MEMSIZE. The65* segment on disk is located at file offset OFFSET and has length66* FILESIZE.67*68* FILESIZE may be less than MEMSIZE; if so the remaining portion of69* the in-memory segment should be zero-filled.70*71* Note that uiomove will catch it if someone tries to load an72* executable whose load address is in kernel space. If you should73* change this code to not use uiomove, be sure to check for this case74* explicitly.75*/76static77int78load_segment(struct vnode *v, off_t offset, vaddr_t vaddr,79size_t memsize, size_t filesize,80int is_executable)81{82struct iovec iov;83struct uio u;84int result;8586if (filesize > memsize) {87kprintf("ELF: warning: segment filesize > segment memsize\n");88filesize = memsize;89}9091DEBUG(DB_EXEC, "ELF: Loading %lu bytes to 0x%lx\n",92(unsigned long) filesize, (unsigned long) vaddr);9394iov.iov_ubase = (userptr_t)vaddr;95iov.iov_len = memsize; // length of the memory space96u.uio_iov = &iov;97u.uio_iovcnt = 1;98u.uio_resid = filesize; // amount to read from the file99u.uio_offset = offset;100u.uio_segflg = is_executable ? UIO_USERISPACE : UIO_USERSPACE;101u.uio_rw = UIO_READ;102u.uio_space = curthread->t_addrspace;103104result = VOP_READ(v, &u);105if (result) {106return result;107}108109if (u.uio_resid != 0) {110/* short read; problem with executable? */111kprintf("ELF: short read on segment - file truncated?\n");112return ENOEXEC;113}114115/*116* If memsize > filesize, the remaining space should be117* zero-filled. There is no need to do this explicitly,118* because the VM system should provide pages that do not119* contain other processes' data, i.e., are already zeroed.120*121* During development of your VM system, it may have bugs that122* cause it to (maybe only sometimes) not provide zero-filled123* pages, which can cause user programs to fail in strange124* ways. Explicitly zeroing program BSS may help identify such125* bugs, so the following disabled code is provided as a126* diagnostic tool. Note that it must be disabled again before127* you submit your code for grading.128*/129#if 0130{131size_t fillamt;132133fillamt = memsize - filesize;134if (fillamt > 0) {135DEBUG(DB_EXEC, "ELF: Zero-filling %lu more bytes\n",136(unsigned long) fillamt);137u.uio_resid += fillamt;138result = uiomovezeros(fillamt, &u);139}140}141#endif142143return result;144}145146/*147* Load an ELF executable user program into the current address space.148*149* Returns the entry point (initial PC) for the program in ENTRYPOINT.150*/151int152load_elf(struct vnode *v, vaddr_t *entrypoint)153{154Elf_Ehdr eh; /* Executable header */155Elf_Phdr ph; /* "Program header" = segment header */156int result, i;157struct iovec iov;158struct uio ku;159160/*161* Read the executable header from offset 0 in the file.162*/163164uio_kinit(&iov, &ku, &eh, sizeof(eh), 0, UIO_READ);165result = VOP_READ(v, &ku);166if (result) {167return result;168}169170if (ku.uio_resid != 0) {171/* short read; problem with executable? */172kprintf("ELF: short read on header - file truncated?\n");173return ENOEXEC;174}175176/*177* Check to make sure it's a 32-bit ELF-version-1 executable178* for our processor type. If it's not, we can't run it.179*180* Ignore EI_OSABI and EI_ABIVERSION - properly, we should181* define our own, but that would require tinkering with the182* linker to have it emit our magic numbers instead of the183* default ones. (If the linker even supports these fields,184* which were not in the original elf spec.)185*/186187if (eh.e_ident[EI_MAG0] != ELFMAG0 ||188eh.e_ident[EI_MAG1] != ELFMAG1 ||189eh.e_ident[EI_MAG2] != ELFMAG2 ||190eh.e_ident[EI_MAG3] != ELFMAG3 ||191eh.e_ident[EI_CLASS] != ELFCLASS32 ||192eh.e_ident[EI_DATA] != ELFDATA2MSB ||193eh.e_ident[EI_VERSION] != EV_CURRENT ||194eh.e_version != EV_CURRENT ||195eh.e_type!=ET_EXEC ||196eh.e_machine!=EM_MACHINE) {197return ENOEXEC;198}199200/*201* Go through the list of segments and set up the address space.202*203* Ordinarily there will be one code segment, one read-only204* data segment, and one data/bss segment, but there might205* conceivably be more. You don't need to support such files206* if it's unduly awkward to do so.207*208* Note that the expression eh.e_phoff + i*eh.e_phentsize is209* mandated by the ELF standard - we use sizeof(ph) to load,210* because that's the structure we know, but the file on disk211* might have a larger structure, so we must use e_phentsize212* to find where the phdr starts.213*/214215for (i=0; i<eh.e_phnum; i++) {216off_t offset = eh.e_phoff + i*eh.e_phentsize;217uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);218219result = VOP_READ(v, &ku);220if (result) {221return result;222}223224if (ku.uio_resid != 0) {225/* short read; problem with executable? */226kprintf("ELF: short read on phdr - file truncated?\n");227return ENOEXEC;228}229230switch (ph.p_type) {231case PT_NULL: /* skip */ continue;232case PT_PHDR: /* skip */ continue;233case PT_MIPS_REGINFO: /* skip */ continue;234case PT_LOAD: break;235default:236kprintf("loadelf: unknown segment type %d\n",237ph.p_type);238return ENOEXEC;239}240241result = as_define_region(curthread->t_addrspace,242ph.p_vaddr, ph.p_memsz,243ph.p_flags & PF_R,244ph.p_flags & PF_W,245ph.p_flags & PF_X);246if (result) {247return result;248}249}250251result = as_prepare_load(curthread->t_addrspace);252if (result) {253return result;254}255256/*257* Now actually load each segment.258*/259260for (i=0; i<eh.e_phnum; i++) {261off_t offset = eh.e_phoff + i*eh.e_phentsize;262uio_kinit(&iov, &ku, &ph, sizeof(ph), offset, UIO_READ);263264result = VOP_READ(v, &ku);265if (result) {266return result;267}268269if (ku.uio_resid != 0) {270/* short read; problem with executable? */271kprintf("ELF: short read on phdr - file truncated?\n");272return ENOEXEC;273}274275switch (ph.p_type) {276case PT_NULL: /* skip */ continue;277case PT_PHDR: /* skip */ continue;278case PT_MIPS_REGINFO: /* skip */ continue;279case PT_LOAD: break;280default:281kprintf("loadelf: unknown segment type %d\n",282ph.p_type);283return ENOEXEC;284}285286result = load_segment(v, ph.p_offset, ph.p_vaddr,287ph.p_memsz, ph.p_filesz,288ph.p_flags & PF_X);289if (result) {290return result;291}292}293294result = as_complete_load(curthread->t_addrspace);295if (result) {296return result;297}298299*entrypoint = eh.e_entry;300301return 0;302}303304305