/*- * Copyright (c) 2000 Masaru OKI * Copyright (c) 1994, 1995, 1998 Scott Bartram * Copyright (c) 1994 Adam Glass * Copyright (c) 1993, 1994 Christopher G. Demetriou * * originally from NetBSD kern/exec_ecoff.c * * Copyright (c) 2000 Takanori Watanabe * Copyright (c) 2000 KUROSAWA Takahiro * Copyright (c) 1995-1996 Sen Schmidt * Copyright (c) 1996 Peter Wemm * All rights reserved. * * originally from FreeBSD kern/imgact_elf.c * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Masaru OKI. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_pecoff.h" #define PECOFF_PE_SIGNATURE "PE\0\0" static int pecoff_fixup(register_t **, struct image_params *); #ifndef PECOFF_DEBUG #define DPRINTF(a) #else #define DPRINTF(a) printf a #endif static struct sysentvec pecoff_sysvec = { SYS_MAXSYSCALL, sysent, 0, 0, NULL, 0, NULL, NULL, pecoff_fixup, sendsig, sigcode, &szsigcode, 0, "FreeBSD PECoff", NULL, NULL, MINSIGSTKSZ, PAGE_SIZE, VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS, USRSTACK, PS_STRINGS, VM_PROT_ALL, exec_copyout_strings, exec_setregs, NULL }; static const char signature[] = PECOFF_PE_SIGNATURE; static int exec_pecoff_coff_prep_omagic(struct image_params *, struct coff_filehdr *, struct coff_aouthdr *, int peoffs); static int exec_pecoff_coff_prep_nmagic(struct image_params *, struct coff_filehdr *, struct coff_aouthdr *, int peoffs); static int exec_pecoff_coff_prep_zmagic(struct image_params *, struct coff_filehdr *, struct coff_aouthdr *, int peoffs); static int exec_pecoff_coff_makecmds(struct image_params *, struct coff_filehdr *, int); static int pecoff_signature(struct thread *, struct vnode *, const struct pecoff_dos_filehdr *); static int pecoff_read_from(struct thread *, struct vnode *, int, caddr_t, int); static int pecoff_load_section(struct thread * td, 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 pecoff_fixup(register_t ** stack_base, struct image_params * imgp) { int len = sizeof(struct pecoff_args); struct pecoff_imghdr *ap; register_t *pos; pos = *stack_base + (imgp->args->argc + imgp->args->envc + 2); ap = (struct pecoff_imghdr *) imgp->auxargs; if (copyout(ap, pos, len)) { return 0; } free(ap, M_TEMP); imgp->auxargs = NULL; (*stack_base)--; suword(*stack_base, (long) imgp->args->argc); return 0; } static int pecoff_load_section(struct thread * td, 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; size_t copy_len; size_t copy_map_len; size_t copy_start; vm_object_t object; vm_offset_t copy_map_offset; 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); DPRINTF(("SECARG:%x %p %x %x\n", offset, vmaddr, memsz, filsz)); if (file_addr != offset) { /* * The section is not on page boundary. We can't use * vm_map_insert(). Use copyin instead. */ map_len = round_page(memsz); copy_len = filsz; copy_map_offset = file_addr; copy_map_len = round_page(offset + filsz) - file_addr; copy_start = offset - file_addr; DPRINTF(("offset=%x vmaddr=%lx filsz=%x memsz=%x\n", offset, (long)vmaddr, filsz, memsz)); DPRINTF(("map_len=%x copy_len=%x copy_map_offset=%x" " copy_map_len=%x copy_start=%x\n", map_len, copy_len, copy_map_offset, copy_map_len, copy_start)); } else { map_len = trunc_page(filsz); 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_ON_WRITE | MAP_PREFAULT); vm_map_unlock(&vmspace->vm_map); if (rv != KERN_SUCCESS) { vm_object_deallocate(object); return EINVAL; } /* we can stop now if we've covered it all */ if (memsz == filsz) return 0; } copy_map_offset = trunc_page(offset + filsz); copy_map_len = PAGE_SIZE; copy_start = 0; 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; } 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); DPRINTF(("EMP-rv:%d,%x %x\n", rv, map_addr, map_addr + map_len)); if (rv != KERN_SUCCESS) { return EINVAL; } } DPRINTF(("COPYARG %x %x\n", map_addr, copy_len)); if (copy_len != 0) { vm_object_reference(object); rv = vm_map_find(exec_map, object, copy_map_offset, &data_buf, copy_map_len, TRUE, VM_PROT_READ, VM_PROT_ALL, MAP_COPY_ON_WRITE | MAP_PREFAULT_PARTIAL); if (rv != KERN_SUCCESS) { vm_object_deallocate(object); return EINVAL; } /* send the page fragment to user space */ error = copyout((caddr_t) data_buf + copy_start, (caddr_t) map_addr, copy_len); vm_map_remove(exec_map, data_buf, data_buf + copy_map_len); DPRINTF(("%d\n", error)); if (error) return (error); } /* * set it to the specified protection */ vm_map_protect(&vmspace->vm_map, map_addr, map_addr + map_len, prot, FALSE); return error; } static int pecoff_load_file(struct thread * td, const char *file, u_long * addr, u_long * entry, u_long * ldexport) { struct nameidata nd; struct pecoff_dos_filehdr dh; struct coff_filehdr *fp = 0; struct coff_aouthdr *ap; struct pecoff_opthdr *wp; struct coff_scnhdr *sh = 0; struct vmspace *vmspace = td->td_proc->p_vmspace; struct vattr attr; struct image_params image_params, *imgp; int peofs; int error, i, scnsiz; imgp = &image_params; /* * Initialize part of the common data */ imgp->proc = td->td_proc; imgp->execlabel = NULL; imgp->attr = &attr; imgp->firstpage = NULL; NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, file, td); if ((error = namei(&nd)) != 0) { nd.ni_vp = NULL; goto fail; } NDFREE(&nd, NDF_ONLY_PNBUF); imgp->vp = nd.ni_vp; /* * Check permissions, modes, uid, etc on the file, and "open" it. */ error = exec_check_permissions(imgp); VOP_UNLOCK(nd.ni_vp, 0, td); if (error) goto fail; if ((error = pecoff_read_from(td, imgp->vp, 0, (caddr_t) & dh, sizeof(dh))) != 0) goto fail; if ((error = pecoff_signature(td, imgp->vp, &dh) != 0)) goto fail; fp = malloc(PECOFF_HDR_SIZE, M_TEMP, M_WAITOK); peofs = dh.d_peofs + sizeof(signature) - 1; if ((error = pecoff_read_from(td, imgp->vp, peofs, (caddr_t) fp, PECOFF_HDR_SIZE) != 0)) goto fail; if (COFF_BADMAG(fp)) { error = ENOEXEC; goto fail; } ap = (void *) ((char *) fp + sizeof(struct coff_filehdr)); wp = (void *) ((char *) ap + sizeof(struct coff_aouthdr)); /* read section header */ scnsiz = sizeof(struct coff_scnhdr) * fp->f_nscns; sh = malloc(scnsiz, M_TEMP, M_WAITOK); if ((error = pecoff_read_from(td, imgp->vp, peofs + PECOFF_HDR_SIZE, (caddr_t) sh, scnsiz)) != 0) goto fail; /* * Read Section infomation and map sections. */ for (i = 0; i < fp->f_nscns; i++) { int prot = 0; if (sh[i].s_flags & COFF_STYP_DISCARD) continue; /* XXX ? */ if ((sh[i].s_flags & COFF_STYP_TEXT) && (sh[i].s_flags & COFF_STYP_EXEC) == 0) continue; if ((sh[i].s_flags & (COFF_STYP_TEXT | COFF_STYP_DATA | COFF_STYP_BSS)) == 0) continue; prot |= (sh[i].s_flags & COFF_STYP_READ) ? VM_PROT_READ : 0; prot |= (sh[i].s_flags & COFF_STYP_WRITE) ? VM_PROT_WRITE : 0; prot |= (sh[i].s_flags & COFF_STYP_EXEC) ? VM_PROT_EXECUTE : 0; sh[i].s_vaddr += wp->w_base; /* RVA --> VA */ if ((error = pecoff_load_section(td, vmspace, imgp->vp, sh[i].s_scnptr ,(caddr_t) sh[i].s_vaddr, sh[i].s_paddr, sh[i].s_size ,prot)) != 0) goto fail; } *entry = wp->w_base + ap->a_entry; *addr = wp->w_base; *ldexport = wp->w_imghdr[0].i_vaddr + wp->w_base; fail: if (fp) free(fp, M_TEMP); if (sh) free(sh, M_TEMP); if (nd.ni_vp) vrele(nd.ni_vp); return error; } static int exec_pecoff_coff_prep_omagic(struct image_params * imgp, struct coff_filehdr * fp, struct coff_aouthdr * ap, int peofs) { return ENOEXEC; } static int exec_pecoff_coff_prep_nmagic(struct image_params * imgp, struct coff_filehdr * fp, struct coff_aouthdr * ap, int peofs) { return ENOEXEC; } static int exec_pecoff_coff_prep_zmagic(struct image_params * imgp, struct coff_filehdr * fp, struct coff_aouthdr * ap, int peofs) { int scnsiz = sizeof(struct coff_scnhdr) * fp->f_nscns; int error = ENOEXEC, i; int prot; u_long text_size = 0, data_size = 0, dsize; u_long text_addr = 0, data_addr = VM_MAXUSER_ADDRESS; u_long ldexport = 0, ldbase = 0; struct pecoff_opthdr *wp; struct coff_scnhdr *sh; struct vmspace *vmspace; struct pecoff_args *argp = NULL; sh = malloc(scnsiz, M_TEMP, M_WAITOK); wp = (void *) ((char *) ap + sizeof(struct coff_aouthdr)); error = pecoff_read_from(FIRST_THREAD_IN_PROC(imgp->proc), imgp->vp, peofs + PECOFF_HDR_SIZE, (caddr_t) sh, scnsiz); if (error) return (error); error = exec_new_vmspace(imgp, &pecoff_sysvec); if (error) return (error); vmspace = imgp->proc->p_vmspace; for (i = 0; i < fp->f_nscns; i++) { prot = VM_PROT_WRITE; /* XXX for relocation? */ prot |= (sh[i].s_flags & COFF_STYP_READ) ? VM_PROT_READ : 0; prot |= (sh[i].s_flags & COFF_STYP_WRITE) ? VM_PROT_WRITE : 0; prot |= (sh[i].s_flags & COFF_STYP_EXEC) ? VM_PROT_EXECUTE : 0; sh[i].s_vaddr += wp->w_base; if (sh[i].s_flags & COFF_STYP_DISCARD) continue; if ((sh[i].s_flags & COFF_STYP_TEXT) != 0) { error = pecoff_load_section( FIRST_THREAD_IN_PROC(imgp->proc), vmspace, imgp->vp, sh[i].s_scnptr, (caddr_t) sh[i].s_vaddr, sh[i].s_paddr, sh[i].s_size ,prot); DPRINTF(("ERROR%d\n", error)); if (error) goto fail; text_addr = trunc_page(sh[i].s_vaddr); text_size = trunc_page(sh[i].s_size + sh[i].s_vaddr - text_addr); } if ((sh[i].s_flags & (COFF_STYP_DATA|COFF_STYP_BSS)) != 0) { if (pecoff_load_section( FIRST_THREAD_IN_PROC(imgp->proc), vmspace, imgp->vp, sh[i].s_scnptr, (caddr_t) sh[i].s_vaddr, sh[i].s_paddr, sh[i].s_size, prot) != 0) goto fail; data_addr = min(trunc_page(sh[i].s_vaddr), data_addr); dsize = round_page(sh[i].s_vaddr + sh[i].s_paddr) - data_addr; data_size = max(dsize, data_size); } } vmspace->vm_tsize = text_size >> PAGE_SHIFT; vmspace->vm_taddr = (caddr_t) (uintptr_t) text_addr; vmspace->vm_dsize = data_size >> PAGE_SHIFT; vmspace->vm_daddr = (caddr_t) (uintptr_t) data_addr; argp = malloc(sizeof(struct pecoff_args), M_TEMP, M_WAITOK); if (argp == NULL) { error = ENOMEM; goto fail; } argp->a_base = wp->w_base; argp->a_entry = wp->w_base + ap->a_entry; argp->a_end = data_addr + data_size; argp->a_subsystem = wp->w_subvers; error = pecoff_load_file(FIRST_THREAD_IN_PROC(imgp->proc), "/usr/libexec/ld.so.dll", &ldbase, &imgp->entry_addr, &ldexport); if (error) goto fail; argp->a_ldbase = ldbase; argp->a_ldexport = ldexport; memcpy(argp->a_imghdr, wp->w_imghdr, sizeof(struct pecoff_imghdr) * 16); for (i = 0; i < 16; i++) { argp->a_imghdr[i].i_vaddr += wp->w_base; } imgp->proc->p_sysent = &pecoff_sysvec; imgp->auxargs = argp; imgp->auxarg_size = sizeof(struct pecoff_args); imgp->interpreted = 0; if (sh != NULL) free(sh, M_TEMP); return 0; fail: error = (error) ? error : ENOEXEC; if (sh != NULL) free(sh, M_TEMP); if (argp != NULL) free(argp, M_TEMP); return error; } int exec_pecoff_coff_makecmds(struct image_params * imgp, struct coff_filehdr * fp, int peofs) { struct coff_aouthdr *ap; int error; if (COFF_BADMAG(fp)) { return ENOEXEC; } ap = (void *) ((char *) fp + sizeof(struct coff_filehdr)); switch (ap->a_magic) { case COFF_OMAGIC: error = exec_pecoff_coff_prep_omagic(imgp, fp, ap, peofs); break; case COFF_NMAGIC: error = exec_pecoff_coff_prep_nmagic(imgp, fp, ap, peofs); break; case COFF_ZMAGIC: error = exec_pecoff_coff_prep_zmagic(imgp, fp, ap, peofs); break; default: return ENOEXEC; } return error; } static int pecoff_signature(td, vp, dp) struct thread *td; struct vnode *vp; const struct pecoff_dos_filehdr *dp; { int error; char buf[512]; char *pesig; if (DOS_BADMAG(dp)) { return ENOEXEC; } error = pecoff_read_from(td, vp, dp->d_peofs, buf, sizeof(buf)); if (error) { return error; } pesig = buf; if (memcmp(pesig, signature, sizeof(signature) - 1) == 0) { return 0; } return EFTYPE; } int pecoff_read_from(td, vp, pos, buf, siz) struct thread *td; struct vnode *vp; int pos; caddr_t buf; int siz; { int error; size_t resid; error = vn_rdwr(UIO_READ, vp, buf, siz, pos, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td); if (error) return error; if (resid != 0) { return ENOEXEC; } return 0; } static int imgact_pecoff(struct image_params * imgp) { const struct pecoff_dos_filehdr *dp = (const struct pecoff_dos_filehdr *) imgp->image_header; struct coff_filehdr *fp; int error, peofs; struct thread *td = curthread; error = pecoff_signature(FIRST_THREAD_IN_PROC(imgp->proc), imgp->vp, dp); if (error) { return -1; } VOP_UNLOCK(imgp->vp, 0, td); peofs = dp->d_peofs + sizeof(signature) - 1; fp = malloc(PECOFF_HDR_SIZE, M_TEMP, M_WAITOK); error = pecoff_read_from(FIRST_THREAD_IN_PROC(imgp->proc), imgp->vp, peofs, (caddr_t) fp, PECOFF_HDR_SIZE); if (error) goto fail; error = exec_pecoff_coff_makecmds(imgp, fp, peofs); fail: free(fp, M_TEMP); vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td); return error; } static struct execsw pecoff_execsw = {imgact_pecoff, "FreeBSD PEcoff"}; EXEC_SET(pecoff, pecoff_execsw);