/* * 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; return 0; }
/* * 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 = curthread->t_addrspace; /* * 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. */ DEBUG(DB_DEMAND,"Num Of Segements:%d\n",eh.e_phnum); 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; } DEBUG(DB_DEMAND, "MEM SZ:%d\n",ph.p_memsz); DEBUG(DB_DEMAND, "FILE SZ:%d\n", ph.p_filesz); DEBUG(DB_DEMAND, "VADDR:%p\n",(void*)ph.p_vaddr); result = as_define_region(curthread->t_addrspace, 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(curthread->t_addrspace); 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); DEBUG(DB_DEMAND, "SEGMENT: %d\n",1); 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; } struct addrspace *as = curthread->t_addrspace; vaddr_t va = ph.p_vaddr; DEBUG(DB_DEMAND, "Original VA: %p\n", (void*) va); DEBUG(DB_DEMAND, "Max VA:%p\n", (void*) (va + ph.p_memsz)); // va &= PAGE_FRAME; struct page_table *pt = pgdir_walk(as,va,false); int pt_index = VA_TO_PT_INDEX(va); int permissions = PTE_TO_PERMISSIONS(pt->table[pt_index]); int small_pages = 0; if(ph.p_memsz <= PAGE_SIZE) { DEBUG(DB_DEMAND, "Segement Total Size is < 4k\n"); // pt = pgdir_walk(as,va,false); // pt_index = VA_TO_PT_INDEX(va); // permissions = PTE_TO_PERMISSIONS(pt->table[pt_index]); result = dynamic_load_segment(v, ph.p_offset, va, ph.p_memsz, ph.p_filesz, ph.p_flags & PF_X,permissions); if (result) { return result; } // page->state = DIRTY; small_pages = 1; } else { int filesize = (int) ph.p_filesz; int memsize = (int) ph.p_memsz; off_t cur_offset = ph.p_offset; DEBUG(DB_DEMAND, "Loading >1 Segment\n"); DEBUG(DB_DEMAND, "Original Parameters:\n"); DEBUG(DB_DEMAND, "File Size: %d\n",filesize); DEBUG(DB_DEMAND, "Mem Size: %d\n", memsize); DEBUG(DB_DEMAND, "Offset:%d\n", (int) cur_offset); DEBUG(DB_DEMAND, "VA:%p\n", (void*) va); size_t amt_to_read; int pages = 0; int blank_pages = 0; while(filesize > 0) { // struct page *page = page_alloc(as,va & PAGE_FRAME,permissions); DEBUG(DB_DEMAND, "loading...\n"); DEBUG(DB_DEMAND, "File Size: %d\n",filesize); DEBUG(DB_DEMAND, "Mem Size: %d\n", memsize); DEBUG(DB_DEMAND, "Offset:%d\n", (int) cur_offset); DEBUG(DB_DEMAND, "VA:%p\n", (void*) va); amt_to_read = (filesize - PAGE_SIZE > 0 ? PAGE_SIZE : filesize); result = dynamic_load_segment(v, cur_offset, va, PAGE_SIZE, amt_to_read, ph.p_flags & PF_X,permissions); if (result) { return result; } filesize -= PAGE_SIZE; memsize -= PAGE_SIZE; va += PAGE_SIZE; cur_offset += PAGE_SIZE; // page->state = DIRTY; pages++; } (void)blank_pages; DEBUG(DB_DEMAND,"Empty Pages...\n"); while(memsize > 0) { DEBUG(DB_DEMAND,"Mem Size:%d\n",memsize); DEBUG(DB_DEMAND,"VA: %p\n",(void*) va); bool lock = get_coremap_lock(); struct page *page = page_alloc(as,va & PAGE_FRAME,permissions); release_coremap_lock(lock); va += PAGE_SIZE; memsize -= PAGE_SIZE; KASSERT(page->state == LOCKED); page->state = DIRTY; blank_pages++; } DEBUG(DB_DEMAND, "Small Pages: %d\n", small_pages); DEBUG(DB_DEMAND, "Pages: %d\n", pages); DEBUG(DB_DEMAND, "Blank Pages: %d\n", blank_pages); } // result = load_segment(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(curthread->t_addrspace); if (result) { return result; } *entrypoint = eh.e_entry; /* Register load complete in addrspace */ as->loadelf_done = true; DEBUG(DB_VM,"LoadELFDone\n"); // kprintf("LoadELF done\n"); return 0; }