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