diff options
Diffstat (limited to 'sys/kern/imgact_elf.c')
| -rw-r--r-- | sys/kern/imgact_elf.c | 283 |
1 files changed, 168 insertions, 115 deletions
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index a0a228466784b..c33c6bea43cb5 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.43 1998/12/04 22:54:51 archie Exp $ + * $Id: imgact_elf.c,v 1.39 1998/10/15 09:52:19 dfr Exp $ */ #include "opt_rlimit.h" @@ -63,25 +63,40 @@ #include <vm/vm_prot.h> #include <vm/vm_extern.h> -#include <machine/elf.h> #include <machine/md_var.h> -__ElfType(Brandinfo); -__ElfType(Auxargs); +#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 + 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 proc *p, - struct vmspace *vmspace, struct vnode *vp, +static int elf_load_section __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, @@ -148,124 +163,86 @@ elf_remove_brand_entry(Elf_Brandinfo *entry) static int elf_check_header(const Elf_Ehdr *hdr, int type) { - 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) + 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)) return ENOEXEC; - if (!ELF_MACHINE_OK(hdr->e_machine)) +#ifdef __i386__ + if (hdr->e_machine != EM_386 && hdr->e_machine != EM_486) +#endif +#ifdef __alpha__ + if (hdr->e_machine != EM_ALPHA) +#endif return ENOEXEC; - if (hdr->e_type != type || hdr->e_version != ELF_TARG_VER) + + if (hdr->e_type != type) return ENOEXEC; return 0; } static int -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) +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) { size_t map_len; vm_offset_t map_addr; - int error, rv; + int error; + unsigned char *data_buf = 0; 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) - file_addr; + map_len = trunc_page(offset+filsz) - trunc_page(offset); else - 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; - } + 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; + if (memsz == filsz) + return 0; /* - * 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. + * 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... */ 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) { - 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_map_find(&vmspace->vm_map, NULL, 0, + &map_addr, map_len, FALSE, + VM_PROT_ALL, VM_PROT_ALL,0)) + return error; } - 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); - } + 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); /* * set it to the specified protection @@ -273,6 +250,7 @@ elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, vm_o 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; } @@ -285,9 +263,10 @@ 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; + vm_prot_t prot = 0; 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; @@ -333,9 +312,19 @@ 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 ((hdr->e_phoff > PAGE_SIZE) || - (hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) { + if (header_size + hdr->e_phoff > PAGE_SIZE) { error = ENOEXEC; goto fail; } @@ -343,8 +332,14 @@ 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++) { - if (phdr[i].p_type == PT_LOAD) { /* Loadable segment */ - prot = 0; + 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_flags & PF_X) prot |= VM_PROT_EXECUTE; if (phdr[i].p_flags & PF_W) @@ -352,7 +347,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(p, vmspace, nd.ni_vp, + if (error = elf_load_section(vmspace, nd.ni_vp, phdr[i].p_offset, (caddr_t)phdr[i].p_vaddr + (*addr), @@ -372,13 +367,37 @@ 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: @@ -397,14 +416,14 @@ static int exec_elf_imgact(struct image_params *imgp) { const Elf_Ehdr *hdr = (const Elf_Ehdr *) imgp->image_header; - const Elf_Phdr *phdr; + const Elf_Phdr *phdr, *mapped_phdr = NULL; Elf_Auxargs *elf_auxargs = NULL; struct vmspace *vmspace; - vm_prot_t prot; + vm_prot_t prot = 0; 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; + int error, i, header_size = 0; const char *interp = NULL; Elf_Brandinfo *brand_info; char *brand; @@ -421,12 +440,24 @@ 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 + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) { + (hdr->e_phoff + header_size) > 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. @@ -441,8 +472,12 @@ 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 */ - prot = 0; + { + UPRINTF ("ELF PT_LOAD section "); if (phdr[i].p_flags & PF_X) prot |= VM_PROT_EXECUTE; if (phdr[i].p_flags & PF_W) @@ -450,8 +485,7 @@ exec_elf_imgact(struct image_params *imgp) if (phdr[i].p_flags & PF_R) prot |= VM_PROT_READ; - if (error = elf_load_section(imgp->proc, - vmspace, imgp->vp, + if (error = elf_load_section(vmspace, imgp->vp, phdr[i].p_offset, (caddr_t)phdr[i].p_vaddr, phdr[i].p_memsz, @@ -470,26 +504,44 @@ 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: - break; + UPRINTF ("ELF %d section ??\n", phdr[i].p_type); } } @@ -548,8 +600,9 @@ exec_elf_imgact(struct image_params *imgp) imgp->proc->p_sysent = brand_info->sysvec; if (interp != NULL) { - snprintf(path, sizeof(path), "%s%s", - brand_info->emul_path, interp); + 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); @@ -557,6 +610,8 @@ exec_elf_imgact(struct image_params *imgp) } } + UPRINTF("Executing %s binary\n", brand_info->brand); + /* * Construct auxargs table (used by the fixup routine) */ @@ -649,6 +704,7 @@ elf_coredump(p) { register struct vnode *vp; register struct ucred *cred = p->p_cred->pc_ucred; + register struct vmspace *vm = p->p_vmspace; struct nameidata nd; struct vattr vattr; int error, error1; @@ -842,6 +898,7 @@ elf_corehdr(p, vp, cred, numsegs, hdr, hdrsize) size_t hdrsize; void *hdr; { + struct vmspace *vm = p->p_vmspace; size_t off; prstatus_t status; prfpregset_t fpregset; @@ -854,11 +911,7 @@ elf_corehdr(p, vp, cred, numsegs, hdr, hdrsize) status.pr_gregsetsz = sizeof(gregset_t); status.pr_fpregsetsz = sizeof(fpregset_t); status.pr_osreldate = osreldate; -#ifndef COMPAT_LINUX_THREADS status.pr_cursig = p->p_sigacts->ps_sig; -#else - status.pr_cursig = p->p_sig; -#endif /* COMPAT_LINUX_THREADS */ status.pr_pid = p->p_pid; fill_regs(p, &status.pr_reg); |
