*BSD News Article 20453


Return to BSD News archive

Newsgroups: comp.os.386bsd.development
Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!elroy.jpl.nasa.gov!swrinde!cs.utexas.edu!uunet!majipoor.cygnus.com!kithrup.com!sef
From: sef@kithrup.com (Sean Eric Fagan)
Subject: new ptrace()
Organization: Kithrup Enterprises, Ltd.
Message-ID: <CCwLFM.2C2@kithrup.com>
Date: Sun, 5 Sep 1993 23:04:18 GMT
Lines: 1070

As I said in an earlier posting, I've been rewriting ptrace().  I'm
almost done, now; it works well enough that I'm using it myself, and
it's been added to the netbsd tree.

Three of the changed files are included below as a context diff to
my tree; kern/sys_process.c is a context diff from the freebsd tree.
(I don't think I *have* an unmodified sys_process.c online anymore, so
I grabbed the next best thing.  And, yes, I know, I know, I should use
cvs.  When I upgrade to netbsd, maybe... :))

Some things to note about it:

1.  It no longer uses the traditional method of four context switches
to get at the debugged process; instead, it uses the VM code.  This may
or may not be faster :).  It is, however, somewhat necessary for my
other project(s).

2.  There is *no* 386-specific code in the new ptrace().  It calls four
machine-dependent functions (described below).  This is partially because
I don't like machine-dependent code in supposedly machine-independent
files, but it's mostly to help people doing ports.

3.  There are two new ptrace() commands:  PT_GETREGS and PT_SETREGS.
These get and set the debugged process' registers (obviously :)).  I'm
working on PT_GETFPREGS and PT_SETFPREGS, but bruce evans told me some
stuff about how the fp registers are handled today, and I decided to put
it off for a bit.

4.  The four machine-dependent functions are:  ptrace_single_step(),
ptrace_set_pc(), ptrace_getregs(), and ptrace_setregs().  They should
be defined in a machine-dependent file; I defined them in
i386/i386/machdep.c.  A very brief description of each:

ptrace_single_step(struct proc *);
	Do whatever is necessary to set up the process given to execute
	only a single instruction.  Returns 0 on success, non-zero on
	failure.  (The SPARC port, for example, will most likely return
	EINVAL or something similar, because single-stepping on a sparc
	is a pain and is best done by the debugger.)
ptrace_set_pc(struct proc *, unsigned int);
	Set the PC in the specified process.  The second argument should
	possibly be a void* or caddr_t.  It can return an error, also,
	but it seems unlikely to me :).
ptrace_getregs(struct proc *, unsigned int *);
	Get the registers from the process in question and copy them out
	to the address in question.  Return failure if necessary.
ptrace_setregs(struct proc *, unsigned int *);
	Like above, only set the registers (obviously).  Again, the
	second argument is a pointer to user memory.

I have not yet made changes to gdb to have it use PT_{SET,GET}REGS, so
they've only been tested by a little program I wrote to fork, stop in
the child, and then fetch the registers in the parent.  It had the
right values :).

Anyway, share and enjoy.

*** sys/ptrace.h.~1~	Tue Dec 24 14:24:03 1991
--- sys/ptrace.h	Sat Sep  4 11:08:00 1993
***************
*** 46,51 ****
--- 46,55 ----
  #define	PT_CONTINUE	7	/* continue the child */
  #define	PT_KILL		8	/* kill the child process */
  #define	PT_STEP		9	/* single step the child */
+ /*#define	PT_ATTACH	10	/* attach to running process */
+ /*#define	PT_DETACH	11	/* detach from running process */
+ #define	PT_GETREGS	12	/* fetch registers */
+ #define	PT_SETREGS	13	/* set registers */
  
  #ifndef KERNEL
  
*** i386/include/reg.h.~1~	Tue Dec 24 14:23:24 1991
--- i386/include/reg.h	Sat Sep  4 00:38:36 1993
***************
*** 85,93 ****
  #define	R1	sECX
  /*
   * Registers accessible to ptrace(2) syscall for debugger
   */
! #ifdef IPCREG
! #define	NIPCREG 14
! int ipcreg[NIPCREG] =
!   { tES,tDS,tEDI,tESI,tEBP,tEBX,tEDX,tECX,tEAX,tEIP,tCS,tEFLAGS,tESP,tSS };
! #endif
--- 85,109 ----
  #define	R1	sECX
  /*
   * Registers accessible to ptrace(2) syscall for debugger
+  * The machine-dependent code for PT_{SET,GET}REGS needs to
+  * use whichver order, defined above, is correct, so that it
+  * is all invisible to the user.
   */
! struct regs {
! 	unsigned int	r_es;
! 	unsigned int	r_ds;
! 	unsigned int	r_edi;
! 	unsigned int	r_esi;
! 	unsigned int	r_ebp;
! 	unsigned int	r_ebx;
! 	unsigned int	r_edx;
! 	unsigned int	r_ecx;
! 	unsigned int	r_eax;
! 	unsigned int	r_eip;
! 	unsigned int	r_cs;
! 	unsigned int	r_eflags;
! 	unsigned int	r_esp;
! 	unsigned int	r_ss;
! 	unsigned int	r_fs;
! 	unsigned int	r_gs;
! };
*** i386/i386/machdep.c.~6~	Sun Jul 18 16:25:47 1993
--- i386/i386/machdep.c	Sat Sep  4 00:44:10 1993
***************
*** 1129,1131 ****
--- 1129,1281 ----
  	if(lencopied) *lencopied = tally;
  	return(ENAMETOOLONG);
  }
+ 
+ /*
+  * The registers are in the frame; the frame is in the user area of
+  * the process in question; when the process is active, the registers
+  * are in "the kernel stack"; when it's not, they're still there, but
+  * things get flipped around.  So, since p->p_regs is the whole address
+  * of the register set, take its offset from the kernel stack, and
+  * index into the user block.  Don't you just *love* virtual memory?
+  * (I'm starting to think seymour is right...)
+  */
+ 
+ int
+ ptrace_set_pc (struct proc *p, unsigned int addr) {
+ 	struct pcb *pcb;
+ 	void *regs = (char*)p->p_addr +
+ 		((char*) p->p_regs - (char*) kstack);
+ 
+ 	pcb = &p->p_addr->u_pcb;
+ 	if (pcb->pcb_flags & FM_TRAP)
+ 		((struct trapframe *)regs)->tf_eip = addr;
+ 	else
+ 		((struct syscframe *)regs)->sf_eip = addr;
+ 	return 0;
+ }
+ 
+ int
+ ptrace_single_step (struct proc *p) {
+ 	struct pcb *pcb;
+ 	void *regs = (char*)p->p_addr +
+ 		((char*) p->p_regs - (char*) kstack);
+ 
+ 	pcb = &p->p_addr->u_pcb;
+ 	if (pcb->pcb_flags & FM_TRAP)
+ 		((struct trapframe *)regs)->tf_eflags |= PSL_T;
+ 	else
+ 		((struct syscframe *)regs)->sf_eflags |= PSL_T;
+ 	return 0;
+ }
+ 
+ /*
+  * Copy the registers to user-space.  This is tedious because
+  * we essentially duplicate code for trapframe and syscframe. *sigh*
+  */
+ 
+ int
+ ptrace_getregs (struct proc *p, unsigned int *addr) {
+ 	int error;
+ 	struct trapframe *tp;
+ 	struct syscframe *sp;
+ 	struct pcb *pcb;
+ 	struct regs regs = {0};
+ 	void *ptr = (char*)p->p_addr +
+ 		((char*) p->p_regs - (char*) kstack);
+ 
+ 	pcb = &p->p_addr->u_pcb;
+ 	if (pcb->pcb_flags & FM_TRAP) {
+ 		tp = ptr;
+ 		regs.r_es = tp->tf_es;
+ 		regs.r_ds = tp->tf_ds;
+ 		regs.r_edi = tp->tf_edi;
+ 		regs.r_esi = tp->tf_esi;
+ 		regs.r_ebp = tp->tf_ebp;
+ 		regs.r_ebx = tp->tf_ebx;
+ 		regs.r_edx = tp->tf_edx;
+ 		regs.r_ecx = tp->tf_ecx;
+ 		regs.r_eax = tp->tf_eax;
+ 		regs.r_eip = tp->tf_eip;
+ 		regs.r_cs = tp->tf_cs;
+ 		regs.r_eflags = tp->tf_eflags;
+ 		regs.r_esp = tp->tf_esp;
+ 		regs.r_ss = tp->tf_ss;
+ 	} else {
+ 		sp = ptr;
+ 		/*
+ 		 * No sf_es or sf_ds... dunno why.
+ 		 */
+ 		/*
+ 		 * regs.r_es = sp->sf_es;
+ 		 * regs.r_ds = sp->sf_ds;
+ 		 */
+ 		regs.r_edi = sp->sf_edi;
+ 		regs.r_esi = sp->sf_esi;
+ 		regs.r_ebp = sp->sf_ebp;
+ 		regs.r_ebx = sp->sf_ebx;
+ 		regs.r_edx = sp->sf_edx;
+ 		regs.r_ecx = sp->sf_ecx;
+ 		regs.r_eax = sp->sf_eax;
+ 		regs.r_eip = sp->sf_eip;
+ 		regs.r_cs = sp->sf_cs;
+ 		regs.r_eflags = sp->sf_eflags;
+ 		regs.r_esp = sp->sf_esp;
+ 		regs.r_ss = sp->sf_ss;
+ 	}
+ 	return copyout (&regs, addr, sizeof (regs));
+ }
+ 
+ int
+ ptrace_setregs (struct proc *p, unsigned int *addr) {
+ 	int error;
+ 	struct trapframe *tp;
+ 	struct syscframe *sp;
+ 	struct pcb *pcb;
+ 	struct regs regs = {0};
+ 	void *ptr = (char*)p->p_addr +
+ 		((char*) p->p_regs - (char*) kstack);
+ 
+ 	if (error = copyin (addr, &regs, sizeof(regs)))
+ 		return error;
+ 
+ 	pcb = &p->p_addr->u_pcb;
+ 	if (pcb->pcb_flags & FM_TRAP) {
+ 		tp = ptr;
+ 		tp->tf_es = regs.r_es;
+ 		tp->tf_ds = regs.r_ds;
+ 		tp->tf_edi = regs.r_edi;
+ 		tp->tf_esi = regs.r_esi;
+ 		tp->tf_ebp = regs.r_ebp;
+ 		tp->tf_ebx = regs.r_ebx;
+ 		tp->tf_edx = regs.r_edx;
+ 		tp->tf_ecx = regs.r_ecx;
+ 		tp->tf_eax = regs.r_eax;
+ 		tp->tf_eip = regs.r_eip;
+ 		tp->tf_cs = regs.r_cs;
+ 		tp->tf_eflags = regs.r_eflags;
+ 		tp->tf_esp = regs.r_esp;
+ 		tp->tf_ss = regs.r_ss;
+ 	} else {
+ 		sp = ptr;
+ 		/*
+ 		 * No sf_es or sf_ds members, dunno why...
+ 		 */
+ 		/*
+ 		 * sp->sf_es = regs.r_es;
+ 		 * sp->sf_ds = regs.r_ds;
+ 		 */
+ 		sp->sf_edi = regs.r_edi;
+ 		sp->sf_esi = regs.r_esi;
+ 		sp->sf_ebp = regs.r_ebp;
+ 		sp->sf_ebx = regs.r_ebx;
+ 		sp->sf_edx = regs.r_edx;
+ 		sp->sf_ecx = regs.r_ecx;
+ 		sp->sf_eax = regs.r_eax;
+ 		sp->sf_eip = regs.r_eip;
+ 		sp->sf_cs = regs.r_cs;
+ 		regs.r_eflags = sp->sf_eflags;
+ 		regs.r_esp = sp->sf_esp;
+ 		regs.r_ss = sp->sf_ss;
+ 	}
+ 	return 0;
+ }
*** /tmp/sys_process.c	Sun Sep  5 15:50:44 1993
--- kern/sys_process.c	Sat Sep  4 01:43:56 1993
***************
*** 42,50 ****
   * 08 Apr 93	Bruce Evans		Several VM system fixes
   */
  
- #include <stddef.h>
- 
- #define IPCREG
  #include "param.h"
  #include "proc.h"
  #include "vnode.h"
--- 42,47 ----
***************
*** 51,61 ****
  #include "buf.h"
  #include "ptrace.h"
  
- #include "machine/eflags.h"
  #include "machine/reg.h"
  #include "machine/psl.h"
  #include "vm/vm.h"
  #include "vm/vm_page.h"
  
  #include "user.h"
  
--- 48,58 ----
  #include "buf.h"
  #include "ptrace.h"
  
  #include "machine/reg.h"
  #include "machine/psl.h"
  #include "vm/vm.h"
  #include "vm/vm_page.h"
+ #include "vm/vm_kern.h"
  
  #include "user.h"
  
***************
*** 84,124 ****
  #define	SFTRC	0
  #endif
  
! /*
!  * `ipcreg' defined in <machine/reg.h>
!  * Should we define a structure with all regs?
   */
! int sipcreg[NIPCREG] =
!   { 0,0,sEDI,sESI,sEBP,sEBX,sEDX,sECX,sEAX,sEIP,sCS,sEFLAGS,sESP,sSS };
  
! struct {
! 	int	flag;
! #define IPC_BUSY	1
! #define IPC_WANT	2
! #define IPC_DONE	4
! 	int	req;			/* copy of ptrace request */
! 	int	*addr;			/* copy of ptrace address */
! 	int	data;			/* copy of ptrace data */
! 	int	error;			/* errno from `procxmt' */
! 	int	regs[NIPCREG];		/* PT_[GS]ETREG */
! 	caddr_t	buf;			/* PT_BREAD/WRITE */
! 	int	buflen;			/* 	"	*/
! } ipc;
  
  /*
   * Process debugging system call.
   */
! 
! struct ptrace_args {
  	int	req;
  	int	pid;
  	int	*addr;
  	int	data;
! };
! 
! ptrace(curp, uap, retval)
! 	struct proc *curp;
! 	register struct ptrace_args *uap;
  	int *retval;
  {
  	struct proc *p;
--- 81,237 ----
  #define	SFTRC	0
  #endif
  
! int
! pread (struct proc *procp, unsigned int addr, unsigned int *retval) {
! 	int		rv;
! 	vm_map_t	map, tmap;
! 	vm_object_t	object;
! 	vm_offset_t	kva;
! 	int		page_offset;	/* offset into page */
! 	vm_offset_t	pageno;		/* page number */
! 	vm_map_entry_t	out_entry;
! 	vm_prot_t	out_prot;
! 	boolean_t	wired, single_use;
! 	vm_offset_t	off;
! 
! 	/* Map page into kernel space */
! 
! 	map = &procp->p_vmspace->vm_map;
! 
! 	page_offset = addr - trunc_page(addr);
! 	pageno = trunc_page(addr);
!   
! 	tmap = map;
! 	rv = vm_map_lookup (&tmap, pageno, VM_PROT_READ, &out_entry,
! 		&object, &off, &out_prot, &wired, &single_use);
! 
! 	if (rv != KERN_SUCCESS)
! 		return EINVAL;
!   
! 	vm_map_lookup_done (tmap, out_entry);
!   
! 	/* Find space in kernel_map for the page we're interested in */
! 	rv = vm_map_find (kernel_map, object, off, &kva, PAGE_SIZE, 1);
! 
! 	if (!rv) {
! 		vm_object_reference (object);
! 
! 		rv = vm_map_pageable (kernel_map, kva, kva + PAGE_SIZE, 0);
! 		if (!rv) {
! 			*retval = 0;
! 			bcopy (kva + page_offset, retval, sizeof *retval);
! 		}
! 		vm_map_remove (kernel_map, kva, kva + PAGE_SIZE);
! 	}
! 
! 	return rv;
! }
! 
! int
! pwrite (struct proc *procp, unsigned int addr, unsigned int datum) {
! 	int		rv;
! 	vm_map_t	map, tmap;
! 	vm_object_t	object;
! 	vm_offset_t	kva;
! 	int		page_offset;	/* offset into page */
! 	vm_offset_t	pageno;		/* page number */
! 	vm_map_entry_t	out_entry;
! 	vm_prot_t	out_prot;
! 	boolean_t	wired, single_use;
! 	vm_offset_t	off;
! 	boolean_t	fix_prot = 0;
! 
! 	/* Map page into kernel space */
!   
! 	map = &procp->p_vmspace->vm_map;
!   
! 	page_offset = addr - trunc_page(addr);
! 	pageno = trunc_page(addr);
!   
! 	/*
! 	 * Check the permissions for the area we're interested in.
! 	 */
! 
! 	if (vm_map_check_protection (map, pageno, pageno + PAGE_SIZE,
! 		VM_PROT_WRITE) == FALSE) {
! 		/*
! 		 * If the page was not writable, we make it so.
! 		 * XXX It is possible a page may *not* be read/executable,
! 		 * if a process changes that!
! 		 */
! 		fix_prot = 1;
! 		/* The page isn't writable, so let's try making it so... */
! 		if ((rv = vm_map_protect (map, pageno, pageno + PAGE_SIZE,
! 			VM_PROT_ALL, 0)) != KERN_SUCCESS)
! 		  return EFAULT;	/* I guess... */
! 	}
! 
! 	/*
! 	 * Now we need to get the page.  out_entry, out_prot, wired, and
! 	 * single_use aren't used.  One would think the vm code would be
! 	 * a *bit* nicer...  We use tmap because vm_map_lookup() can
! 	 * change the map argument.
! 	 */
! 
! 	tmap = map;
! 	rv = vm_map_lookup (&tmap, pageno, VM_PROT_WRITE, &out_entry,
! 		&object, &off, &out_prot, &wired, &single_use);
! 	if (rv != KERN_SUCCESS) {
! 		return EINVAL;
! 	}
! 
! 	/*
! 	 * Okay, we've got the page.  Let's release tmap.
! 	 */
! 
! 	vm_map_lookup_done (tmap, out_entry);
!   
! 	/*
! 	 * Fault the page in...
! 	 */
! 
! 	rv = vm_fault (map, pageno, VM_PROT_WRITE, FALSE);
! 	if (rv != KERN_SUCCESS)
! 		return EFAULT;
! 
! 	/*
! 	 * The page may need to be faulted in again, it seems.
! 	 * This covers COW pages, I believe.
  	 */
! 
! 	if (!rv)
! 		rv = vm_fault (map, pageno, VM_PROT_WRITE, 0);
! 
! 	/* Find space in kernel_map for the page we're interested in */
! 	rv = vm_map_find (kernel_map, object, off, &kva, PAGE_SIZE, 1);
! 
! 	if (!rv) {
! 		vm_object_reference (object);
! 
! 		rv = vm_map_pageable (kernel_map, kva, kva + PAGE_SIZE, 0);
! 		if (!rv) {
! 		  bcopy (&datum, kva + page_offset, sizeof datum);
! 		}
! 		vm_map_remove (kernel_map, kva, kva + PAGE_SIZE);
! 	}
    
! 	if (fix_prot)
! 		vm_map_protect (map, pageno, pageno + PAGE_SIZE,
! 			VM_PROT_READ|VM_PROT_EXECUTE, 0);
! 	return rv;
! }
  
  /*
   * Process debugging system call.
   */
! ptrace(curp, uap, retval)
! 	struct proc *curp;
! 	register struct args {
  		int	req;
  		int	pid;
  		int	*addr;
  		int	data;
! 	} *uap;
  	int *retval;
  {
  	struct proc *p;
***************
*** 127,133 ****
  	*retval = 0;
  	if (uap->req == PT_TRACE_ME) {
  		curp->p_flag |= STRC;
- 		/*p->p_tptr = p->p_pptr; * What shall we do here ? */
  		return 0;
  	}
  	if ((p = pfind(uap->pid)) == NULL) {
--- 240,245 ----
***************
*** 134,140 ****
  		return ESRCH;
  	}
  
! #ifdef notyet
  	if (uap->req != PT_ATTACH && (
  			(p->p_flag & STRC) == 0 ||
  			(p->p_tptr && curp != p->p_tptr) ||
--- 246,252 ----
  		return ESRCH;
  	}
  
! #ifdef PT_ATTACH
  	if (uap->req != PT_ATTACH && (
  			(p->p_flag & STRC) == 0 ||
  			(p->p_tptr && curp != p->p_tptr) ||
***************
*** 142,151 ****
  
  		return ESRCH;
  #endif
! 
! 
  #ifdef PT_ATTACH
  	switch (uap->req) {
  	case PT_ATTACH:
  		if (curp->p_ucred->cr_uid != 0 && (
  			curp->p_ucred->cr_uid != p->p_ucred->cr_uid ||
--- 254,278 ----
  
  		return ESRCH;
  #endif
! #ifdef PT_ATTACH
! 	if (uap->req != PT_ATTACH) {
! #endif
! 		if ((p->p_flag & STRC) == 0)
! 			return EPERM;
! 		if (p->p_stat != SSTOP || (p->p_flag & SWTED) == 0)
! 			return EBUSY;
  #ifdef PT_ATTACH
+ 	}
+ #endif
+ 	/*
+ 	 * XXX The PT_ATTACH code is completely broken.  It will
+ 	 * be obsoleted by a /proc filesystem, so is it worth it
+ 	 * to fix it?  (Answer, probably.  So that'll be next,
+ 	 * I guess.)
+ 	 */
+ 
  	switch (uap->req) {
+ #ifdef PT_ATTACH
  	case PT_ATTACH:
  		if (curp->p_ucred->cr_uid != 0 && (
  			curp->p_ucred->cr_uid != p->p_ucred->cr_uid ||
***************
*** 154,160 ****
  
  		p->p_tptr = curp;
  		p->p_flag |= STRC;
! 		psignal(p, SIGTRAP);
  		return 0;
  
  	case PT_DETACH:
--- 281,287 ----
  
  		p->p_tptr = curp;
  		p->p_flag |= STRC;
! 		psignal(p, SIGSTOP);
  		return 0;
  
  	case PT_DETACH:
***************
*** 174,536 ****
  		splx(s);
  		return 0;
  
! #ifdef PT_INHERIT
  	case PT_INHERIT:
  		if ((p->p_flag & STRC) == 0)
  			return ESRCH;
  		p->p_flag |= SFTRC;
  		return 0;
! #endif
! 
! 	default:
! 		break;
! 	}
! #endif
  
! 	/* Other ptrace calls require target process to be in stopped state */
! 	if ((p->p_flag & STRC) == 0 || p->p_stat != SSTOP) {
! 		return ESRCH;
! 	}
  
! 	/* Acquire the ipc structure */
! 	while (ipc.flag & IPC_BUSY) {
! 		ipc.flag |= IPC_WANT;
! 		error = tsleep((caddr_t)&ipc, PWAIT|PCATCH, "ipc", 0);
! 		if (error)
! 			goto out;
  	}
  
! 	/* Got it, fill it */
! 	ipc.flag = IPC_BUSY;
! 	ipc.error = 0;
! 	ipc.req = uap->req;
! 	ipc.addr = uap->addr;
! 	ipc.data = uap->data;
! 
! #ifdef PT_GETREGS
! 	switch (uap->req) {
! 	case PT_SETREGS:
! 		error = copyin((char *)ipc.addr, (char *)ipc.regs, sizeof(ipc.regs));
! 		if (error)
! 			goto out;
! 		break;
  
! #ifdef notyet	/* requires change in number of args to ptrace syscall */
! 	case PT_BWRITE_I:
! 	case PT_BWRITE_D:
! 		ipc.buflen = uap->data;
! 		ipc.buf = kmem_alloc_wait(kernelmap, uap->data);
! 		error = copyin((char *)ipc.addr, (char *)ipc.buf, ipc.buflen);
! 		if (error) {
! 			kmem_free_wakeup(kernelmap, ipc.buf, ipc.buflen);
! 			goto out;
  		}
! #endif
! 	default:
! 		break;
  	}
! #endif
! 
  	setrun(p);
! 	while ((ipc.flag & IPC_DONE) == 0) {
! 		error = tsleep((caddr_t)&ipc, PWAIT|PCATCH, "ipc", 0);
! 		if (error)
! 			goto out;
! 	}
! 
! 	*retval = ipc.data;
! 	if (error = ipc.error)
! 		goto out;
! 
  #ifdef PT_GETREGS
- 	switch (uap->req) {
  	case PT_GETREGS:
! 		error = copyout((char *)ipc.regs, (char *)ipc.addr, sizeof(ipc.regs));
! 		break;
! 
! 	case PT_BREAD_I:
! 	case PT_BREAD_D:
! 		/* Not yet */
  	default:
  		break;
  	}
- #endif
  
! out:
! 	/* Release ipc structure */
! 	ipc.flag &= ~IPC_BUSY;
! 	if (ipc.flag & IPC_WANT) {
! 		ipc.flag &= ~IPC_WANT;
! 		wakeup((caddr_t)&ipc);
! 	}
! 	return error;
  }
  
  procxmt(p)
  	register struct proc *p;
  {
- 	int i, *xreg, rv = 0;
- #ifdef i386
- 	int new_eflags, old_cs, old_ds, old_es, old_ss, old_eflags;
- 	int *regs;
- #endif
- 
- 	/* Are we still being traced? */
- 	if ((p->p_flag & STRC) == 0)
  		return 1;
- 
- 	p->p_addr->u_kproc.kp_proc = *p;
- 	fill_eproc(p, &p->p_addr->u_kproc.kp_eproc);
- 
- 	switch (ipc.req) {
- 	case PT_READ_I:
- 	case PT_READ_D:
- 		if (!useracc(ipc.addr, sizeof(ipc.data), B_READ)) {
- 			ipc.error = EFAULT;
- 			break;
- 		}
- 		ipc.error = copyin((char *)ipc.addr, (char *)&ipc.data, sizeof(ipc.data));
- 		break;
- 
- 	case PT_READ_U:
- 		if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) {
- 			ipc.error = EFAULT;
- 			break;
- 		}
- 		ipc.data = *(int *)((u_int)p->p_addr + (u_int)ipc.addr);
- 		break;
- 
- 	case PT_WRITE_I:
- 	case PT_WRITE_D: {				/* 04 Sep 92*/
- 		vm_prot_t prot;		/* current protection of region */
- 		int cow;		/* ensure copy-on-write happens */
- 
- 		if (cow = (useracc(ipc.addr, sizeof(ipc.data), B_WRITE) == 0)) {
- 			vm_offset_t	addr = (vm_offset_t)ipc.addr;
- 			vm_size_t	size;
- 			vm_prot_t	max_prot;
- 			vm_inherit_t	inh;
- 			boolean_t	shared;
- 			vm_object_t	object;
- 			vm_offset_t	objoff;
- 
- 			/*
- 			 * XXX - the useracc check is stronger than the vm
- 			 * checks because the user page tables are in the map.
- 			 * Anyway, most of this can be removed now that COW
- 			 * works.
- 			 */
- 			if (!useracc(ipc.addr, sizeof(ipc.data), B_READ) ||
- 			    vm_region(&p->p_vmspace->vm_map, &addr, &size,
- 					&prot, &max_prot, &inh, &shared,
- 					&object, &objoff) != KERN_SUCCESS ||
- 			    vm_protect(&p->p_vmspace->vm_map, ipc.addr,
- 					sizeof(ipc.data), FALSE,
- 					prot|VM_PROT_WRITE) != KERN_SUCCESS ||
- 			    vm_fault(&p->p_vmspace->vm_map,trunc_page(ipc.addr),
- 					VM_PROT_WRITE, FALSE) != KERN_SUCCESS) {
- 
- 				ipc.error = EFAULT;
- 				break;
- 			}
- 		}
- 		ipc.error = copyout((char *)&ipc.data,
- 					(char *)ipc.addr, sizeof(ipc.data));
- 		if (cow)
- 			if (vm_protect(&p->p_vmspace->vm_map, ipc.addr,
- 					sizeof(ipc.data), FALSE,
- 					prot) != KERN_SUCCESS)
- 				printf("ptrace: oops\n");
- 		break;
- 	}
- 
- 	case PT_WRITE_U:
- #ifdef i386
- 		regs = p->p_regs;
- 		/*
- 		 * XXX - privileged kernel state is scattered all over the
- 		 * user area.  Only allow write access to areas known to
- 		 * be safe.
- 		 */
- #define	GO_IF_SAFE(min, size) \
- 		if ((u_int)ipc.addr >= (min) \
- 		    && (u_int)ipc.addr <= (min) + (size) - sizeof(int)) \
- 			goto pt_write_u
- 		/*
- 		 * Allow writing entire FPU state.
- 		 */
- 		GO_IF_SAFE(offsetof(struct user, u_pcb)
- 			   + offsetof(struct pcb, pcb_savefpu),
- 			   sizeof(struct save87));
- 		/*
- 		 * Allow writing ordinary registers.  Changes to segment
- 		 * registers and to some bits in %eflags will be silently
- 		 * ignored.  Such changes ought to be an error.
- 		 */
- /*
-  * XXX - there is no define for the base of the user area except USRSTACK.
-  * XXX - USRSTACK is not the base of the user stack.  It is the base of the
-  * user area.
-  */
- #define	USER_OFF(va)	((u_int)(va) - USRSTACK)
- 		GO_IF_SAFE(USER_OFF(regs),
- 			   (curpcb->pcb_flags & FM_TRAP ? tSS + 1 : sSS + 1)
- 			   * sizeof *regs);
- 		ipc.error = EFAULT;
- 		break;
- #else
- 		if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) {
- 			ipc.error = EFAULT;
- 			break;
- 		}
- #endif
- 	pt_write_u:
- #ifdef i386
- 		if (curpcb->pcb_flags & FM_TRAP) {
- 			old_cs = regs[tCS];
- 			old_ds = regs[tES];
- 			old_es = regs[tES];
- 			old_ss = regs[tSS];
- 			old_eflags = regs[tEFLAGS];
- 		} else {
- 			old_cs = regs[sCS];
- 			old_ss = regs[sSS];
- 			old_eflags = regs[sEFLAGS];
- 		}
- #endif
- 		*(int *)((u_int)p->p_addr + (u_int)ipc.addr) = ipc.data;
- #ifdef i386
- 		/*
- 		 * Don't allow segment registers to change (although they can
- 		 * be changed directly to certain values).
- 		 * Don't allow privileged bits in %eflags to change.  Users
- 		 * have privilege to change TF and NT although although they
- 		 * usually shouldn't.
- 		 * XXX - fix PT_SETREGS.
- 		 * XXX - simplify.  Maybe copy through a temporary struct.
- 		 * Watch out for problems when ipc.addr is not a multiple
- 		 * of the register size.
- 		 */
- #define EFL_UNPRIVILEGED (EFL_CF | EFL_PF | EFL_AF | EFL_ZF | EFL_SF \
- 			  | EFL_TF | EFL_DF | EFL_OF | EFL_NT)
- 		if (curpcb->pcb_flags & FM_TRAP) {
- 			regs[tCS] = old_cs;
- 			regs[tDS] = old_ds;
- 			regs[tES] = old_es;
- 			regs[tSS] = old_es;
- 			new_eflags = regs[tEFLAGS];
- 			regs[tEFLAGS]
- 				= (new_eflags & EFL_UNPRIVILEGED)
- 				  | (old_eflags & ~EFL_UNPRIVILEGED);
- 		} else {
- 			regs[sCS] = old_cs;
- 			regs[sSS] = old_ss;
- 			new_eflags = regs[sEFLAGS];
- 			regs[sEFLAGS]
- 				= (new_eflags & EFL_UNPRIVILEGED)
- 				  | (old_eflags & ~EFL_UNPRIVILEGED);
- 		}
- #endif
- 		break;
- 
- 	case PT_CONTINUE:
- 		if (ipc.addr != (int *)1) {
- #ifdef i386
- 			p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
- #endif
- 		}
- 		p->p_flag &= ~SSTRC;	/* Only set by PT_SYSCALL */
- 		if ((unsigned)ipc.data >= NSIG) {
- 			ipc.error = EINVAL;
- 		} else {
- 			p->p_xstat = ipc.data;
- 			rv = 1;
- 		}
- 		break;
- 
- 	case PT_KILL:
- 		p->p_flag &= ~SSTRC;	/* Only set by PT_SYSCALL */
- 		rv = 2;
- 		break;
- 
- 	case PT_STEP:
- #ifdef i386
- 		if (ipc.addr != (int *)1) {
- 			p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
- 		}
- 		p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEFLAGS:sEFLAGS] |= PSL_T;
- #endif
- 		p->p_flag &= ~SSTRC;	/* Only set by PT_SYSCALL */
- 		p->p_xstat = 0;
- 		rv = 1;
- 		break;
- 
- #ifdef PT_SYSCALL
- 	case PT_SYSCALL:
- 		if (ipc.addr != (int *)1) {
- #ifdef i386
- 			p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
- #endif
- 		}
- 		p->p_flag |= SSTRC;
- 		p->p_xstat = 0;
- 		rv = 1;
- 		break;
- #endif
- #ifdef PT_GETREGS
- 	case PT_GETREGS:
- #ifdef i386
- 		xreg = (curpcb->pcb_flags&FM_TRAP)?ipcreg:sipcreg;
- #endif
- 
- 		for (i = 0; i < NIPCREG; i++)
- 			ipc.regs[i] = p->p_regs[xreg[i]];
- 		break;
- 
- 	case PT_SETREGS:
- #ifdef i386
- 		xreg = (curpcb->pcb_flags&FM_TRAP)?ipcreg:sipcreg;
- #endif
- 
- 		for (i = 0; i < NIPCREG; i++)
- 			p->p_regs[xreg[i]] = ipc.regs[i];
- 		break;
- #endif
- 
- #ifdef PT_DUMP
- 	case PT_DUMP:
- 		/* Should be able to specify core file name */
- 		ipc.error = coredump(p);
- 		break;
- #endif
- 
- 	default:
- 		ipc.error = EINVAL;
- 	}
- 	ipc.flag |= IPC_DONE;
- 	wakeup((caddr_t)&ipc);
- 
- 	if (rv == 2)
- 		kexit(p, 0); 	/*???*/
- 
- 	return rv;
  }
  
  /*
   * Enable process profiling system call.
   */
! 
! struct profil_args {
  	short	*bufbase;	/* base of data buffer */
  	unsigned bufsize;	/* size of data buffer */
  	unsigned pcoffset;	/* pc offset (for subtraction) */
  	unsigned pcscale;	/* scaling factor for offset pc */
! };
! 
! /* ARGSUSED */
! profil(p, uap, retval)
! 	struct proc *p;
! 	register struct profil_args *uap;
  	int *retval;
  {
  	/* from looking at man pages, and include files, looks like
--- 301,413 ----
  		splx(s);
  		return 0;
  
! # ifdef PT_INHERIT
  	case PT_INHERIT:
  		if ((p->p_flag & STRC) == 0)
  			return ESRCH;
  		p->p_flag |= SFTRC;
  		return 0;
! # endif	/* PT_INHERIT */
! #endif	/* PT_ATTACH */
  
! 	case PT_READ_I:
! 	case PT_READ_D:
! 		if (error = pread (p, (unsigned int)uap->addr, retval))
! 			return error;
! 		return 0;
! 	case PT_WRITE_I:
! 	case PT_WRITE_D:
! 		if (error = pwrite (p, (unsigned int)uap->addr,
! 				    (unsigned int)uap->data))
! 			return error;
! 		return 0;
! 	case PT_STEP:
! 		if (error = ptrace_single_step (p))
! 			return error;
! 		/* fallthrough */
! 	case PT_CONTINUE:
! 		/*
! 		 * Continue at addr uap->addr with signal
! 		 * uap->data; if uap->addr is 1, then we just
! 		 * let the chips fall where they may.
! 		 *
! 		 * The only check I'll make right now is for
! 		 * uap->data to be larger than NSIG; if so, we return
! 		 * EINVAL.
! 		 */
! 		if (uap->data >= NSIG)
! 			return EINVAL;
  
! 		if (uap->addr != (int*)1) {
! 			fill_eproc (p, &p->p_addr->u_kproc.kp_eproc);
! 			if (error = ptrace_set_pc (p, uap->addr))
! 				return error;
  		}
  
! 		p->p_xstat = uap->data;
  
! /*		if (p->p_stat == SSTOP) */
! 		setrun (p);
! 		return 0;
! 	case PT_READ_U:
! 		if ((u_int)uap->addr > (UPAGES * NBPG - sizeof(int))) {
! 			return EFAULT;
  		}
! 		p->p_addr->u_kproc.kp_proc = *p;
! 		fill_eproc (p, &p->p_addr->u_kproc.kp_eproc);
! 		*retval = *(int*)((u_int)p->p_addr + (u_int)uap->addr);
! 		return 0;
! 	case PT_WRITE_U:
! 		if ((u_int)uap->addr > (UPAGES * NBPG - sizeof(int))) {
! 			return EFAULT;
  		}
! 		p->p_addr->u_kproc.kp_proc = *p;
! 		fill_eproc (p, &p->p_addr->u_kproc.kp_eproc);
! 		*(int*)((u_int)p->p_addr + (u_int)uap->addr) = uap->data;
! 		return 0;
! 	case PT_KILL:
! 		p->p_xstat = SIGKILL;
  		setrun(p);
! 		return 0;
  #ifdef PT_GETREGS
  	case PT_GETREGS:
! 		/*
! 		 * copyout the registers into addr.  There's no
! 		 * size constraint!!! *GRRR*
! 		 */
! 		return ptrace_getregs(p, uap->addr);
! 	case PT_SETREGS:
! 		/*
! 		 * copyin the registers from addr.  Again, no
! 		 * size constraint!!! *GRRRR*
! 		 */
! 		return ptrace_setregs (p, uap->addr);
! #endif /* PT_GETREGS */
  	default:
  		break;
  	}
  
! 	return 0;
  }
  
  procxmt(p)
  	register struct proc *p;
  {
  	return 1;
  }
  
  /*
   * Enable process profiling system call.
   */
! /* ARGSUSED */
! profil(p, uap, retval)
! 	struct proc *p;
! 	register struct args {
  		short	*bufbase;	/* base of data buffer */
  		unsigned bufsize;	/* size of data buffer */
  		unsigned pcoffset;	/* pc offset (for subtraction) */
  		unsigned pcscale;	/* scaling factor for offset pc */
! 	} *uap;
  	int *retval;
  {
  	/* from looking at man pages, and include files, looks like