From: pk@cs.few.eur.nl (Paul Kranenburg)
Newsgroups: comp.unix.bsd
Subject: ptrace
Date: 15 Apr 92 20:27:40 GMT
Reply-To: pk@cs.eur.nl
Organization: Erasmus University Rotterdam

Here is an implementation of the ptrace(2) system call for 386BSD.
If you are only interested in the stock ptrace operations (as currently
defined in <sys/ptrace.h>) the only files that modifications are
 `kern/sys_process.c' and 'kern_execve.c'.

The rest of the diffs attached below contain some changes necessary to
let a debugger attach-to/detach-from a running process, do system call
tracing and trace flags inheritance by debuggee's child processes
(experimental). The files affected are:

	- kern/kern_sig.c
	- kern/kern_exit.c
	- kern/kern_fork.c
	- i386/i386/trap.c
	- sys/proc.h
	- sys/ptrace.h
	- sys/user.h	(!)

(!) Unfortunately, I had to bring back a number of fields to user area
 to support system call tracing (an alternative solution, anyone?)

You may also want to apply a mod to the machine dependent part of gdb.

To demonstrate these ptrace extensions, I have ported `strace' - a symbolic
system call tracer originally written for SunOS - to NET-2 BSD. It can be
anonymously FTP'ed from `ftp.cs.few.eur.nl' (~ftp/pub/386bsd/strace.tar.Z).

I am also looking into the feasibility of implementing a /proc filesystem.
Are there plans for such things in future BSD releases?

Cheers,

Paul.


[PS. the file `kern_execve.c' is included in full below, due to the numerous
incompatible patches floating around. It contains a number of other
rationalisations as well.]

---------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  kern.diff i386.diff sys.diff kern_execve.c gdb.diff
# Wrapped by pk@bridei on Wed Apr 15 21:00:17 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'kern.diff' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'kern.diff'\"
else
echo shar: Extracting \"'kern.diff'\" \(10619 characters\)
sed "s/^X//" >'kern.diff' <<'END_OF_FILE'
X
X------- kern_fork.c -------
X*** /tmp/da06888	Wed Apr 15 20:48:51 1992
X--- kern_fork.c	Wed Apr 15 20:19:36 1992
X***************
X*** 231,236 ****
X--- 231,246 ----
X  			VREF(p2->p_tracep);
X  	}
X  #endif
X+ #ifdef SFTRC
X+ 	if ((p1->p_flag & (STRC|SFTRC)) == (STRC|SFTRC)) {
X+ 		/* Inherit trace flags */
X+ 		p2->p_flag |= (p1->p_flag & (STRC|SFTRC|SSTRC));
X+ 		if (p1->p_tptr)
X+ 			p2->p_tptr = p1->p_tptr;
X+ 		else
X+ 			p2->p_tptr = p1->p_pptr;
X+ 	}
X+ #endif
X  
X  #if defined(tahoe)
X  	p2->p_vmspace->p_ckey = p1->p_vmspace->p_ckey; /* XXX move this */
X
X------- kern_exit.c -------
X*** /tmp/da06891	Wed Apr 15 20:48:52 1992
X--- kern_exit.c	Wed Apr 15 20:19:36 1992
X***************
X*** 210,216 ****
X--- 210,221 ----
X  		 * Traced processes are killed
X  		 * since their existence means someone is screwing up.
X  		 */
X+ #include "ptrace.h"
X+ #ifndef PT_ATTACH
X  		if (q->p_flag&STRC) {
X+ #else
X+ 		if (q->p_flag&STRC && (q->p_tptr == 0 || q->p_tptr == p)) {
X+ #endif
X  			q->p_flag &= ~STRC;
X  			psignal(q, SIGKILL);
X  		}
X***************
X*** 373,380 ****
X--- 378,396 ----
X  			nprocs--;
X  			return (0);
X  		}
X+ 
X  		if (p->p_stat == SSTOP && (p->p_flag & SWTED) == 0 &&
X+ #ifndef PT_ATTACH
X  		    (p->p_flag & STRC || uap->options & WUNTRACED)) {
X+ #else
X+ 			/*
X+ 			 * Don't let natural father have it (yet), if this
X+ 			 * process is being traced by someone else
X+ 			 */
X+ 		    ((p->p_flag & STRC || uap->options & WUNTRACED) &&
X+ 			(p->p_tptr == 0 || p->p_tptr == q))) {
X+ #endif
X+ 
X  			p->p_flag |= SWTED;
X  			retval[0] = p->p_pid;
X  #ifdef COMPAT_43
X***************
X*** 392,397 ****
X--- 408,443 ----
X  			return (error);
X  		}
X  	}
X+ 
X+ #ifdef PT_ATTACH
X+ 	/*if (nfound == 0)*/
X+ 		/* Look for bastards */
X+ 		for (p = allproc; p; p = p->p_nxt) {
X+ 			if (uap->pid != WAIT_ANY &&
X+ 			    p->p_pid != uap->pid && p->p_pgid != -uap->pid ||
X+ 			    p->p_tptr != q || (p->p_flag & STRC) == 0)
X+ 				continue;
X+ 
X+ 			nfound++;
X+ 			if (p->p_stat == SSTOP && (p->p_flag & SWTED) == 0) {
X+ 				p->p_flag |= SWTED;
X+ 				retval[0] = p->p_pid;
X+ #ifdef COMPAT_43
X+ 				if (uap->compat) {
X+ 					retval[1] = W_STOPCODE(p->p_xstat);
X+ 					error = 0;
X+ 				} else
X+ #endif
X+ 				if (uap->status) {
X+ 					status = W_STOPCODE(p->p_xstat);
X+ 					error = copyout((caddr_t)&status,
X+ 					    (caddr_t)uap->status, sizeof(status));
X+ 				} else
X+ 					error = 0;
X+ 				return (error);
X+ 			}
X+ 		}
X+ #endif
X  	if (nfound == 0)
X  		return (ECHILD);
X  	if (uap->options & WNOHANG) {
X
X------- kern_sig.c -------
X*** /tmp/da06894	Wed Apr 15 20:48:52 1992
X--- kern_sig.c	Wed Apr 15 20:19:37 1992
X***************
X*** 55,60 ****
X--- 55,61 ----
X  #include "vm/vm.h"
X  #include "kinfo_proc.h"
X  #include "user.h"		/* for coredump */
X+ #include "ptrace.h"
X  
X  /*
X   * Can process p, with pcred pc, send the signal signo to process q?
X***************
X*** 798,803 ****
X--- 799,809 ----
X  			 * stopped until released by the parent.
X  			 */
X  			p->p_xstat = sig;
X+ #ifdef PT_ATTACH
X+ 			if (p->p_tptr)
X+ 				psignal(p->p_tptr, SIGCHLD);
X+ 			else
X+ #endif
X  			psignal(p->p_pptr, SIGCHLD);
X  			do {
X  				stop(p);
X***************
X*** 909,914 ****
X--- 915,925 ----
X  
X  	p->p_stat = SSTOP;
X  	p->p_flag &= ~SWTED;
X+ #ifdef PT_ATTACH
X+ 	if (p->p_tptr)
X+ 		wakeup((caddr_t)p->p_tptr);
X+ 	else
X+ #endif
X  	wakeup((caddr_t)p->p_pptr);
X  }
X  
X
X------- sys_process.c -------
X*** /tmp/da06920	Wed Apr 15 20:51:42 1992
X--- sys_process.c	Wed Apr 15 20:37:40 1992
X***************
X*** 48,53 ****
X--- 48,99 ----
X  #include "user.h"
X  
X  /*
X+  * NOTES.
X+  *
X+  * The following ptrace calls have been defined in addition to
X+  * the standard ones found in original <sys/ptrace.h>:
X+  *
X+  * PT_ATTACH		-	attach to running process
X+  * PT_DETACH		-	detach from running process
X+  * PT_SYSCALL		-	trace system calls
X+  * PT_GETREG		-	get register file
X+  * PT_SETREG		-	set register file
X+  * PT_BREAD_[IDU]	-	block read from process (not yet implemented)
X+  * PT_BWRITE_[IDU]	-	block write	"		"
X+  * PT_INHERIT		-	make forked processes inherit trace flags
X+  *
X+  */
X+ 
X+ /* Define to prevent extraneous clutter in source */
X+ #ifndef SSTRC
X+ #define	SSTRC	0
X+ #endif
X+ #ifndef SFTRC
X+ #define	SFTRC	0
X+ #endif
X+ 
X+ /*
X+  * `ipcreg' defined in <machine/reg.h>
X+  * Should we define a structure with all regs?
X+  */
X+ int sipcreg[NIPCREG] =
X+   { 0,0,sEDI,sESI,sEBP,sEBX,sEDX,sECX,sEAX,sEIP,sCS,sEFLAGS,sESP,sSS };
X+ 
X+ struct {
X+ 	int	flag;
X+ #define IPC_BUSY	1
X+ #define IPC_WANT	2
X+ #define IPC_DONE	4
X+ 	int	req;			/* copy of ptrace request */
X+ 	int	*addr;			/* copy of ptrace address */
X+ 	int	data;			/* copy of ptrace data */
X+ 	int	error;			/* errno from `procxmt' */
X+ 	int	regs[NIPCREG];		/* PT_[GS]ETREG */
X+ 	caddr_t	buf;			/* PT_BREAD/WRITE */
X+ 	int	buflen;			/* 	"	*/
X+ } ipc;
X+ 
X+ /*
X   * Process debugging system call.
X   */
X  ptrace(curp, uap, retval)
X***************
X*** 60,80 ****
X  	} *uap;
X  	int *retval;
X  {
X  
X! 	/*
X! 	 * Body deleted.
X! 	 */
X! 	return (ENOSYS);
X  }
X  
X  procxmt(p)
X  	register struct proc *p;
X  {
X  
X! 	/*
X! 	 * Body deleted.
X! 	 */
X! 	return (0);
X  }
X  
X  /*
X--- 106,368 ----
X  	} *uap;
X  	int *retval;
X  {
X+ 	struct proc *p;
X+ 	int s, error = 0;
X  
X! 	*retval = 0;
X! 	if (uap->req == PT_TRACE_ME) {
X! 		curp->p_flag |= STRC;
X! 		/*p->p_tptr = p->p_pptr; * What shall we do here ? */
X! 		return 0;
X! 	}
X! 	if ((p = pfind(uap->pid)) == NULL) {
X! 		return ESRCH;
X! 	}
X! 
X! 	if (curp->p_ucred->cr_uid != 0 &&
X! 		(curp->p_ucred->cr_uid != p->p_ucred->cr_uid ||
X! 		curp->p_ucred->cr_uid != p->p_cred->p_svuid)) {
X! 
X! 		return EACCES;
X! 	}
X! 
X! #ifdef PT_ATTACH
X! 	switch (uap->req) {
X! 	case PT_ATTACH:
X! 		p->p_tptr = curp;
X! 		p->p_flag |= STRC;
X! 		psignal(p, SIGTRAP);
X! 		return 0;
X! 
X! 	case PT_DETACH:
X! 		if ((unsigned)uap->data >= NSIG)
X! 			return EINVAL;
X! 		p->p_flag &= ~(STRC|SSTRC|SFTRC);
X! 		p->p_tptr = NULL;
X! 		psignal(p->p_pptr, SIGCHLD);
X! 		wakeup((caddr_t)p->p_pptr);
X! 		s = splhigh();
X! 		if (p->p_stat == SSTOP) {
X! 			p->p_xstat = uap->data;
X! 			setrun(p);
X! 		} else if (uap->data) {
X! 			psignal(p, uap->data);
X! 		}
X! 		splx(s);
X! 		return 0;
X! 
X! #ifdef PT_INHERIT
X! 	case PT_INHERIT:
X! 		if ((p->p_flag & STRC) == 0)
X! 			return ESRCH;
X! 		p->p_flag |= SFTRC;
X! 		return 0;
X! #endif
X! 
X! 	default:
X! 		break;
X! 	}
X! #endif
X! 
X! 	/* Other ptrace calls require target process to be in stopped state */
X! 	if ((p->p_flag & STRC) == 0 || p->p_stat != SSTOP) {
X! 		return ESRCH;
X! 	}
X! 
X! 	/* Acquire the ipc structure */
X! 	while (ipc.flag & IPC_BUSY) {
X! 		ipc.flag |= IPC_WANT;
X! 		error = tsleep((caddr_t)&ipc, PWAIT|PCATCH, "ipc", 0);
X! 		if (error)
X! 			goto out;
X! 	}
X! 
X! 	/* Got it, fill it */
X! 	ipc.flag = IPC_BUSY;
X! 	ipc.error = 0;
X! 	ipc.req = uap->req;
X! 	ipc.addr = uap->addr;
X! 	ipc.data = uap->data;
X! 
X! #ifdef PT_GETREGS
X! 	switch (uap->req) {
X! 	case PT_SETREGS:
X! 		error = copyin((char *)ipc.addr, (char *)ipc.regs, sizeof(ipc.regs));
X! 		if (error)
X! 			goto out;
X! 		break;
X! 
X! #ifdef notyet	/* requires change in number of args to ptrace syscall */
X! 	case PT_BWRITE_I:
X! 	case PT_BWRITE_D:
X! 		ipc.buflen = uap->data;
X! 		ipc.buf = kmem_alloc_wait(kernelmap, uap->data);
X! 		error = copyin((char *)ipc.addr, (char *)ipc.buf, ipc.buflen);
X! 		if (error) {
X! 			kmem_free_wakeup(kernelmap, ipc.buf, ipc.buflen);
X! 			goto out;
X! 		}
X! #endif
X! 	default:
X! 		break;
X! 	}
X! #endif
X! 
X! 	setrun(p);
X! 	while ((ipc.flag & IPC_DONE) == 0) {
X! 		error = tsleep((caddr_t)&ipc, PWAIT|PCATCH, "ipc", 0);
X! 		if (error)
X! 			goto out;
X! 	}
X! 
X! 	*retval = ipc.data;
X! 	if (error = ipc.error)
X! 		goto out;
X! 
X! #ifdef PT_GETREGS
X! 	switch (uap->req) {
X! 	case PT_GETREGS:
X! 		error = copyout((char *)ipc.regs, (char *)ipc.addr, sizeof(ipc.regs));
X! 		break;
X! 
X! 	case PT_BREAD_I:
X! 	case PT_BREAD_D:
X! 		/* Not yet */
X! 	default:
X! 		break;
X! 	}
X! #endif
X! 
X! out:
X! 	/* Release ipc structure */
X! 	ipc.flag &= ~IPC_BUSY;
X! 	if (ipc.flag & IPC_WANT) {
X! 		ipc.flag &= ~IPC_WANT;
X! 		wakeup((caddr_t)&ipc);
X! 	}
X! 	return error;
X  }
X  
X  procxmt(p)
X  	register struct proc *p;
X  {
X+ 	int i, *xreg, rv = 0;
X  
X! 	/* Are we still being traced? */
X! 	if ((p->p_flag & STRC) == 0)
X! 		return 1;
X! 
X! 	p->p_addr->u_kproc.kp_proc = *p;
X! 	fill_eproc(p, &p->p_addr->u_kproc.kp_eproc);
X! 
X! 	switch (ipc.req) {
X! 	case PT_READ_I:
X! 	case PT_READ_D:
X! 		rv = copyin((char *)ipc.addr, (char *)&ipc.data, sizeof(ipc.data));
X! 		break;
X! 
X! 	case PT_READ_U:
X! 		if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) {
X! 			ipc.error = EFAULT;
X! 			break;
X! 		}
X! 		ipc.data = *(int *)((u_int)p->p_addr + (u_int)ipc.addr);
X! 		break;
X! 
X! 	case PT_WRITE_I:
X! 	case PT_WRITE_D:
X! 		rv = copyout((char *)&ipc.data, (char *)ipc.addr, sizeof(ipc.data));
X! 		break;
X! 
X! 	case PT_WRITE_U:
X! 		if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) {
X! 			ipc.error = EFAULT;
X! 			break;
X! 		}
X! 		*(int *)((u_int)p->p_addr + (u_int)ipc.addr) = ipc.data;
X! 		break;
X! 
X! 	case PT_CONTINUE:
X! 		if (ipc.addr != (int *)1) {
X! #ifdef i386
X! 			p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
X! #endif
X! 		}
X! 		p->p_flag &= ~SSTRC;	/* Only set by PT_SYSCALL */
X! 		if ((unsigned)ipc.data >= NSIG) {
X! 			ipc.error = EINVAL;
X! 		} else {
X! 			p->p_xstat = ipc.data;
X! 			rv = 1;
X! 		}
X! 		break;
X! 
X! 	case PT_KILL:
X! 		p->p_flag &= ~SSTRC;	/* Only set by PT_SYSCALL */
X! 		rv = 2;
X! 		break;
X! 
X! 	case PT_STEP:
X! #ifdef i386
X! 		if (ipc.addr != (int *)1) {
X! 			p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
X! 		}
X! 		p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEFLAGS:sEFLAGS] |= PSL_T;
X! #endif
X! 		p->p_flag &= ~SSTRC;	/* Only set by PT_SYSCALL */
X! 		p->p_xstat = 0;
X! 		rv = 1;
X! 		break;
X! 
X! #ifdef PT_SYSCALL
X! 	case PT_SYSCALL:
X! 		if (ipc.addr != (int *)1) {
X! #ifdef i386
X! 			p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
X! #endif
X! 		}
X! 		p->p_flag |= SSTRC;
X! 		p->p_xstat = 0;
X! 		rv = 1;
X! 		break;
X! #endif
X! #ifdef PT_GETREGS
X! 	case PT_GETREGS:
X! #ifdef i386
X! 		xreg = (curpcb->pcb_flags&FM_TRAP)?ipcreg:sipcreg;
X! #endif
X! 
X! 		for (i = 0; i < NIPCREG; i++)
X! 			ipc.regs[i] = p->p_regs[xreg[i]];
X! 		break;
X! 
X! 	case PT_SETREGS:
X! #ifdef i386
X! 		xreg = (curpcb->pcb_flags&FM_TRAP)?ipcreg:sipcreg;
X! #endif
X! 
X! 		for (i = 0; i < NIPCREG; i++)
X! 			p->p_regs[xreg[i]] = ipc.regs[i];
X! 		break;
X! #endif
X! 
X! #ifdef PT_DUMP
X! 	case PT_DUMP:
X! 		/* Should be able to specify core file name */
X! 		ipc.error = coredump(p);
X! 		break;
X! #endif
X! 
X! 	default:
X! 		ipc.error = EINVAL;
X! 	}
X! 	ipc.flag |= IPC_DONE;
X! 	wakeup((caddr_t)&ipc);
X! 
X! 	if (rv == 2)
X! 		exit(p, 0); 	/*???*/
X! 
X! 	return rv;
X  }
X  
X  /*
END_OF_FILE
if test 10619 -ne `wc -c <'kern.diff'`; then
    echo shar: \"'kern.diff'\" unpacked with wrong size!
fi
# end of 'kern.diff'
fi
if test -f 'i386.diff' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'i386.diff'\"
else
echo shar: Extracting \"'i386.diff'\" \(2185 characters\)
sed "s/^X//" >'i386.diff' <<'END_OF_FILE'
X
X------- trap.c -------
X*** /tmp/da06881	Wed Apr 15 20:47:23 1992
X--- trap.c	Wed Apr 15 20:21:35 1992
X***************
X*** 391,397 ****
X--- 391,459 ----
X  #endif
X  	rval[0] = 0;
X  	rval[1] = frame.sf_edx;
X+ #ifdef SSTRC
X+ #ifdef notyet
X+ 	/* We might consider trapping exec calls here in stead of
X+ 	 * of doing a psignal(SIGTRAP) in `kern_execve', like so:
X+ 	 */
X+ 	if (code == SYS_exec)
X+ 		exectrap = 1;
X+ 
X+ again:
X+ 	if (p->p_flag & STRC && (p->p_flag & SSTRC || exectrap)) {
X+ 		if (p->p_flag & SSTRC)
X+ 			exectrap = 0;	/* I will say this only once */
X+ #endif
X+ 	if (p->p_flag & STRC && p->p_flag & SSTRC) {
X+ 		/* Copy args to u. */
X+ 		bcopy((char *)args, (char *)p->p_addr->u_arg, sizeof(args));
X+ 		p->p_addr->u_scno = code;
X+ 		p->p_xstat = SIGTRAP;
X+ 		if (p->p_tptr)
X+ 			psignal(p->p_tptr, SIGCHLD);
X+ 		else
X+ 			psignal(p->p_pptr, SIGCHLD);
X+ 		do {
X+ 			stop(p);
X+ 			swtch();
X+ 		} while (!procxmt(p) && p->p_flag & STRC);
X+ 		p->p_addr->u_scno = 0;
X+ #ifdef notyet
X+ 		if (exectrap) {
X+ 			exectrap = 0;
X+ 			goto again;
X+ 		}
X+ #endif
X+ 	}
X+ #endif
X+ 
X  	error = (*callp->sy_call)(p, args, rval);
X+ 
X+ 	/*
X+ 	 * Reinitialize proc pointer `p' as it may be different
X+ 	 * if this is a child returning from fork syscall.
X+ 	 */
X+ 	p = curproc;
X+ 
X+ #ifdef SSTRC
X+ 	if (p->p_flag & STRC && p->p_flag & SSTRC) {
X+ 		/* Copy args to u. */
X+ 		bcopy((char *)args, (char *)p->p_addr->u_arg, sizeof(args));
X+ 		p->p_addr->u_rval[0] = rval[0];
X+ 		p->p_addr->u_rval[1] = rval[1];
X+ 		p->p_addr->u_error = error;
X+ 		p->p_flag &= ~SSTRC;
X+ 		p->p_xstat = SIGTRAP;
X+ 		if (p->p_tptr)
X+ 			psignal(p->p_tptr, SIGCHLD);
X+ 		else
X+ 			psignal(p->p_pptr, SIGCHLD);
X+ 		do {
X+ 			stop(p);
X+ 			swtch();
X+ 		} while (!procxmt(p) && p->p_flag & STRC);
X+ 	}
X+ #endif
X  	if (error == ERESTART)
X  		frame.sf_eip = opc;
X  	else if (error != EJUSTRETURN) {
X***************
X*** 407,417 ****
X  	/* else if (error == EJUSTRETURN) */
X  		/* nothing to do */
X  done:
X- 	/*
X- 	 * Reinitialize proc pointer `p' as it may be different
X- 	 * if this is a child returning from fork syscall.
X- 	 */
X- 	p = curproc;
X  	while (i = CURSIG(p))
X  		psig(i);
X  	p->p_pri = p->p_usrpri;
X--- 469,474 ----
END_OF_FILE
if test 2185 -ne `wc -c <'i386.diff'`; then
    echo shar: \"'i386.diff'\" unpacked with wrong size!
fi
# end of 'i386.diff'
fi
if test -f 'sys.diff' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sys.diff'\"
else
echo shar: Extracting \"'sys.diff'\" \(2388 characters\)
sed "s/^X//" >'sys.diff' <<'END_OF_FILE'
X
X------- proc.h -------
X*** /tmp/da06994	Wed Apr 15 20:58:11 1992
X--- proc.h	Wed Apr 15 20:57:39 1992
X***************
X*** 101,106 ****
X--- 101,107 ----
X  #define	p_startzero	p_ysptr
X  	struct	proc *p_ysptr;	/* pointer to younger siblings */
X  	struct	proc *p_cptr;	/* pointer to youngest living child */
X+ 	struct	proc *p_tptr;	/* pointer to tracing process */
X  
X  	/* scheduling */
X  	u_int	p_cpu;		/* cpu usage for scheduling */
X***************
X*** 207,212 ****
X--- 208,216 ----
X  #endif
X  /* not currently in use (never set) */
X  #define	SPAGE	0x0020000	/* process in page wait state */
X+ /* More tracing flags */
X+ #define	SSTRC	0x0080000	/* process is tracing syscalls */
X+ #define	SFTRC	0x0100000	/* trace state is inherited on fork */
X  
X  #ifdef KERNEL
X  /*
X
X------- user.h -------
X*** /tmp/da06997	Wed Apr 15 20:58:11 1992
X--- user.h	Wed Apr 15 20:57:39 1992
X***************
X*** 66,71 ****
X--- 66,79 ----
X  	 * not valid at other times!
X  	 */
X  	struct	kinfo_proc u_kproc;	/* proc + eproc */
X+ 
X+ 	/* Support for syscall tracing, can't these be in eproc? */
X+ 	int	u_scno;			/* syscall number */
X+ 	int	u_arg[8];		/* syscall arguments */
X+ 	int	u_rval[2];		/* syscall return values */
X+ #define u_rval1 u_rval[0]		/* compatibility mode */
X+ #define u_rval2 u_rval[1]
X+ 	int	u_error;		/* syscall error */
X  };
X  
X  /*
X
X------- ptrace.h -------
X*** /tmp/da07000	Wed Apr 15 20:58:12 1992
X--- ptrace.h	Wed Apr 15 20:57:39 1992
X***************
X*** 47,52 ****
X--- 47,66 ----
X  #define	PT_KILL		8	/* kill the child process */
X  #define	PT_STEP		9	/* single step the child */
X  
X+ #define PT_ATTACH	10	/* attach to running process */
X+ #define PT_DETACH	11	/* detach from previously attached process */
X+ #define PT_SYSCALL	12	/* trace system calls */
X+ #define PT_DUMP		13	/* make a core dump */
X+ #define PT_BREAD_I	14	/* read data from child's I space */
X+ #define PT_BREAD_D	15	/* read data from child's D space */
X+ #define PT_BWRITE_I	16	/* write data to child's I space */
X+ #define PT_BWRITE_D	17	/* write data to child's D space */
X+ #define PT_GETREGS	18	/* get all registers */
X+ #define PT_SETREGS	19	/* set all registers */
X+ #define PT_GETFPREGS	20	/* get all floating point registers */
X+ #define PT_SETFPREGS	21	/* set all floating point registers */
X+ #define PT_INHERIT	22	/* let tracees children inherit trace flag */
X+ 
X  #ifndef KERNEL
X  
X  #include <sys/cdefs.h>
END_OF_FILE
if test 2388 -ne `wc -c <'sys.diff'`; then
    echo shar: \"'sys.diff'\" unpacked with wrong size!
fi
# end of 'sys.diff'
fi
if test -f 'kern_execve.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'kern_execve.c'\"
else
echo shar: Extracting \"'kern_execve.c'\" \(12055 characters\)
sed "s/^X//" >'kern_execve.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1992 William Jolitz. All rights reserved.
X * Written by William Jolitz 1/92
X *
X * Redistribution and use in source and binary forms are freely permitted
X * provided that the above copyright notice and attribution and date of work
X * and this paragraph are duplicated in all such forms.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X * This procedure implements a minimal program execution facility for
X * 386BSD. It interfaces to the BSD kernel as the execve system call.
X * Significant limitations and lack of compatiblity with POSIX are
X * present with this version, to make its basic operation more clear.
X *
X */
X
X#include "param.h"
X#include "systm.h"
X#include "proc.h"
X#include "mount.h"
X#include "namei.h"
X#include "vnode.h"
X#include "file.h"
X#include "exec.h"
X#include "stat.h"
X#include "wait.h"
X#include "signalvar.h"
X#include "mman.h"
X
X#include "vm/vm.h"
X#include "vm/vm_param.h"
X#include "vm/vm_map.h"
X#include "vm/vm_kern.h"
X
X#include "machine/reg.h"
X
Xstatic char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_execve.c,v 1.3 92/01/21 21:29:13 william Exp $";
X
X/*
X * Bill's first-cut execve() system call. Puts hair on your chest.
X */
X
X/* ARGSUSED */
Xexecve(p, uap, retval)
X	struct proc *p;
X	register struct args {
X		char	*fname;
X		char	**argp;
X		char	**envp;
X	} *uap;
X	int *retval;
X{
X	register struct nameidata *ndp;
X	int rv, amt;
X	struct nameidata nd;
X	struct exec hdr;
X	char **kargbuf, **kargbufp, *kstringbuf, *kstringbufp;
X	char **org, **vectp, *ep;
X	int needsenv, limitonargs;
X	u_int stringlen;
X	int addr, size;
X	int argc;
X	char *cp;
X	struct vmspace *vs;
X	int tsize, dsize, bsize, cnt, foff;
X
X	int indir = 0;
X	register struct pcred *pcred = p->p_cred;
X	register struct ucred *cred ;
X	struct vnode *vp;
X	struct vattr vattr;
X	uid_t uid;
X	gid_t gid;
X	char *shellname;
X	union {
X		char	ex_shell[MAXINTERP];	/* #! and interpreter name */
X		struct	exec ex_exec;
X	} exdata;
X
X	/*
X	 * Step 1. Lookup filename to see if we have something to execute.
X	 */
X	ndp = &nd;
X	ndp->ni_dirp = uap->fname;
X	ndp->ni_segflg = UIO_USERSPACE;
X
Xagain:
X	ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
X
X	/* is it there? */
X	if (rv = namei(ndp, p))
X		return (rv);
X
X	vp = ndp->ni_vp;
X	cred = pcred->pc_ucred;
X	uid = pcred->p_ruid;		/* get orginal uid/gid */
X	gid = pcred->p_rgid;
X
X        if (rv = VOP_GETATTR(vp, &vattr, cred, p))
X		goto exec_fail;
X
X        if (vp->v_mount->mnt_flag & MNT_NOEXEC) {	/* no exec on fs ? */
X		rv = EACCES;
X		goto exec_fail;
X	}
X        if (indir == 0 && (p->p_flag & STRC) == 0 &&
X			(vp->v_mount->mnt_flag & MNT_NOSUID) == 0) {
X		if (vattr.va_mode & VSUID)	/* check for SUID */
X			uid = vattr.va_uid;
X		if (vattr.va_mode & VSGID)	/* check for SGID */
X			gid = vattr.va_gid;
X	}
X
X	/* is it a regular file? */
X	if (vp->v_type != VREG) {
X		rv = EACCES;
X		goto exec_fail;
X	}
X
X	/* is it executable? */
X	if ((vattr.va_mode & VEXEC) == 0) {
X		rv = ENOEXEC;
X		goto exec_fail;
X	}
X
X	/* Can user access it? (always succeeds for root) */
X	if (rv = VOP_ACCESS(vp, VEXEC, cred, p))
X		goto exec_fail;
X	
X
X	/*
X	 * Step 2. Does the file contain a format we can
X	 * understand and execute
X	 */
X	/*
X	 * Read in first few bytes of file for segment sizes, magic number:
X	 *	ZMAGIC = demand paged RO text
X	 * Also an ASCII line beginning with #! is
X	 * the file name of a ``shell'' and arguments may be prepended
X	 * to the argument list if given here.
X	 */
X	exdata.ex_shell[0] = '\0';	/* for zero length files */
X
X	rv = vn_rdwr(UIO_READ, vp, (caddr_t)&exdata, sizeof (exdata),
X	    (off_t)0, UIO_SYSSPACE, (IO_UNIT|IO_NODELOCKED), cred, &amt, p);
X	if (rv)
X		goto exec_fail;
X#ifndef lint
X	if (amt > sizeof(exdata) - sizeof(exdata.ex_exec) &&
X	    exdata.ex_shell[0] != '#') {
X		rv = ENOEXEC;
X		goto exec_fail;
X	}
X#endif
X	/* that we recognize? */
X	rv = ENOEXEC;
X	if ((int)exdata.ex_exec.a_magic == ZMAGIC) {
X
X		/* sanity check  "ain't not such thing as a sanity clause" -groucho */
X		if (	/*exdata.ex_exec.a_text == 0 || */
X			exdata.ex_exec.a_text > MAXTSIZ ||
X			exdata.ex_exec.a_text % NBPG ||
X			exdata.ex_exec.a_text > vattr.va_size)
X			goto exec_fail;
X
X		if (	exdata.ex_exec.a_data == 0 ||
X			exdata.ex_exec.a_data > DFLDSIZ ||
X			exdata.ex_exec.a_data > vattr.va_size ||
X			exdata.ex_exec.a_data + exdata.ex_exec.a_text > vattr.va_size)
X			goto exec_fail;
X
X		if (exdata.ex_exec.a_bss > MAXDSIZ)
X			goto exec_fail;
X	
X		if (exdata.ex_exec.a_text + exdata.ex_exec.a_data +
X				exdata.ex_exec.a_bss > MAXTSIZ + MAXDSIZ)
X			goto exec_fail;
X
X	} else {
X
X		if (exdata.ex_shell[0] != '#' ||
X		    exdata.ex_shell[1] != '!' ||
X		    indir) {
X			rv = ENOEXEC;
X			goto exec_fail;
X		}
X		for (cp = &exdata.ex_shell[2];; ++cp) {
X			if (cp >= &exdata.ex_shell[MAXINTERP]) {
X				rv = ENOEXEC;
X				goto exec_fail;
X			}
X			if (*cp == '\n') {
X				*cp = '\0';
X				break;
X			}
X			if (*cp == '\t')
X				*cp = ' ';
X		}
X		cp = &exdata.ex_shell[2];	/* get shell interpreter name */
X		while (*cp == ' ')
X			cp++;
X		shellname = ndp->ni_dirp = cp;
X		while (*cp && *cp != ' ')
X			cp++;
X		if (*cp)
X			*cp++ = '\0';
X
X		indir = 1;	/* indicate this is a script file */
X		vput(vp);	/* find shell interpreter */
X		ndp->ni_segflg = UIO_SYSSPACE;
X		goto again;
X#if 0
X		ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF;
X		if (rv = namei(ndp, p))
X			return (rv);
X		vp = ndp->ni_vp;
X		if (rv = VOP_GETATTR(vp, &vattr, cred, p))
X			goto exec_fail;
X		uid = pcred->p_ruid;	/* shell scripts can't be setuid */
X		gid = pcred->p_rgid;
X#endif
X	}
X	
X	/*
X	 * Step 3.  File and header are valid. Now, dig out the strings
X	 * out of the old process image.
X	 */
X
X	/* assumption: most execve's have less than 256 arguments, with a
X	 * total of string storage space not exceeding 2K. It is more
X	 * frequent that when this fails, string space falls short first
X	 * (e.g. as when a large termcap environment variable is present).
X	 * It is infrequent when more than 256 arguments are used that take
X	 * up less than 2K of space (e.g. args average more than 8 chars).
X	 *
X	 * What we give up in this implementation is a dense encoding of
X	 * the data structure in the receiving program's address space.
X	 * This means that there is plenty of wasted space (up to 6KB)
X	 * as the price we pay for a fast, single pass algorithm.
X	 *
X	 * Our alternative would be to accumulate strings and pointers
X	 * in the first pass, then, knowing the sizes and number of the
X	 * strings, pack them neatly and tightly togeither in the second
X	 * pass. This means two copies of the strings, and string copying
X	 * is much of the cost of exec.
X	 */
X
X	/* allocate string buffer and arg buffer */
X	org = kargbuf = (char **) kmem_alloc_wait(exec_map, NCARGS + PAGE_SIZE);
X	kstringbuf = kstringbufp = ((char *)kargbuf) + NBPG/2;
X	kargbuf += NBPG/(4*sizeof(int));
X	kargbufp = kargbuf;
X				/* for each envp, copy in string */
X	limitonargs = NCARGS;
X	cnt = 0;
X
X	/* first, do (shell name if any then) args */
X	needsenv = 1;
X	if (indir)  {
X		ep = shellname ;
Xtwice:
X	    if (ep) {
X		/* did we outgrow initial argbuf, if so, die */
X		if (kargbufp >= (char **)kstringbuf) {
X			rv = E2BIG;
X			goto exec_fail;
X		}
X	
X		if (rv = copyinstr(ep, kstringbufp, limitonargs,
X			&stringlen)) goto exec_fail;
X		*kargbufp = (char *)(kstringbufp - kstringbuf
X			+ USRSTACK + NBPG/2);
X		kargbufp++;
X		cnt++;
X		kstringbufp += stringlen;
X		limitonargs -= stringlen + sizeof(long);
X	    }
X
X		if (indir) {
X			indir = 0;
X			ep = uap->fname;	/* orginal executable is 1st
X						   arguement with scripts */
X			goto twice;
X		}
X		if (vectp = uap->argp) vectp++;	/* manually doing the first
X						   arguement with scripts */
X	}
X	else
X		vectp = uap->argp;	/* normal executable */
X
Xdo_env_as_well:
X	if (vectp == 0) goto dont_bother;
X	do {
X		/* did we outgrow initial argbuf, if so, die */
X		if (kargbufp >= (char **)kstringbuf)
X			goto exec_fail;
X	
X		/* get an string pointer */
X		ep = (char *)fuword(vectp++);
X		if (ep == (char *)-1) {
X			rv = EFAULT;
X			goto exec_fail;
X		}
X
X		/* if not null pointer, copy in string */
X		if (ep) {
X			if (rv = copyinstr(ep, kstringbufp, limitonargs,
X				&stringlen)) goto exec_fail;
X			/* assume that strings usually all fit in last page */
X			*kargbufp = (char *)(kstringbufp - kstringbuf
X				+ USRSTACK + NBPG/2);
X			kargbufp++;
X			cnt++;
X			kstringbufp += stringlen;
X			limitonargs -= stringlen + sizeof(long);
X		} else {
X			*kargbufp++ = 0;
X			limitonargs -= sizeof(long);
X			break;
X		}
X	} while (limitonargs > 0);
X
Xdont_bother:
X	if (limitonargs <= 0) {
X		rv = E2BIG;
X		goto exec_fail;
X	}
X
X	if (needsenv) {
X		argc = cnt;
X		vectp = uap->envp;
X		needsenv = 0;
X		goto do_env_as_well;
X	}
X 
X	kargbuf[-1] = (char *)argc;
X
X	/*
X	 * Step 4. Build the new processes image.
X	 */
X
X	/* At this point, we are committed -- destroy old executable */
X
X	/*
X	 * This seems to work for the moment, should really be using `copyin'
X	 * Besides, `namei' already did this, should have a way of culling
X	 * this out of nameidata
X	 */
X	cp = uap->fname + strlen(uap->fname) - 1;
X	while (*cp != '/' && cp > uap->fname) cp--;
X	strncpy(p->p_comm, cp+1, MAXCOMLEN);
X
X	vs = p->p_vmspace;
X	addr = 0;
X	size = USRSTACK - addr;
X	/* blow away all address space */
X	rv = vm_deallocate(&vs->vm_map, addr, size, FALSE);
X
X	/* At this point we release vfork'ed parent, before we blow it */
X	if (p->p_flag & SPPWAIT) {
X		p->p_flag &= ~(SPPWAIT);
X		wakeup((caddr_t)p->p_pptr);
X	}
X	if (rv)
X		goto exec_abort;
X
X	/* build a new address space */
X	addr = 0;
X	if (exdata.ex_exec.a_text == 0) {
X		/* screwball mode */
X		foff = tsize = 0;
X		exdata.ex_exec.a_data += exdata.ex_exec.a_text;
X	} else {
X		tsize = roundup(exdata.ex_exec.a_text, NBPG);
X		foff = NBPG;
X	}
X	dsize = roundup(exdata.ex_exec.a_data, NBPG);
X	bsize = roundup(exdata.ex_exec.a_bss + dsize, NBPG);
X	bsize -= dsize;
X
X	/* map text & data*/
X	rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL,
X	MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff);
X	if (rv)
X		goto exec_abort;
X
X	/* r/w data, ro text */
X	if (tsize) {
X		addr = 0;
X		rv = vm_protect(&vs->vm_map, addr, tsize, FALSE, VM_PROT_READ|VM_PROT_EXECUTE);
X		if (rv)
X			goto exec_abort;
X	}
X
X	/* create anonymous memory region for bss */
X	addr = dsize + tsize;
X	rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE);
X	if (rv)
X		goto exec_abort;
X
X	/* create anonymous memory region for stack */
X	addr = USRSTACK - MAXSSIZ;
X	rv = vm_allocate(&vs->vm_map, &addr, MAXSSIZ, FALSE);
X	if (rv)
X		goto exec_abort;
X
X	/*
X	 * Step 5. Prepare process for execution.
X	 */
X
X	/* touchup process information */
X	vs->vm_tsize = tsize/NBPG;		/* text size (pages) XXX */
X	vs->vm_dsize = (dsize+bsize)/NBPG;	/* data size (pages) XXX */
X	vs->vm_ssize = MAXSSIZ/NBPG;	/* stack size (pages) */
X	vs->vm_taddr = 0;	/* user virtual address of text XXX */
X	vs->vm_daddr = (caddr_t)tsize;	/* user virtual address of data XXX */
X	/* user VA at max stack growth */
X	vs->vm_maxsaddr = (caddr_t)(USRSTACK - MAXSSIZ);
X
X	/* everything fits in a single page, no fixups, no more work */
X	/* (groan) due to bug in vm_map_copy, can't remap. copy for now. */
X	size = kstringbufp + 1 - kstringbuf;
X	size = roundup(size + NBPG/2, NBPG);
X	for (; kargbuf < kargbufp; kargbuf++)
X		if (*kargbuf)
X			*kargbuf -= size;
X	rv = copyout((caddr_t)org, (caddr_t)USRSTACK - size, size);
X	if(rv)
X		goto exec_abort;
X
X	/* close files on exec, fixup signals */
X	fdcloseexec(p);
X	execsigs(p);
X
X	p->p_regs[SP] = USRSTACK - size + NBPG/4 - 4;
X	vs->vm_ssize = size / NBPG;	/* stack size (pages) */
X	setregs(p, exdata.ex_exec.a_entry);
X	kmem_free_wakeup(exec_map, org, NCARGS + PAGE_SIZE);
X	vput(ndp->ni_vp);
X
X	p->p_ucred->cr_uid = p->p_cred->p_svuid = uid;
X	p->p_ucred->cr_gid = p->p_cred->p_svgid = gid;
X
X	/* Notify debugger that interesting things are abound */
X	if (p->p_flag & STRC)
X		psignal(p, SIGTRAP);
X
X	return (0);
X
Xexec_fail:
X	vput(ndp->ni_vp);
X	return(rv);
X
Xexec_abort:
X	/* untested and probably bogus */
X	kmem_free_wakeup(exec_map, org, NCARGS + PAGE_SIZE);
X	vput(ndp->ni_vp);
X	exit(p, W_EXITCODE(0, SIGABRT));
X	return(0);
X
X}
END_OF_FILE
if test 12055 -ne `wc -c <'kern_execve.c'`; then
    echo shar: \"'kern_execve.c'\" unpacked with wrong size!
fi
# end of 'kern_execve.c'
fi
if test -f 'gdb.diff' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'gdb.diff'\"
else
echo shar: Extracting \"'gdb.diff'\" \(898 characters\)
sed "s/^X//" >'gdb.diff' <<'END_OF_FILE'
X
X------- m-i386bsd.h -------
X*** /tmp/da06961	Wed Apr 15 20:55:59 1992
X--- m-i386bsd.h	Wed Apr 15 20:55:17 1992
X***************
X*** 330,335 ****
X--- 330,338 ----
X  #define POP_FRAME  { i386_pop_frame (); }
X  
X  #define	NEW_CALL_FUNCTION
X+ 
X+ #define ATTACH_DETACH
X+ 
X  
X  #if 0
X  /* Interface definitions for kernel debugger KDB.  */
X
X------- i386bsd-dep.c -------
X*** /tmp/da06964	Wed Apr 15 20:55:59 1992
X--- i386bsd-dep.c	Wed Apr 15 20:55:16 1992
X***************
X*** 1552,1558 ****
X  setregmap(flags)
X  	int flags;
X  {
X! #ifdef EX_TRAPSTK
X  	regmap = flags & EX_TRAPSTK ? trapmap : syscallmap;
X  #else
X  	regmap = trapmap;	/* the lesser evil */
X--- 1552,1560 ----
X  setregmap(flags)
X  	int flags;
X  {
X! #ifdef FM_TRAP
X! 	regmap = flags & FM_TRAP ? trapmap: syscallmap;
X! #elif EX_TRAPSTK
X  	regmap = flags & EX_TRAPSTK ? trapmap : syscallmap;
X  #else
X  	regmap = trapmap;	/* the lesser evil */
END_OF_FILE
if test 898 -ne `wc -c <'gdb.diff'`; then
    echo shar: \"'gdb.diff'\" unpacked with wrong size!
fi
# end of 'gdb.diff'
fi
echo shar: End of shell archive.
exit 0