/*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * This module is believed to contain source code proprietary to AT&T. * Use and redistribution is subject to the Berkeley Software License * Agreement and your Software Agreement with AT&T (Western Electric). * * @(#)sys_process.c 8.1 (Berkeley) 6/10/93 */ #define IPCREG #include <sys/param.h> #include <sys/proc.h> #include <sys/vnode.h> #include <sys/buf.h> #include <sys/ptrace.h> #include <machine/reg.h> #include <machine/psl.h> #include <vm/vm.h> #include <vm/vm_page.h> #include <sys/user.h> /* * Priority for tracing */ #define IPCPRI PZERO /* * Tracing variables. * Used to pass trace command from * parent to child being traced. * This data base cannot be * shared and is locked * per user. */ struct { int ip_lock; int ip_req; caddr_t ip_addr; int ip_data; } ipc; /* * Process debugging system call. */ struct ptrace_args { int req; pid_t pid; caddr_t addr; int data; }; ptrace(curp, uap, retval) struct proc *curp; register struct ptrace_args *uap; int *retval; { register struct proc *p; int error; if (uap->req <= 0) { curp->p_flag |= STRC; return (0); } p = pfind(uap->pid); if (p == 0) return (ESRCH); if (uap->req == PT_ATTACH) { /* * Must be root if the process has used set user or group * privileges or does not belong to the real user. Must * not be already traced. Can't attach to ourselves. */ if ((p->p_flag & SUGID || p->p_cred->p_ruid != curp->p_cred->p_ruid) && (error = suser(p->p_ucred, &p->p_acflag)) != 0) return (error); if (p->p_flag & STRC) return (EALREADY); /* ??? */ if (p->p_pid == curp->p_pid) return (EINVAL); /* * It would be nice if the tracing relationship was separate * from the parent relationship but that would require * another set of links in the proc struct or for "wait" * to scan the entire proc table. To make life easier, * we just re-parent the process we're trying to trace. * The old parent is remembered so we can put things back * on a "detach". */ p->p_flag |= STRC; p->p_oppid = p->p_pptr->p_pid; proc_reparent(p, curp); psignal(p, SIGSTOP); return (0); } if (p->p_stat != SSTOP || p->p_pptr != curp || !(p->p_flag & STRC)) return (ESRCH); while (ipc.ip_lock) sleep((caddr_t)&ipc, IPCPRI); ipc.ip_lock = p->p_pid; ipc.ip_data = uap->data; ipc.ip_addr = uap->addr; ipc.ip_req = uap->req; p->p_flag &= ~SWTED; while (ipc.ip_req > 0) { if (p->p_stat==SSTOP) setrun(p); sleep((caddr_t)&ipc, IPCPRI); } *retval = ipc.ip_data; ipc.ip_lock = 0; wakeup((caddr_t)&ipc); if (ipc.ip_req < 0) return (EIO); return (0); } #define PHYSOFF(p, o) ((caddr_t)(p) + (o)) #if defined(hp300) || defined(luna68k) #define PHYSALIGNED(a) (((int)(a) & (sizeof(short) - 1)) == 0) #else #define PHYSALIGNED(a) (((int)(a) & (sizeof(int) - 1)) == 0) #endif #if defined(i386) #undef PC #undef SP #undef PS #undef R0 #undef R1 #define PC tEIP #define SP tESP #define PS tEFLAGS #define R0 tEDX #define R1 tECX #endif /* * Transmit a tracing request from the parent to the child process * being debugged. This code runs in the context of the child process * to fulfill the command requested by the parent. */ procxmt(p) register struct proc *p; { register int i, *poff, *regs; extern char kstack[]; if (ipc.ip_lock != p->p_pid) return (0); p->p_slptime = 0; regs = p->p_md.md_regs; p->p_addr->u_kproc.kp_proc.p_md.md_regs = regs; /* u.u_ar0 */ i = ipc.ip_req; ipc.ip_req = 0; switch (i) { case PT_READ_I: /* read the child's text space */ if (!useracc((caddr_t)ipc.ip_addr, 4, B_READ)) goto error; ipc.ip_data = fuiword((caddr_t)ipc.ip_addr); break; case PT_READ_D: /* read the child's data space */ if (!useracc((caddr_t)ipc.ip_addr, 4, B_READ)) goto error; ipc.ip_data = fuword((caddr_t)ipc.ip_addr); break; case PT_READ_U: /* read the child's u. */ i = (int)ipc.ip_addr; if ((u_int) i > ctob(UPAGES)-sizeof(int) || !PHYSALIGNED(i)) goto error; ipc.ip_data = *(int *)PHYSOFF(p->p_addr, i); break; case PT_WRITE_I: /* write the child's text space */ if ((i = suiword((caddr_t)ipc.ip_addr, ipc.ip_data)) < 0) { vm_offset_t sa, ea; int rv; sa = trunc_page((vm_offset_t)ipc.ip_addr); ea = round_page((vm_offset_t)ipc.ip_addr+sizeof(int)); rv = vm_map_protect(&p->p_vmspace->vm_map, sa, ea, VM_PROT_DEFAULT, FALSE); if (rv == KERN_SUCCESS) { i = suiword((caddr_t)ipc.ip_addr, ipc.ip_data); (void) vm_map_protect(&p->p_vmspace->vm_map, sa, ea, VM_PROT_READ|VM_PROT_EXECUTE, FALSE); } } if (i < 0) goto error; break; case PT_WRITE_D: /* write the child's data space */ if (suword((caddr_t)ipc.ip_addr, 0) < 0) goto error; (void) suword((caddr_t)ipc.ip_addr, ipc.ip_data); break; case PT_WRITE_U: /* write the child's u. */ i = (int)ipc.ip_addr; #ifdef mips poff = (int *)PHYSOFF(p->p_addr, i); #else poff = (int *)PHYSOFF(kstack, i); #endif for (i=0; i<NIPCREG; i++) if (poff == ®s[ipcreg[i]]) goto ok; #if defined(hp300) || defined(luna68k) /* * In the new frame layout, PS/PC are skewed by 2 bytes. */ regs = (int *)((short *)regs + 1); if (poff == ®s[PC]) goto ok; #endif if (poff == ®s[PS]) { ipc.ip_data |= PSL_USERSET; ipc.ip_data &= ~PSL_USERCLR; #ifdef PSL_CM_CLR if (ipc.ip_data & PSL_CM) ipc.ip_data &= ~PSL_CM_CLR; #endif goto ok; } #if defined(hp300) || defined(luna68k) #ifdef FPCOPROC if (poff >= (int *)&((struct user *)kstack)->u_pcb.pcb_fpregs.fpf_regs && poff <= (int *)&((struct user *)kstack)->u_pcb.pcb_fpregs.fpf_fpiar) goto ok; #endif #endif goto error; ok: *poff = ipc.ip_data; break; case PT_STEP: /* single step the child */ case PT_CONTINUE: /* continue the child */ regs = (int *)((short *)regs + 1); if ((unsigned)ipc.ip_data >= NSIG) goto error; if ((int)ipc.ip_addr != 1) regs[PC] = (int)ipc.ip_addr; p->p_xstat = ipc.ip_data; /* see issig */ #ifdef PSL_T /* need something more machine independent here... */ if (i == PT_STEP) regs[PS] |= PSL_T; #endif wakeup((caddr_t)&ipc); return (1); case PT_KILL: /* kill the child process */ wakeup((caddr_t)&ipc); exit1(p, (int)p->p_xstat); case PT_DETACH: /* stop tracing the child */ regs = (int *)((short *)regs + 1); if ((unsigned)ipc.ip_data >= NSIG) goto error; if ((int)ipc.ip_addr != 1) regs[PC] = (int)ipc.ip_addr; p->p_xstat = ipc.ip_data; /* see issig */ p->p_flag &= ~STRC; if (p->p_oppid != p->p_pptr->p_pid) { register struct proc *pp = pfind(p->p_oppid); if (pp) proc_reparent(p, pp); } p->p_oppid = 0; wakeup((caddr_t)&ipc); return (1); default: error: ipc.ip_req = -1; } wakeup((caddr_t)&ipc); return (0); }