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 (®s, 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, ®s, 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