* Copyright (c) 1992 William Jolitz. All rights reserved.
* Written by William Jolitz 1/92
* Redistribution and use in source and binary forms are freely permitted
* provided that the above copyright notice and attribution and date of work
* and this paragraph are duplicated in all such forms.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* This procedure implements a minimal program execution facility for
* 386BSD. It interfaces to the BSD kernel as the execve system call.
* Significant limitations and lack of compatiblity with POSIX are
* present with this version, to make its basic operation more clear.
static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_execve.c,v 1.3 92/01/21 21:29:13 william Exp $";
* Bill's first-cut execve() system call. Puts hair on your chest.
register struct nameidata *ndp;
char **kargbuf, **kargbufp, *kstringbuf, *kstringbufp;
char **org, **vectp, *ep;
u_int needsenv, limitonargs, stringlen;
int tsize, dsize, bsize, cnt, foff;
* Step 1. Lookup filename to see if we have something to execute.
ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
ndp->ni_segflg = UIO_USERSPACE;
ndp->ni_dirp = uap->fname;
/* is it a regular file? */
if (ndp->ni_vp->v_type != VREG) {
rv = VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p);
rv = vn_stat(ndp->ni_vp, &statb, p);
* Step 2. Does the file contain a format we can
rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&hdr, sizeof(hdr),
0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p);
/* big enough to hold a header? */
if (hdr.a_magic != ZMAGIC)
/* sanity check "ain't not such thing as a sanity clause" -groucho */
if (/*hdr.a_text == 0 || */ hdr.a_text > MAXTSIZ
|| hdr.a_text % NBPG || hdr.a_text > statb.st_size)
if (hdr.a_data == 0 || hdr.a_data > DFLDSIZ
|| hdr.a_data > statb.st_size
|| hdr.a_data + hdr.a_text > statb.st_size)
if (hdr.a_text + hdr.a_data + hdr.a_bss > MAXTSIZ + MAXDSIZ)
* Step 3. File and header are valid. Now, dig out the strings
* out of the old process image.
/* assumption: most execve's have less than 256 arguments, with a
* total of string storage space not exceeding 2K. It is more
* frequent that when this fails, string space falls short first
* (e.g. as when a large termcap environment variable is present).
* It is infrequent when more than 256 arguments are used that take
* up less than 2K of space (e.g. args average more than 8 chars).
* What we give up in this implementation is a dense encoding of
* the data structure in the receiving program's address space.
* This means that there is plenty of wasted space (up to 6KB)
* as the price we pay for a fast, single pass algorithm.
* Our alternative would be to accumulate strings and pointers
* in the first pass, then, knowing the sizes and number of the
* strings, pack them neatly and tightly togeither in the second
* pass. This means two copies of the strings, and string copying
* is much of the cost of exec.
/* allocate string buffer and arg buffer */
org = kargbuf = (char **) kmem_alloc_wait(exec_map,
(NCARGS + PAGE_SIZE)/PAGE_SIZE);
kstringbuf = kstringbufp = ((char *)kargbuf) + NBPG/2;
kargbuf += NBPG/(4*sizeof(int));
/* for each envp, copy in string */
if(vectp == 0) goto dont_bother;
/* did we outgrow initial argbuf, if so, die */
if (kargbufp == (char **)kstringbuf)
/* get an string pointer */
ep = (char *)fuword(vectp++);
/* if not null pointer, copy in string */
if (rv = copyinstr(ep, kstringbufp, limitonargs,
&stringlen)) goto exec_fail;
/* assume that strings usually all fit in last page */
*kargbufp = (char *)(kstringbufp - kstringbuf
+ USRSTACK - NBPG + NBPG/2);
kstringbufp += stringlen;
limitonargs -= stringlen + sizeof(long);
limitonargs -= sizeof(long);
} while (limitonargs > 0);
kargbuf[-1] = (char *)argc;
* Step 4. Build the new processes image.
/* At this point, we are committed -- destroy old executable */
/* blow away all address space */
rv = vm_deallocate(&vs->vm_map, addr, size, FALSE);
/* build a new address space */
hdr.a_data += hdr.a_text;
tsize = roundup(hdr.a_text, NBPG);
dsize = roundup(hdr.a_data, NBPG);
bsize = roundup(hdr.a_bss + dsize, NBPG);
rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL,
MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff);
rv = vm_protect(&vs->vm_map, addr, tsize, FALSE, VM_PROT_READ|VM_PROT_EXECUTE);
/* create anonymous memory region for bss */
rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE);
/* create anonymous memory region for stack */
addr = USRSTACK - MAXSSIZ;
rv = vm_allocate(&vs->vm_map, &addr, MAXSSIZ, FALSE);
* Step 5. Prepare process for execution.
/* touchup process information */
vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */
vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */
vs->vm_ssize = MAXSSIZ/NBPG; /* stack size (pages) */
vs->vm_taddr = 0; /* user virtual address of text XXX */
vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */
/* user VA at max stack growth */
vs->vm_maxsaddr = (caddr_t)(USRSTACK - MAXSSIZ);
/* everything fits in a single page, no fixups, no more work */
/* (groan) due to bug in vm_map_copy, can't remap. copy for now. */
rv = copyout((caddr_t)org, (caddr_t)USRSTACK - NBPG, NBPG);
/* close files on exec, fixup signals */
p->p_regs[SP] = USRSTACK - NBPG + NBPG/4 - 4;
vs->vm_ssize = 1; /* stack size (pages) */
kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
/* untested and probably bogus */
kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
exit(p, W_EXITCODE(0, SIGABRT));