diff options
author | peter <peter@FreeBSD.org> | 1998-10-18 15:55:12 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1998-10-18 15:55:12 +0000 |
commit | 20984da25528129212c632e761cf0ee0f27a4b8b (patch) | |
tree | 3d1906e219197116301688d5d001f3d9755df5b0 /sys/kern | |
parent | 6f3b588dc2451e3fc9999417e47754d521a12d8a (diff) | |
download | FreeBSD-src-20984da25528129212c632e761cf0ee0f27a4b8b.zip FreeBSD-src-20984da25528129212c632e761cf0ee0f27a4b8b.tar.gz |
Some cleanups and optimizations:
- Use the system headers method for Elf32/Elf64 symbol compatability
- get rid of the UPRINTF debugging.
- check the ELF header for compatability much more completely
- optimize the section mapper. Use the same direct VM interfaces that
imgact_aout.c and kern_exec.c use.
- Check the return codes from the vm_* functions better. Some return
KERN_* results, not an errno.
- prefault the page tables to reduce startup faults on page tables like
a.out does.
- reset the segment protection to zero for each loop, otherwise each
segment could get progressively more privs. (eg: if the first was
read/write/execute, and the second was meant to be read/execute, the
bug would make the second r/w/x too. In practice this was not a
problem because executables are normally laid out with text first.)
- Don't impose arbitary limits. Use the limits on headers imposed by
the need to fit them into one page.
- Remove unused switch() cases now that the verbose debugging is gone.
I've been using an earlier version of this for a month or so.
This sped up ELF exec speed a bit for me but I found it hard to get
consistant benchmarks when I tested it last (a few weeks ago).
I'm still bothered by the page read out of order caused by the
transition from data to bss. This which requires either part filling the
transition page or clearing the remainder.
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/imgact_elf.c | 271 |
1 files changed, 108 insertions, 163 deletions
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index c33c6be..e049b52 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -26,7 +26,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: imgact_elf.c,v 1.39 1998/10/15 09:52:19 dfr Exp $ + * $Id: imgact_elf.c,v 1.40 1998/10/16 03:55:00 peter Exp $ */ #include "opt_rlimit.h" @@ -63,40 +63,25 @@ #include <vm/vm_prot.h> #include <vm/vm_extern.h> +#include <machine/elf.h> #include <machine/md_var.h> -#define MAX_PHDR 32 /* XXX enough ? */ - -#if ELF_TARG_CLASS == ELFCLASS32 - -#define Elf_Ehdr Elf32_Ehdr -#define Elf_Phdr Elf32_Phdr -#define Elf_Auxargs Elf32_Auxargs -#define Elf_Brandinfo Elf32_Brandinfo - -#else - -#define Elf_Ehdr Elf64_Ehdr -#define Elf_Phdr Elf64_Phdr -#define Elf_Auxargs Elf64_Auxargs -#define Elf_Brandinfo Elf64_Brandinfo - -#endif - +__ElfType(Brandinfo); +__ElfType(Auxargs); static int elf_check_header __P((const Elf_Ehdr *hdr, int type)); static int elf_freebsd_fixup __P((long **stack_base, struct image_params *imgp)); static int elf_load_file __P((struct proc *p, char *file, u_long *addr, u_long *entry)); -static int elf_load_section __P((struct vmspace *vmspace, struct vnode *vp, +static int elf_load_section __P((struct proc *p, + struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot)); static int exec_elf_imgact __P((struct image_params *imgp)); static int elf_trace = 0; SYSCTL_INT(_debug, OID_AUTO, elf_trace, CTLFLAG_RW, &elf_trace, 0, ""); -#define UPRINTF if (elf_trace) uprintf static struct sysentvec elf_freebsd_sysvec = { SYS_MAXSYSCALL, @@ -163,86 +148,124 @@ elf_remove_brand_entry(Elf_Brandinfo *entry) static int elf_check_header(const Elf_Ehdr *hdr, int type) { - if (!(hdr->e_ident[EI_MAG0] == ELFMAG0 && - hdr->e_ident[EI_MAG1] == ELFMAG1 && - hdr->e_ident[EI_MAG2] == ELFMAG2 && - hdr->e_ident[EI_MAG3] == ELFMAG3)) + if (!IS_ELF(*hdr) || + hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || + hdr->e_ident[EI_DATA] != ELF_TARG_DATA || + hdr->e_ident[EI_VERSION] != EV_CURRENT) return ENOEXEC; -#ifdef __i386__ - if (hdr->e_machine != EM_386 && hdr->e_machine != EM_486) -#endif -#ifdef __alpha__ - if (hdr->e_machine != EM_ALPHA) -#endif + if (!ELF_MACHINE_OK(hdr->e_machine)) return ENOEXEC; - - if (hdr->e_type != type) + if (hdr->e_type != type || hdr->e_version != ELF_TARG_VER) return ENOEXEC; return 0; } static int -elf_load_section(struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot) +elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot) { size_t map_len; vm_offset_t map_addr; - int error; - unsigned char *data_buf = 0; + int error, rv; size_t copy_len; + vm_object_t object; + vm_offset_t file_addr; + vm_offset_t data_buf = 0; + + object = vp->v_object; + error = 0; map_addr = trunc_page((vm_offset_t)vmaddr); + file_addr = trunc_page(offset); + /* + * We have two choices. We can either clear the data in the last page + * of an oversized mapping, or we can start the anon mapping a page + * early and copy the initialized data into that first page. We + * choose the second.. + */ if (memsz > filsz) - map_len = trunc_page(offset+filsz) - trunc_page(offset); + map_len = trunc_page(offset+filsz) - file_addr; else - map_len = round_page(offset+filsz) - trunc_page(offset); - - if (error = vm_mmap (&vmspace->vm_map, - &map_addr, - map_len, - prot, - VM_PROT_ALL, - MAP_PRIVATE | MAP_FIXED, - (caddr_t)vp, - trunc_page(offset))) - return error; + map_len = round_page(offset+filsz) - file_addr; + + if (map_len != 0) { + vm_object_reference(object); + vm_map_lock(&vmspace->vm_map); + rv = vm_map_insert(&vmspace->vm_map, + object, + file_addr, /* file offset */ + map_addr, /* virtual start */ + map_addr + map_len,/* virtual end */ + prot, + VM_PROT_ALL, + MAP_COPY_NEEDED | MAP_COPY_ON_WRITE); + vm_map_unlock(&vmspace->vm_map); + if (rv != KERN_SUCCESS) + return EINVAL; + + /* prefault the page tables */ + pmap_object_init_pt(&vmspace->vm_pmap, + map_addr, + object, + (vm_pindex_t) OFF_TO_IDX(file_addr), + map_len, + 0); + + /* we can stop now if we've covered it all */ + if (memsz == filsz) + return 0; + } - if (memsz == filsz) - return 0; /* - * We have to map the remaining bit of the file into the kernel's - * memory map, allocate some anonymous memory, and copy that last - * bit into it. The remaining space should be .bss... + * We have to get the remaining bit of the file into the first part + * of the oversized map segment. This is normally because the .data + * segment in the file is extended to provide bss. It's a neat idea + * to try and save a page, but it's a pain in the behind to implement. */ copy_len = (offset + filsz) - trunc_page(offset + filsz); map_addr = trunc_page((vm_offset_t)vmaddr + filsz); map_len = round_page((vm_offset_t)vmaddr + memsz) - map_addr; + /* This had damn well better be true! */ if (map_len != 0) { - if (error = vm_map_find(&vmspace->vm_map, NULL, 0, - &map_addr, map_len, FALSE, - VM_PROT_ALL, VM_PROT_ALL,0)) - return error; + vm_map_lock(&vmspace->vm_map); + rv = vm_map_insert(&vmspace->vm_map, NULL, 0, + map_addr, map_addr + map_len, + VM_PROT_ALL, VM_PROT_ALL, 0); + vm_map_unlock(&vmspace->vm_map); + if (rv != KERN_SUCCESS) + return EINVAL; } - if (error = vm_mmap(exec_map, - (vm_offset_t *)&data_buf, - PAGE_SIZE, - VM_PROT_READ, - VM_PROT_READ, - 0, - (caddr_t)vp, - trunc_page(offset + filsz))) - return error; - - error = copyout(data_buf, (caddr_t)map_addr, copy_len); - - vm_map_remove(exec_map, (vm_offset_t)data_buf, - (vm_offset_t)data_buf + PAGE_SIZE); + if (copy_len != 0) { + vm_object_reference(object); + rv = vm_map_find(exec_map, + object, + trunc_page(offset + filsz), + &data_buf, + PAGE_SIZE, + TRUE, + VM_PROT_READ, + VM_PROT_ALL, + MAP_COPY_ON_WRITE | MAP_COPY_NEEDED); + if (rv != KERN_SUCCESS) { + vm_object_deallocate(object); + return EINVAL; + } + pmap_object_init_pt(exec_map->pmap, data_buf, object, + (vm_pindex_t) OFF_TO_IDX(trunc_page(offset + filsz)), + PAGE_SIZE, 1); + + /* send the page fragment to user space */ + error = copyout((caddr_t)data_buf, (caddr_t)map_addr, copy_len); + vm_map_remove(exec_map, data_buf, data_buf + PAGE_SIZE); + if (error) + return (error); + } /* * set it to the specified protection @@ -250,7 +273,6 @@ elf_load_section(struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, vm_map_protect(&vmspace->vm_map, map_addr, map_addr + map_len, prot, FALSE); - UPRINTF("bss size %d (%x)\n", map_len-copy_len, map_len-copy_len); return error; } @@ -263,10 +285,9 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry) struct vmspace *vmspace = p->p_vmspace; struct vattr attr; struct image_params image_params, *imgp; - vm_prot_t prot = 0; + vm_prot_t prot; unsigned long text_size = 0, data_size = 0; unsigned long text_addr = 0, data_addr = 0; - int header_size = 0; int error, i; imgp = &image_params; @@ -312,19 +333,9 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry) if (error = elf_check_header(hdr, ET_DYN)) goto fail; - /* - * ouch, need to bounds check in case user gives us a corrupted - * file with an insane header size - */ - if (hdr->e_phnum > MAX_PHDR) { /* XXX: ever more than this? */ - error = ENOEXEC; - goto fail; - } - - header_size = hdr->e_phentsize * hdr->e_phnum; - /* Only support headers that fit within first page for now */ - if (header_size + hdr->e_phoff > PAGE_SIZE) { + if ((hdr->e_phoff > PAGE_SIZE) || + (hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) { error = ENOEXEC; goto fail; } @@ -332,14 +343,8 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry) phdr = (Elf_Phdr *)(imgp->image_header + hdr->e_phoff); for (i = 0; i < hdr->e_phnum; i++) { - switch(phdr[i].p_type) { - - case PT_NULL: /* NULL section */ - UPRINTF ("ELF(file) PT_NULL section\n"); - break; - case PT_LOAD: /* Loadable segment */ - { - UPRINTF ("ELF(file) PT_LOAD section "); + if (phdr[i].p_type == PT_LOAD) { /* Loadable segment */ + prot = 0; if (phdr[i].p_flags & PF_X) prot |= VM_PROT_EXECUTE; if (phdr[i].p_flags & PF_W) @@ -347,7 +352,7 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry) if (phdr[i].p_flags & PF_R) prot |= VM_PROT_READ; - if (error = elf_load_section(vmspace, nd.ni_vp, + if (error = elf_load_section(p, vmspace, nd.ni_vp, phdr[i].p_offset, (caddr_t)phdr[i].p_vaddr + (*addr), @@ -367,37 +372,13 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry) phdr[i].p_vaddr - trunc_page(phdr[i].p_vaddr)); *entry=(unsigned long)hdr->e_entry+(*addr); - UPRINTF(".text <%08lx,%08lx> entry=%08lx\n", - text_addr, text_size, *entry); } else { data_addr = trunc_page(phdr[i].p_vaddr+(*addr)); data_size = round_page(phdr[i].p_memsz + phdr[i].p_vaddr - trunc_page(phdr[i].p_vaddr)); - UPRINTF(".data <%08lx,%08lx>\n", - data_addr, data_size); } } - break; - - case PT_DYNAMIC:/* Dynamic link information */ - UPRINTF ("ELF(file) PT_DYNAMIC section\n"); - break; - case PT_INTERP: /* Path to interpreter */ - UPRINTF ("ELF(file) PT_INTERP section\n"); - break; - case PT_NOTE: /* Note section */ - UPRINTF ("ELF(file) PT_NOTE section\n"); - break; - case PT_SHLIB: /* Shared lib section */ - UPRINTF ("ELF(file) PT_SHLIB section\n"); - break; - case PT_PHDR: /* Program header table info */ - UPRINTF ("ELF(file) PT_PHDR section\n"); - break; - default: - UPRINTF ("ELF(file) %d section ??\n", phdr[i].p_type ); - } } fail: @@ -419,11 +400,11 @@ exec_elf_imgact(struct image_params *imgp) const Elf_Phdr *phdr, *mapped_phdr = NULL; Elf_Auxargs *elf_auxargs = NULL; struct vmspace *vmspace; - vm_prot_t prot = 0; + vm_prot_t prot; u_long text_size = 0, data_size = 0; u_long text_addr = 0, data_addr = 0; u_long addr, entry = 0, proghdr = 0; - int error, i, header_size = 0; + int error, i; const char *interp = NULL; Elf_Brandinfo *brand_info; char *brand; @@ -440,24 +421,12 @@ exec_elf_imgact(struct image_params *imgp) * detected an ELF file. */ - /* - * ouch, need to bounds check in case user gives us a corrupted - * file with an insane header size - */ - if (hdr->e_phnum > MAX_PHDR) { /* XXX: ever more than this? */ - return ENOEXEC; - } - - header_size = hdr->e_phentsize * hdr->e_phnum; - if ((hdr->e_phoff > PAGE_SIZE) || - (hdr->e_phoff + header_size) > PAGE_SIZE) { + (hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) { /* Only support headers in first page for now */ return ENOEXEC; - } else { - phdr = (const Elf_Phdr*) - ((const char *)imgp->image_header + hdr->e_phoff); } + phdr = (const Elf_Phdr*)(imgp->image_header + hdr->e_phoff); /* * From this point on, we may have resources that need to be freed. @@ -472,12 +441,8 @@ exec_elf_imgact(struct image_params *imgp) for (i = 0; i < hdr->e_phnum; i++) { switch(phdr[i].p_type) { - case PT_NULL: /* NULL section */ - UPRINTF ("ELF PT_NULL section\n"); - break; case PT_LOAD: /* Loadable segment */ - { - UPRINTF ("ELF PT_LOAD section "); + prot = 0; if (phdr[i].p_flags & PF_X) prot |= VM_PROT_EXECUTE; if (phdr[i].p_flags & PF_W) @@ -485,7 +450,8 @@ exec_elf_imgact(struct image_params *imgp) if (phdr[i].p_flags & PF_R) prot |= VM_PROT_READ; - if (error = elf_load_section(vmspace, imgp->vp, + if (error = elf_load_section(imgp->proc, + vmspace, imgp->vp, phdr[i].p_offset, (caddr_t)phdr[i].p_vaddr, phdr[i].p_memsz, @@ -504,44 +470,26 @@ exec_elf_imgact(struct image_params *imgp) phdr[i].p_vaddr - text_addr); entry = (u_long)hdr->e_entry; - UPRINTF(".text <%08lx,%08lx> entry=%08lx\n", - text_addr, text_size, entry); } else { data_addr = trunc_page(phdr[i].p_vaddr); data_size = round_page(phdr[i].p_memsz + phdr[i].p_vaddr - data_addr); - UPRINTF(".data <%08lx,%08lx>\n", - data_addr, data_size); } - } - break; - - case PT_DYNAMIC:/* Dynamic link information */ - UPRINTF ("ELF PT_DYNAMIC section ??\n"); break; case PT_INTERP: /* Path to interpreter */ - UPRINTF ("ELF PT_INTERP section "); if (phdr[i].p_filesz > MAXPATHLEN || phdr[i].p_offset + phdr[i].p_filesz > PAGE_SIZE) { error = ENOEXEC; goto fail; } interp = imgp->image_header + phdr[i].p_offset; - UPRINTF("<%s>\n", interp); - break; - case PT_NOTE: /* Note section */ - UPRINTF ("ELF PT_NOTE section\n"); - break; - case PT_SHLIB: /* Shared lib section */ - UPRINTF ("ELF PT_SHLIB section\n"); break; case PT_PHDR: /* Program header table info */ - UPRINTF ("ELF PT_PHDR section <%x>\n", phdr[i].p_vaddr); proghdr = phdr[i].p_vaddr; break; default: - UPRINTF ("ELF %d section ??\n", phdr[i].p_type); + break; } } @@ -602,7 +550,6 @@ exec_elf_imgact(struct image_params *imgp) if (interp != NULL) { strcpy(path, brand_info->emul_path); strcat(path, interp); - UPRINTF("interpreter=<%s> %s\n", interp, brand_info->emul_path); if ((error = elf_load_file(imgp->proc, path, &addr, &imgp->entry_addr)) != 0) { uprintf("ELF interpreter %s not found\n", path); @@ -610,8 +557,6 @@ exec_elf_imgact(struct image_params *imgp) } } - UPRINTF("Executing %s binary\n", brand_info->brand); - /* * Construct auxargs table (used by the fixup routine) */ |