* Copyright (c) 1989, 1990, 1991, 1992 William F. Jolitz, TeleMuse
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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 software is a component of "386BSD" developed by
* William F. Jolitz, TeleMuse.
* 4. Neither the name of the developer nor the name "386BSD"
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
* AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
* SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
* THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
* NOT MAKE USE OF THIS WORK.
* FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
* BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
* REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
* (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
* JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
* LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
* ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
* OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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
* 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.
extern int dostacklimits
;
#define copyinoutstr copyinstr
register struct nameidata
*ndp
;
char **argbuf
, **argbufp
, *stringbuf
, *stringbufp
;
int needsenv
, limitonargs
, stringlen
, addr
, size
, len
,
rv
, amt
, argc
, tsize
, dsize
, bsize
, cnt
, foff
;
* Step 1. Lookup filename to see if we have something to execute.
ndp
->ni_nameiop
= LOOKUP
| LOCKLEAF
| FOLLOW
| SAVENAME
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
/* does it have any attributes? */
rv
= VOP_GETATTR(ndp
->ni_vp
, &attr
, p
->p_ucred
, p
);
/* is it executable, and a regular file? */
if ((attr
.va_mode
& VEXEC
) == 0 || attr
.va_type
!= VREG
) {
* 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? */
/* ... that we recognize? */
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
> attr
.va_size
)
if (hdr
.a_data
== 0 || hdr
.a_data
> DFLDSIZ
|| hdr
.a_data
> attr
.va_size
|| hdr
.a_data
+ hdr
.a_text
> attr
.va_size
)
if (hdr
.a_text
+ hdr
.a_data
+ hdr
.a_bss
> MAXTSIZ
+ MAXDSIZ
)
if (hdr
.a_data
+ hdr
.a_bss
> p
->p_rlimit
[RLIMIT_DATA
].rlim_cur
)
if (hdr
.a_entry
> hdr
.a_text
+ hdr
.a_data
)
* Step 3. File and header are valid. Now, dig out the strings
* out of the old process image.
* We implement a single-pass algorithm that builds a new stack
* frame within the address space of the "old" process image,
* avoiding the second pass entirely. Thus, the new frame is
* in position to be run. This consumes much virtual address space,
* and two pages more of 'real' memory, such are the costs.
* [Also, note the cache wipe that's avoided!]
/* create anonymous memory region for new stack */
if ((unsigned)vs
->vm_maxsaddr
+ MAXSSIZ
< USRSTACK
)
newframe
= (caddr_t
) USRSTACK
- MAXSSIZ
;
vs
->vm_maxsaddr
= newframe
= (caddr_t
) USRSTACK
- 2*MAXSSIZ
;
/* don't do stack limit checking on traps temporarily XXX*/
rv
= vm_allocate(&vs
->vm_map
, &newframe
, MAXSSIZ
, FALSE
);
/* allocate string buffer and arg buffer */
argbuf
= (char **) (newframe
+ MAXSSIZ
- 3*ARG_MAX
);
stringbuf
= stringbufp
= ((char *)argbuf
) + 2*ARG_MAX
;
if(vectp
== 0) goto dont_bother
;
/* for each envp, copy in string */
/* did we outgrow initial argbuf, if so, die */
if (argbufp
== (char **)stringbuf
) {
/* get an string pointer */
ep
= (char *)fuword(vectp
++);
/* if not a null pointer, copy string */
if (rv
= copyinoutstr(ep
, stringbufp
,
(u_int
)limitonargs
, (u_int
*) &stringlen
)) {
suword(argbufp
++, (int)stringbufp
);
limitonargs
-= stringlen
;
} while (limitonargs
> 0);
/* have we done the environment yet ? */
/* remember the arg count for later */
/* At this point, one could optionally implement a
* second pass to condense the strings, arguement vectors,
* and stack to fit the fewest pages.
* One might selectively do this when copying was cheaper
* than leaving allocated two more pages per process.
/* stuff arg count on top of "new" stack */
argbuf
[-1] = (char *)argc
;
* Step 4. Build the new processes image.
* At this point, we are committed -- destroy old executable!
/* blow away all address space, except the stack */
rv
= vm_deallocate(&vs
->vm_map
, 0, USRSTACK
- 2*MAXSSIZ
);
/* destroy "old" stack */
if ((unsigned)newframe
< USRSTACK
- MAXSSIZ
) {
rv
= vm_deallocate(&vs
->vm_map
, USRSTACK
- MAXSSIZ
, MAXSSIZ
);
rv
= vm_deallocate(&vs
->vm_map
, USRSTACK
- 2*MAXSSIZ
, MAXSSIZ
);
/* build a new address space */
/* screwball mode -- special case of 413 to save space for floppy */
hdr
.a_data
+= hdr
.a_text
;
tsize
= roundup(hdr
.a_text
, NBPG
);
/* treat text and data in terms of integral page size */
dsize
= roundup(hdr
.a_data
, NBPG
);
bsize
= roundup(hdr
.a_bss
+ dsize
, NBPG
);
/* map text & data in file, as being "paged in" on demand */
rv
= vm_mmap(&vs
->vm_map
, &addr
, tsize
+dsize
, VM_PROT_ALL
,
MAP_FILE
|MAP_COPY
|MAP_FIXED
, (caddr_t
)ndp
->ni_vp
, foff
);
/* mark pages r/w data, r/o text */
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
);
* Step 5. Prepare process for execution.
/* touchup process information -- vm system is unfinished! */
vs
->vm_tsize
= tsize
/NBPG
; /* text size (pages) XXX */
vs
->vm_dsize
= (dsize
+bsize
)/NBPG
; /* data size (pages) XXX */
vs
->vm_taddr
= 0; /* user virtual address of text XXX */
vs
->vm_daddr
= (caddr_t
)tsize
; /* user virtual address of data XXX */
vs
->vm_maxsaddr
= newframe
; /* user VA at max stack growth XXX */
vs
->vm_ssize
= ((unsigned)vs
->vm_maxsaddr
+ MAXSSIZ
- (unsigned)argbuf
)/ NBPG
+ 1; /* stack size (pages) */
dostacklimits
= 1; /* allow stack limits to be enforced XXX */
/* close files on exec, fixup signals */
/* name this process - nameiexec(p, ndp) */
len
= MIN(ndp
->ni_namelen
,MAXCOMLEN
);
bcopy(ndp
->ni_ptr
, p
->p_comm
, len
);
/* mark as executable, wakeup any process that was vforked and tell
* it that it now has it's own resources back */
if (p
->p_pptr
&& (p
->p_flag
& SPPWAIT
)) {
/* implement set userid/groupid */
if ((attr
.va_mode
&VSUID
) && (p
->p_flag
& STRC
) == 0) {
p
->p_ucred
= crcopy(p
->p_ucred
);
p
->p_cred
->p_svuid
= p
->p_ucred
->cr_uid
= attr
.va_uid
;
if ((attr
.va_mode
&VSGID
) && (p
->p_flag
& STRC
) == 0) {
p
->p_ucred
= crcopy(p
->p_ucred
);
p
->p_cred
->p_svgid
= p
->p_ucred
->cr_groups
[0] = attr
.va_gid
;
/* setup initial register state */
p
->p_regs
[SP
] = (unsigned) (argbuf
- 1);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
/* if tracing process, pass control back to debugger so breakpoints
can be set before the program "runs" */
/* remove interim "new" stack frame we were building */
vm_deallocate(&vs
->vm_map
, newframe
, MAXSSIZ
);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
/* sorry, no more process anymore. exit gracefully */
vm_deallocate(&vs
->vm_map
, newframe
, MAXSSIZ
);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
exit(p
, W_EXITCODE(0, SIGABRT
));