Source to kern/kern_exec.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (C) 1993 Christopher G. Demetriou
 * Copyright (C) 1992 Wolfgang Solfrank.
 * Copyright (C) 1992 TooLs GmbH.
 * All rights reserved.
 *
 * 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 TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
 *
 *	kern_exec.c,v 1.20 1993/07/13 22:13:19 cgd Exp
 */

#undef	SETUIDSCRIPTS		/* Define this for setuid scripts */
#ifdef	SETUIDSCRIPTS
#define	FDSCRIPTS		/* setuid scripts probably also want this */
#endif

#include "param.h"
#include "systm.h"
#include "filedesc.h"
#include "kernel.h"
#include "proc.h"
#include "mount.h"
#include "malloc.h"
#include "namei.h"
#include "vnode.h"
/* #include "seg.h" ??? -- cgd */
#include "file.h"
#include "acct.h"
#include "exec.h"
#include "ktrace.h"
#include "resourcevar.h"
#include "wait.h"

#include "machine/cpu.h"
#include "machine/reg.h"
#include "machine/exec.h"

#include "mman.h"
#include "vm/vm.h"
#include "vm/vm_param.h"
#include "vm/vm_map.h"
#include "vm/vm_kern.h"
#include "vm/vm_pager.h"

#include "signalvar.h"
#include "kinfo_proc.h"

#ifdef COPY_SIGCODE
extern char sigcode[], esigcode[];
#define	szsigcode	(esigcode - sigcode)
#else
#define	szsigcode	0
#endif

#ifdef	FDSCRIPTS
/* The returned attributes are only good for set[ug]id scripts/programs */
static checkaout(p,ndp,vpp,ap,hdr,fdp,argp,argc,epp)
struct proc *p;
struct nameidata *ndp;
struct vnode **vpp;
struct vattr *ap;
struct exec *hdr;
int *fdp;
char **argp;
int *argc;
struct exec_package *epp;
#else
static checkaout(p,ndp,vpp,ap,hdr,argp,argc,epp)
struct proc *p;
struct nameidata *ndp;
struct vnode **vpp;
struct vattr *ap;
struct exec *hdr;
char **argp;
int *argc;
struct exec_package *epp;
#endif
{
    int error;
    struct vnode *vp;
    char *cp, *ep, *name;
    int resid;
    
    /* XXX - THIS ENTIRE FUCTION SHOULD BE REWRITTEN - cgd */

    /* first get the vnode */
    if (error = namei(ndp,p))
	return error;
    *vpp = vp = ndp->ni_vp;
    
    /* check for regular file */
    if (vp->v_type != VREG) {
	error = EACCES;
	goto bad1;
    }
    
    /* get attributes */
    if (error = VOP_GETATTR(vp,ap,p->p_ucred,p))
	goto bad1;
    
    /* Check mount point */
    if (vp->v_mount->mnt_flag&MNT_NOEXEC) {
	error = ENOEXEC;
	goto bad1;
    }
    if ((vp->v_mount->mnt_flag&MNT_NOSUID) || (p->p_flag&STRC))
	ap->va_mode &= ~(VSUID|VSGID);
    
    /* for root we have to check for any exec bit on */
    /* doing it always doesn't hurt) */
    if (!(ap->va_mode&0111)) {
	error = EACCES;
	goto bad1;
    }
    
    /* check access */
    if ((error = VOP_ACCESS(vp,VEXEC,p->p_ucred,p))
	|| (error = VOP_OPEN(vp,FREAD,p->p_ucred,p)))
	goto bad1;
    
    /* now we have the file, get the exec header */
    if (error = vn_rdwr(UIO_READ,vp,(caddr_t)hdr,sizeof(*hdr),0,
			UIO_SYSSPACE,IO_NODELOCKED,p->p_ucred,&resid,p))
	goto bad2;
    
    if (!resid && !(hdr->a_text&PGOFSET) && !(hdr->a_data&PGOFSET)) {
      /* sanity check: if it could get by the above, and still be a
       * shell script.  if so, don't try the vmcmd tricks.
       */
      cp = (char *)hdr;
      if (!(*cp == '#' && cp[1] == '!')) {
	/* set up the vmcmds for creation of the process address space */
	epp->ep_execp = hdr;
	epp->ep_vp = vp;
	epp->ep_vap = ap;
	if (error = exec_makecmds(p, epp))
	  goto bad2;

	if (epp->ep_entry > VM_MAXUSER_ADDRESS) {
	  error = ENOEXEC;
	  goto bad2;
	}
	
	/* check limits */
	if ((epp->ep_tsize > MAXTSIZ) ||
	    (epp->ep_dsize > p->p_rlimit[RLIMIT_DATA].rlim_cur) ||
	    (epp->ep_ssize > p->p_rlimit[RLIMIT_STACK].rlim_cur)) {
	  error = ENOMEM;
	  goto bad2;
	}
	
	return 0;
      }
    }
    
    /* scripts only if it's the first round */
    cp = (char *)hdr;
    ep = (char *)(hdr + 1) - resid;
    if (ndp->ni_segflg == UIO_USERSPACE && *cp == '#' && cp[1] == '!') {
#ifdef	SETUIDSCRIPTS
	struct vattr attr;
#define	ATTRP	&attr
#else
#define	ATTRP	ap
#endif
#ifdef	FDSCRIPTS
	struct file *fp;
	int fd;
	extern struct fileops vnops;
	char fdscripts = 0;
	
	/* we want to give the interpreter /dev/fd/xxx if the script is:
	   set[ug]id or
	   execute only */
	
	if (VOP_ACCESS(vp,VREAD,p->p_ucred,p) == EACCES
	    || (ap->va_mode&(VSUID|VSGID)))
	    fdscripts = 1;
	
	if (fdscripts) {
	    if (error = falloc(p,&fp,&fd))
		goto bad2;
	    
	    VOP_UNLOCK(vp);
	    fp->f_type = DTYPE_VNODE;
	    fp->f_ops = &vnops;
	    fp->f_data = (caddr_t)vp;
	    fp->f_flag = FREAD;
	} else {
#endif
	    VOP_UNLOCK(vp);
	    vn_close(vp,FREAD,p->p_ucred,p);
#ifdef	FDSCRIPTS
	}
#endif
	
	for (cp += 2; cp < ep; cp++) {
	    switch (*cp) {
	    case ' ':
	    case '\t':
		continue;
	    }
	    break;
	}
	if (cp >= ep)
	    error = EACCES;
	
	++*argc;
	for (name = cp; cp < ep; cp++) {
	    switch (*cp) {
	    default:
		*(*argp)++ = *cp;
		continue;
	    case '\0':
	    case '\n':
		ep = cp;
	    case ' ':
	    case '\t':
		*(*argp)++ = *cp++ = 0;
		goto out1;
	    }
	    break;
	}
out1:
	for (; cp < ep; cp++) {
	    switch (*cp) {
	    case ' ':
	    case '\t':
		continue;
	    case '\0':
	    case '\n':
		ep = cp;
	    }
	    break;
	}
	if (cp < ep) {
	    ++*argc;
	    for (; cp < ep; cp++) {
		switch (*cp) {
		default:
		    *(*argp)++ = *cp;
		    continue;
		case '\0':
		case '\n':
		    *(*argp)++ = 0;
		    goto out2;
		}
		break;
	    }
	}
out2:
	
#ifdef	FDSCRIPTS
	if (fdscripts) {
	    sprintf(*argp,"/dev/fd/%d",fd);
	    *fdp = fd;
	} else
#endif
	    strcpy(*argp,ndp->ni_pnbuf);
	while (*(*argp)++);
	++*argc;

	ndp->ni_dirp = name;
	ndp->ni_segflg = UIO_SYSSPACE;
#ifdef	FDSCRIPTS
	if (!error && !(error = checkaout(p,ndp,vpp,ATTRP,hdr,&fd,argp,
					  argc,epp))) {
#else
	if (!error && !(error = checkaout(p,ndp,vpp,ATTRP,hdr,argp,
					  argc,epp))) {
#endif
#ifdef	SETUIDSCRIPTS
	    if (!(ap->va_mode&VSUID))
		ap->va_uid = attr.va_uid;
	    if (!(ap->va_mode&VSGID))
		ap->va_gid = attr.va_gid;
	    ap->va_mode |= attr.va_mode&(VSUID|VSGID);
#endif
	    return 0;
	}
	
#ifdef	FDSCRIPTS
	if (fdscripts) {
	    closefd(p,fd);
	    goto bad2;
	}
#endif
	return error;
    }
    
    error = ENOEXEC;
    
bad2:
    VOP_UNLOCK(vp);
    vn_close(vp,FREAD,p->p_ucred,p);
    FREE(ndp->ni_pnbuf,M_NAMEI);
    return error;
bad1:
    FREE(ndp->ni_pnbuf,M_NAMEI);
    vput(vp);
    return error;
}

#ifdef	FDSCRIPTS
static closefd(p,fd)
struct proc *p;
{
    /* have to close file again */
    if (p->p_fd->fd_lastfile == fd)
	p->p_fd->fd_lastfile--;
    if (p->p_fd->fd_freefile > fd)
	p->p_fd->fd_freefile = fd;
    closef(p->p_fd->fd_ofiles[fd],p);
    p->p_fd->fd_ofiles[fd] = 0;
}
#endif

#ifdef EXEC_DEBUG
exec_print_vmspaceinfo(struct proc *p)
{
  printf("process vmspace:\n");
  printf("\tvm_taddr:    0x%x\n", p->p_vmspace->vm_taddr);
  printf("\tvm_tsize:    0x%x\n", p->p_vmspace->vm_tsize);
  printf("\tvm_daddr:    0x%x\n", p->p_vmspace->vm_daddr);
  printf("\tvm_dsize:    0x%x\n", p->p_vmspace->vm_dsize);
  printf("\tvm_ssize:    0x%x\n", p->p_vmspace->vm_ssize);
  printf("\tvm_maxsaddr: 0x%x\n", p->p_vmspace->vm_maxsaddr);

  /* handy place for debugger breakpoint, but name is too long... */
  asm(".globl _exec_print_vmspaceinfo_leave ; _exec_print_vmspaceinfo_leave:");
}
#endif

#ifdef EXEC_DEBUG
exec_print_argstring(char *str, int len, char **ap, char *a)
{
  printf("(0x%x)-> \t0x%x     \t: %s (%d)\n", ap, a, str, len);
}
#endif

/*
 * exec system call
 */

struct execve_args {
	char	*fname;
	char	**argp;
	char	**envp;
};

/* ARGSUSED */
execve(p, uap, retval)
	register struct proc *p;
	register struct execve_args *uap;
	int *retval;
{

	/*
	 * Body deleted.
	 */
	/*
	 * And reimplemented by ws.
	 * I think this should be vastly more machine dependent
	 * (e.g. stack up/down, startup stack format etc.)
	 */
	int error;
	struct vnode *vp;
	struct nameidata nid;
	struct ucred *cred = p->p_ucred;
	char *argp;
	struct exec hdr;
	char **cpp, *dp, *sp, *np;
	int argc, envc, len;
	char *stack;
	struct vattr attr;
	struct ps_strings arginfo;
	struct exec_package pack;
#ifdef	FDSCRIPTS
	int fd;
#endif
	
	/* get space for argv & environment */
/* was:	if(!(dp = argp = (char *)kmem_alloc_wait(exec_map,ARG_MAX))) - cgd */
	MALLOC(argp, char *, ARG_MAX, M_EXEC, M_WAITOK);
	if(!argp)
	    panic("exec: can't allocate ARG_MAX");
	dp = argp;
	argc = 0;
#ifdef	FDSCRIPTS
	fd = -1;
#endif
	
	nid.ni_dirp = uap->fname;
	nid.ni_segflg = UIO_USERSPACE;
	nid.ni_nameiop = LOOKUP|FOLLOW|LOCKLEAF|SAVENAME;
	/* checkaout possibly copies arguments for scripts */
#ifdef	FDSCRIPTS
	if (error = checkaout(p,&nid,&vp,&attr,&hdr,&fd,&dp,&argc,&pack))
#else
	if (error = checkaout(p,&nid,&vp,&attr,&hdr,&dp,&argc,&pack))
#endif
	    goto freeargs;

	/* Now get argv & environment */
	if (!(cpp = uap->argp)) {
	    error = EINVAL;
	    goto bad;
	}
	
	if (argc > 0) {
	    /* it is a script, so we have to skip the 0th arg */
	    cpp++;
	    /* check access to arg here? */
	}
	
#ifdef EXEC_DEBUG
	printf("start of args = 0x%x\n", dp);
#endif
	while (1) {
	    len = argp + ARG_MAX - dp;
	    if (error = copyin(cpp,&sp,sizeof(sp)))
		goto bad;
	    if (!sp)
		break;
	    if (error = copyinstr(sp,dp,len,(u_int *)&len)) {
		if (error == ENAMETOOLONG)
		    error = E2BIG;
		goto bad;
	    }
	    dp += len;
	    cpp++;
	    argc++;
	}
#ifdef EXEC_DEBUG
	printf("end of args = 0x%x\n", dp);
#endif

	envc = 0;
	if (cpp = uap->envp) {	/* environment need not be there */
	    while (1) {
		len = argp + ARG_MAX - dp;
		if (error = copyin(cpp,&sp,sizeof(sp)))
		    goto bad;
		if (!sp)
		    break;
		if (error = copyinstr(sp,dp,len,(u_int *)&len)) {
		    if (error == ENAMETOOLONG)
			error = E2BIG;
		    goto bad;
		}
		dp += len;
		cpp++;
		envc++;
	    }
	    dp = (char *)ALIGN(dp);
	}
#ifdef EXEC_DEBUG
	printf("end of env = 0x%x\n", dp);
	printf("argc/envc = %d/%d\n", argc, envc);
#endif

	/* Now check if args & environ fit into new stack */
	len = ((argc + envc + 2) * sizeof(char *) + sizeof(int) +
	       dp + sizeof(struct ps_strings) + szsigcode) - argp;
	len = roundup(len, sizeof(char *));
	if (len > p->p_rlimit[RLIMIT_STACK].rlim_cur) {
	    error = ENOMEM;
	    goto bad;
	}

	/* Unmap old program */
	vm_deallocate(&p->p_vmspace->vm_map,0,USRSTACK);
	
	/* Now map address space */
	p->p_vmspace->vm_taddr = (char *) pack.ep_taddr;
	p->p_vmspace->vm_tsize = btoc(pack.ep_tsize);
	p->p_vmspace->vm_daddr = (char *) pack.ep_daddr;
	p->p_vmspace->vm_dsize = btoc(pack.ep_dsize);
	p->p_vmspace->vm_ssize = btoc(pack.ep_ssize);
	p->p_vmspace->vm_maxsaddr = (char *) pack.ep_maxsaddr;
	if (error = exec_runcmds(p, &pack))
		goto exec_abort;
	kill_vmcmd(&pack.ep_vcp);
	
	/* remember information about the process */
	arginfo.ps_nargvstr = argc;
	arginfo.ps_nenvstr = envc;

	/* Now copy argc, args & environ to new stack */
	stack = (char *)(USRSTACK - len);
#ifdef EXEC_DEBUG
	printf("stack wants to live at: 0x%x\n", stack);
#endif
	cpp = (char **)stack;
	
	if (copyout(&argc,cpp++,sizeof(argc)))
	    goto exec_abort;
#ifdef EXEC_DEBUG
	printf("0x%x     \targ count: %d\n", (cpp - 1), argc);
#endif
	dp = (char *)(cpp + argc + envc + 2);

	arginfo.ps_argvstr = dp; /* remember location of argv for later */
	for (sp = argp; --argc >= 0; sp += len, dp += len) {
	    len = strlen(sp) + 1;
	    if (copyout(&dp,cpp++,sizeof(dp))
		|| copyoutstr(sp,dp,len,0))
		goto exec_abort;
#ifdef EXEC_DEBUG
	    exec_print_argstring(sp, len, (cpp - 1), dp);
#endif
	}
	np = 0;
	if (copyout(&np,cpp++,sizeof(np)))
	    goto exec_abort;

	arginfo.ps_envstr = dp; /* remember location of env for later */
	for (; --envc >= 0; sp += len, dp += len) {
	    len = strlen(sp) + 1;
	    if (copyout(&dp,cpp++,sizeof(dp))
		|| copyoutstr(sp,dp,len,0))
		goto exec_abort;
#ifdef EXEC_DEBUG
	    exec_print_argstring(sp, len, (cpp - 1), dp);
#endif
	}
	if (copyout(&np,cpp,sizeof(np)))
	    goto exec_abort;

	/* copy out the process's ps_strings structure */
	if (copyout(&arginfo, (char *)PS_STRINGS, sizeof(arginfo)))
	    goto exec_abort;

#ifdef COPY_SIGCODE
        /* copy out the process's signal trapoline code */
        if (copyout((char *)sigcode, ((char *)PS_STRINGS) - szsigcode,
		    szsigcode)) {
	  goto exec_abort;
	}
#endif

	fdcloseexec(p); /* handle close on exec */
	execsigs(p);    /* reset catched signals */

	/* set command name & other accounting info */
	len = MIN(nid.ni_namelen,MAXCOMLEN);
	bcopy(nid.ni_ptr,p->p_comm,len);
	p->p_comm[len] = 0;
	p->p_acflag &= ~AFORK;
	
	p->p_flag |= SEXEC;
	if (p->p_flag&SPPWAIT) {
	    p->p_flag &= ~SPPWAIT;
	    wakeup((caddr_t)p->p_pptr);
	}
	
	/*
	 *deal with set[ug]id
	 * MNT_NOEXEC and STRC have already been used to disable s[ug]id
	 */
	if (attr.va_mode&VSUID) {
	    p->p_ucred = crcopy(cred);
	    p->p_ucred->cr_uid = attr.va_uid;
	}
	p->p_cred->p_svuid = p->p_ucred->cr_uid;
	if (attr.va_mode&VSGID) {
	    p->p_ucred = crcopy(p->p_ucred);
	    p->p_ucred->cr_gid = attr.va_gid;
	}
	p->p_cred->p_svgid = p->p_ucred->cr_gid;
	/* what else? */
	
/* was: kmem_free_wakeup(exec_map,argp,ARG_MAX); - cgd */
	FREE(argp, M_EXEC);
	
	FREE(nid.ni_pnbuf,M_NAMEI);
	VOP_CLOSE(vp,FREAD,cred,p);
	vput(vp);
	
	/* setup new registers */
	setregs(p, hdr.a_entry, (u_long) stack, retval);
#ifdef EXEC_DEBUG
	printf("exec returning normally\n");
#endif

	if (p->p_flag & STRC)
		psignal(p, SIGTRAP);

	return EJUSTRETURN;
	
bad:
	FREE(nid.ni_pnbuf,M_NAMEI);
	VOP_CLOSE(vp,FREAD,cred,p);
	vput(vp);
#ifdef	FDSCRIPTS
	if (fd)
	    closefd(p,fd);
#endif
freeargs:
/* was:	kmem_free_wakeup(exec_map,argp,ARG_MAX); - cgd */
	FREE(argp, M_EXEC);
#ifdef EXEC_DEBUG
	printf("exec returning on error\n");
#endif
	return error;

exec_abort:
	/* the old process doesn't exist anymore.  exit gracefully.
	 *
	 * get rid of the (new) address space we have created, if any,
	 * get rid of our namei data and vnode, and exit noting failure
	 */
#ifdef EXEC_DEBUG
	printf("exec bailing out (abort)\n");
#endif
	vm_deallocate(&p->p_vmspace->vm_map,0,USRSTACK);
        FREE(nid.ni_pnbuf,M_NAMEI);
        VOP_CLOSE(vp,FREAD,cred,p);
	vput(vp);
	kexit(p, W_EXITCODE(0, SIGABRT));
	kexit(p, -1);

	/* NOTREACHED */
	return 0;
}

int
exec_runcmds(p, epp)
     struct proc *p;
     struct exec_package *epp;
{
  struct exec_vmcmd *cur = epp->ep_vcp;
  int error;

  while (cur && !(error = (*cur->ev_proc)(p, cur)))
    cur = cur->ev_next;

  return error;
}

int
exec_makecmds(p, epp)
     struct proc *p;
     struct exec_package *epp;
{
  u_long midmag, magic;
  u_short mid;
  int error;

  epp->ep_vcp = NULL;

  midmag = ntohl(epp->ep_execp->a_midmag);
  mid = (midmag >> 16 ) & 0x3ff;
  magic = midmag & 0xffff;

#ifdef EXEC_DEBUG
  printf("exec_makecmds: a_midmag is %x, magic=%x mid=%x\n",
    epp->ep_execp->a_midmag, magic, mid);
#endif

  midmag = mid<<16 | magic;

  switch (midmag) {
  case (MID_MACHINE << 16) | ZMAGIC:
    error = exec_prep_zmagic(p, epp);
    break;
  case (MID_MACHINE << 16) | OMAGIC:
  case (MID_MACHINE << 16) | NMAGIC:
  default:
    error = cpu_exec_makecmds(p, epp);
  }

  if (error && epp->ep_vcp)
    kill_vmcmd(&epp->ep_vcp);

bad:

#ifdef EXEC_DEBUG
  printf("exec_makecmds returning with error = %d\n", error);
#endif
  return error;
}

void
kill_vmcmd(vcpp)
     struct exec_vmcmd **vcpp;
{
  struct exec_vmcmd *cur, *next;

  cur = *vcpp;
  *vcpp = NULL;
  while (cur != NULL) {
    if (cur->ev_vp != NULL)
      vrele(cur->ev_vp);
    next = cur->ev_next;
    FREE(cur, M_EXEC);
    cur = next;
  }
}

inline struct exec_vmcmd *
new_vmcmd(proc, len, addr, vp, offset, prot)
     int (*proc) __P((struct proc *p, struct exec_vmcmd *));
     unsigned long len;
     unsigned long addr;
     struct vnode *vp;
     unsigned long offset;
     unsigned long prot;
{
  struct exec_vmcmd *tmpcmdp;

  MALLOC(tmpcmdp, struct exec_vmcmd *, sizeof(struct exec_vmcmd),
	 M_EXEC, M_WAITOK);
  tmpcmdp->ev_next = NULL;
  tmpcmdp->ev_proc = proc;
  tmpcmdp->ev_len = len;
  tmpcmdp->ev_addr = addr;
  if ((tmpcmdp->ev_vp = vp) != NULL)
    vref(vp);
  tmpcmdp->ev_offset = offset;
  tmpcmdp->ev_prot = prot;

  return tmpcmdp;
}

int
exec_prep_zmagic(p, epp)
     struct proc *p;
     struct exec_package *epp;
{
  struct exec *execp = epp->ep_execp;
  struct exec_vmcmd *ccmdp;

  epp->ep_taddr = USRTEXT;
  epp->ep_tsize = execp->a_text;
  epp->ep_daddr = epp->ep_taddr + execp->a_text;
  epp->ep_dsize = execp->a_data + execp->a_bss;
  epp->ep_maxsaddr = USRSTACK - MAXSSIZ;
  epp->ep_minsaddr = USRSTACK;
  epp->ep_ssize = p->p_rlimit[RLIMIT_STACK].rlim_cur;
  epp->ep_entry = execp->a_entry;

  /* check if vnode is in open for writing, because we want to demand-page
   * out of it.  if it is, don't do it, for various reasons
   */
  if ((execp->a_text != 0 || execp->a_data != 0) &&
      (epp->ep_vp->v_flag & VTEXT) == 0 && epp->ep_vp->v_writecount != 0) {
#ifdef DIAGNOSTIC
    if (epp->ep_vp->v_flag & VTEXT)
      panic("exec: a VTEXT vnode has writecount != 0\n");
#endif
    epp->ep_vcp = NULL;
    return ETXTBSY;
  }
  epp->ep_vp->v_flag |= VTEXT;

  /* set up command for text segment */
  epp->ep_vcp = new_vmcmd(vmcmd_map_pagedvn,
			  execp->a_text,
			  epp->ep_taddr,
			  epp->ep_vp,
			  0,
			  VM_PROT_READ|VM_PROT_EXECUTE);
  ccmdp = epp->ep_vcp;

  /* set up command for data segment */
  ccmdp->ev_next = new_vmcmd(vmcmd_map_pagedvn,
			     execp->a_data,
			     epp->ep_daddr,
			     epp->ep_vp,
			     execp->a_text,
			     VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
  ccmdp = ccmdp->ev_next;

  /* set up command for bss segment */
  ccmdp->ev_next = new_vmcmd(vmcmd_map_zero,
			     execp->a_bss,
			     epp->ep_daddr + execp->a_data,
			     0,
			     0,
			     VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
  ccmdp = ccmdp->ev_next;

  /* set up commands for stack.  note that this takes *two*, one
   * to map the part of the stack which we can access, and one
   * to map the part which we can't.
   *
   * arguably, it could be made into one, but that would require
   * the addition of another mapping proc, which is unnecessary
   *
   * note that in memory, thigns assumed to be:
   *    0 ....... ep_maxsaddr <stack> ep_minsaddr
   */
  ccmdp->ev_next = new_vmcmd(vmcmd_map_zero,
			     ((epp->ep_minsaddr - epp->ep_ssize) -
			      epp->ep_maxsaddr),
			     epp->ep_maxsaddr,
			     0,
			     0,
			     VM_PROT_NONE);
  ccmdp = ccmdp->ev_next;
  ccmdp->ev_next = new_vmcmd(vmcmd_map_zero,
			     epp->ep_ssize,
			     (epp->ep_minsaddr - epp->ep_ssize),
			     0,
			     0,
			     VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);

  return 0;
}

int vmcmd_map_pagedvn(p,cmd )
     struct proc *p;
     struct exec_vmcmd *cmd;
{
  /* note that if you're going to map part of an process as being paged from
   * a vnode, that vnode had damn well better be marked as VTEXT.  that's
   * handled in the routine which sets up the vmcmd to call this routine.
   */
#ifdef EXEC_DEBUG
  printf("vmcmd_map_pagedvn: mapping into addr %x for len %x (%x/%x)\n",
	 cmd->ev_addr, cmd->ev_len, cmd->ev_prot, VM_PROT_DEFAULT);
#endif
  return vm_mmap(&p->p_vmspace->vm_map,
		 &cmd->ev_addr,
		 cmd->ev_len,
		 cmd->ev_prot,
		 VM_PROT_DEFAULT,
		 MAP_FIXED|MAP_FILE|MAP_COPY,
		 cmd->ev_vp,
		 cmd->ev_offset);
}

int vmcmd_map_zero(p, cmd)
     struct proc *p;
     struct exec_vmcmd *cmd;
{
  int  error;

#ifdef EXEC_DEBUG
  printf("vmcmd_map_zero: mapping into addr %x for len %x (%x/%x)\n",
	 cmd->ev_addr, cmd->ev_len, cmd->ev_prot, VM_PROT_DEFAULT);
#endif
  error = vm_allocate(&p->p_vmspace->vm_map,
		      &cmd->ev_addr,
		      cmd->ev_len,
		      0);
  if (error)
    return error;

  return vm_protect(&p->p_vmspace->vm_map,
			cmd->ev_addr,
			cmd->ev_len,
			FALSE,
			cmd->ev_prot);
}