📄 pcontrol.c
字号:
errstr = "Pstopstatus PCDSTOP"; break; case PCWSTOP: errstr = "Pstopstatus PCWSTOP"; break; default: errstr = "Pstopstatus PC???"; break; } dprintf("%s: %s\n", errstr, strerror(err)); } deadcheck(P); break; } if (err != EINTR && err != ERESTART) { errno = err; return (-1); } } if (!(P->status.pr_flags & PR_STOPPED)) { P->state = PS_RUN; if (request == PCNULL || request == PCDSTOP || msec != 0) return (0); dprintf("Pstopstatus: process is not stopped\n"); errno = EPROTO; return (-1); } P->state = PS_STOP; if (_libproc_debug) /* debugging */ prdump(P); /* * If the process was already stopped coming into Pstopstatus(), * then don't use its PC to set P->sysaddr since it may have been * changed since the time the process originally stopped. */ if (old_state == PS_STOP) return (0); switch (P->status.pr_lwp.pr_why) { case PR_SYSENTRY: case PR_SYSEXIT: if (Pissyscall_prev(P, P->status.pr_lwp.pr_reg[R_PC], &P->sysaddr) == 0) P->sysaddr = P->status.pr_lwp.pr_reg[R_PC]; break; case PR_REQUESTED: case PR_SIGNALLED: case PR_FAULTED: case PR_JOBCONTROL: case PR_SUSPENDED: break; default: errno = EPROTO; return (-1); } return (0);}/* * Wait for the process to stop for any reason. */intPwait(struct ps_prochandle *P, uint_t msec){ return (Pstopstatus(P, PCWSTOP, msec));}/* * Direct the process to stop; wait for it to stop. */intPstop(struct ps_prochandle *P, uint_t msec){ return (Pstopstatus(P, PCSTOP, msec));}/* * Direct the process to stop; don't wait. */intPdstop(struct ps_prochandle *P){ return (Pstopstatus(P, PCDSTOP, 0));}static voiddeadcheck(struct ps_prochandle *P){ int fd; void *buf; size_t size; if (P->statfd < 0) P->state = PS_UNDEAD; else { if (P->agentstatfd < 0) { fd = P->statfd; buf = &P->status; size = sizeof (P->status); } else { fd = P->agentstatfd; buf = &P->status.pr_lwp; size = sizeof (P->status.pr_lwp); } while (pread(fd, buf, size, (off_t)0) != size) { switch (errno) { default: P->state = PS_UNDEAD; break; case EINTR: case ERESTART: continue; case EAGAIN: P->state = PS_LOST; break; } break; } P->status.pr_flags = P->status.pr_lwp.pr_flags; }}/* * Get the value of one register from stopped process. */intPgetareg(struct ps_prochandle *P, int regno, prgreg_t *preg){ if (regno < 0 || regno >= NPRGREG) { errno = EINVAL; return (-1); } if (P->state == PS_IDLE) { errno = ENODATA; return (-1); } if (P->state != PS_STOP && P->state != PS_DEAD) { errno = EBUSY; return (-1); } *preg = P->status.pr_lwp.pr_reg[regno]; return (0);}/* * Put value of one register into stopped process. */intPputareg(struct ps_prochandle *P, int regno, prgreg_t reg){ if (regno < 0 || regno >= NPRGREG) { errno = EINVAL; return (-1); } if (P->state != PS_STOP) { errno = EBUSY; return (-1); } P->status.pr_lwp.pr_reg[regno] = reg; P->flags |= SETREGS; /* set registers before continuing */ return (0);}intPsetrun(struct ps_prochandle *P, int sig, /* signal to pass to process */ int flags) /* PRSTEP|PRSABORT|PRSTOP|PRCSIG|PRCFAULT */{ int ctlfd = (P->agentctlfd >= 0) ? P->agentctlfd : P->ctlfd; int sbits = (PR_DSTOP | PR_ISTOP | PR_ASLEEP); long ctl[1 + /* PCCFAULT */ 1 + sizeof (siginfo_t)/sizeof (long) + /* PCSSIG/PCCSIG */ 2 ]; /* PCRUN */ long *ctlp = ctl; size_t size; if (P->state != PS_STOP && (P->status.pr_lwp.pr_flags & sbits) == 0) { errno = EBUSY; return (-1); } Psync(P); /* flush tracing flags and registers */ if (flags & PRCFAULT) { /* clear current fault */ *ctlp++ = PCCFAULT; flags &= ~PRCFAULT; } if (flags & PRCSIG) { /* clear current signal */ *ctlp++ = PCCSIG; flags &= ~PRCSIG; } else if (sig && sig != P->status.pr_lwp.pr_cursig) { /* make current signal */ siginfo_t *infop; *ctlp++ = PCSSIG; infop = (siginfo_t *)ctlp; (void) memset(infop, 0, sizeof (*infop)); infop->si_signo = sig; ctlp += sizeof (siginfo_t) / sizeof (long); } *ctlp++ = PCRUN; *ctlp++ = flags; size = (char *)ctlp - (char *)ctl; P->info_valid = 0; /* will need to update map and file info */ /* * If we've cached ucontext-list information while we were stopped, * free it now. */ if (P->ucaddrs != NULL) { free(P->ucaddrs); P->ucaddrs = NULL; P->ucnelems = 0; } if (write(ctlfd, ctl, size) != size) { /* If it is dead or lost, return the real status, not PS_RUN */ if (errno == ENOENT || errno == EAGAIN) { (void) Pstopstatus(P, PCNULL, 0); return (0); } /* If it is not in a jobcontrol stop, issue an error message */ if (errno != EBUSY || P->status.pr_lwp.pr_why != PR_JOBCONTROL) { dprintf("Psetrun: %s\n", strerror(errno)); return (-1); } /* Otherwise pretend that the job-stopped process is running */ } P->state = PS_RUN; return (0);}ssize_tPread(struct ps_prochandle *P, void *buf, /* caller's buffer */ size_t nbyte, /* number of bytes to read */ uintptr_t address) /* address in process */{ return (P->ops->p_pread(P, buf, nbyte, address));}ssize_tPread_string(struct ps_prochandle *P, char *buf, /* caller's buffer */ size_t size, /* upper limit on bytes to read */ uintptr_t addr) /* address in process */{ enum { STRSZ = 40 }; char string[STRSZ + 1]; ssize_t leng = 0; int nbyte; if (size < 2) { errno = EINVAL; return (-1); } size--; /* ensure trailing null fits in buffer */ *buf = '\0'; string[STRSZ] = '\0'; for (nbyte = STRSZ; nbyte == STRSZ && leng < size; addr += STRSZ) { if ((nbyte = P->ops->p_pread(P, string, STRSZ, addr)) <= 0) { buf[leng] = '\0'; return (leng ? leng : -1); } if ((nbyte = strlen(string)) > 0) { if (leng + nbyte > size) nbyte = size - leng; (void) strncpy(buf + leng, string, nbyte); leng += nbyte; } } buf[leng] = '\0'; return (leng);}ssize_tPwrite(struct ps_prochandle *P, const void *buf, /* caller's buffer */ size_t nbyte, /* number of bytes to write */ uintptr_t address) /* address in process */{ return (P->ops->p_pwrite(P, buf, nbyte, address));}intPclearsig(struct ps_prochandle *P){ int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; long ctl = PCCSIG; if (write(ctlfd, &ctl, sizeof (ctl)) != sizeof (ctl)) return (-1); P->status.pr_lwp.pr_cursig = 0; return (0);}intPclearfault(struct ps_prochandle *P){ int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; long ctl = PCCFAULT; if (write(ctlfd, &ctl, sizeof (ctl)) != sizeof (ctl)) return (-1); return (0);}/* * Set a breakpoint trap, return original instruction. */intPsetbkpt(struct ps_prochandle *P, uintptr_t address, ulong_t *saved){ long ctl[1 + sizeof (priovec_t) / sizeof (long) + /* PCREAD */ 1 + sizeof (priovec_t) / sizeof (long)]; /* PCWRITE */ long *ctlp = ctl; size_t size; priovec_t *iovp; instr_t bpt = BPT; instr_t old; if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) { errno = ENOENT; return (-1); } /* fetch the old instruction */ *ctlp++ = PCREAD; iovp = (priovec_t *)ctlp; iovp->pio_base = &old; iovp->pio_len = sizeof (old); iovp->pio_offset = address; ctlp += sizeof (priovec_t) / sizeof (long); /* write the BPT instruction */ *ctlp++ = PCWRITE; iovp = (priovec_t *)ctlp; iovp->pio_base = &bpt; iovp->pio_len = sizeof (bpt); iovp->pio_offset = address; ctlp += sizeof (priovec_t) / sizeof (long); size = (char *)ctlp - (char *)ctl; if (write(P->ctlfd, ctl, size) != size) return (-1); /* * Fail if there was already a breakpoint there from another debugger * or DTrace's user-level tracing on x86. */ if (old == BPT) return (EBUSY); *saved = (ulong_t)old; return (0);}/* * Restore original instruction where a breakpoint was set. */intPdelbkpt(struct ps_prochandle *P, uintptr_t address, ulong_t saved){ instr_t old = (instr_t)saved; instr_t cur; if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) { errno = ENOENT; return (-1); } /* * If the breakpoint instruction we had placed has been overwritten * with a new instruction, then don't try to replace it with the * old instruction. Doing do can cause problems with self-modifying * code -- PLTs for example. If the Pread() fails, we assume that we * should proceed though most likely the Pwrite() will also fail. */ if (Pread(P, &cur, sizeof (cur), address) == sizeof (cur) && cur != BPT) return (0); if (Pwrite(P, &old, sizeof (old), address) != sizeof (old)) return (-1); return (0);}/* * Common code for Pxecbkpt() and Lxecbkpt(). * Develop the array of requests that will do the job, then * write them to the specified control file descriptor. * Return the non-zero errno if the write fails. */static intexecute_bkpt( int ctlfd, /* process or LWP control file descriptor */ const fltset_t *faultset, /* current set of traced faults */ const sigset_t *sigmask, /* current signal mask */ uintptr_t address, /* address of breakpint */ ulong_t saved) /* the saved instruction */{ long ctl[ 1 + sizeof (sigset_t) / sizeof (long) + /* PCSHOLD */ 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ 1 + sizeof (priovec_t) / sizeof (long) + /* PCWRITE */ 2 + /* PCRUN */ 1 + /* PCWSTOP */ 1 + /* PCCFAULT */ 1 + sizeof (priovec_t) / sizeof (long) + /* PCWRITE */ 1 + sizeof (fltset_t) / sizeof (long) + /* PCSFAULT */ 1 + sizeof (sigset_t) / sizeof (long)]; /* PCSHOLD */ long *ctlp = ctl; sigset_t unblock; size_t size; ssize_t ssize; priovec_t *iovp; sigset_t *holdp; fltset_t *faultp; instr_t old = (instr_t)saved; instr_t bpt = BPT; int error = 0; /* block our signals for the duration */ (void) sigprocmask(SIG_BLOCK, &blockable_sigs, &unblock); /* hold posted signals */ *ctlp++ = PCSHOLD; holdp = (sigset_t *)ctlp; prfillset(holdp); prdelset(holdp, SIGKILL); prdelset(holdp, SIGSTOP); ctlp += sizeof (sigset_t) / sizeof (long); /* force tracing of FLTTRACE */ if (!(prismember(faultset, FLTTRACE))) { *ctlp++ = PCSFAULT; faultp = (fltset_t *)ctlp; *faultp = *faultset; praddset(faultp, FLTTRACE); ctlp += sizeof (fltset_t) / sizeof (long); } /* restore the old instruction */ *ctlp++ = PCWRITE; iovp = (priovec_t *)ctlp; iovp->pio_base = &old; iovp->pio_len = sizeof (old); iovp->pio_offset = address; ctlp += sizeof (priovec_t) / sizeof (long); /* clear current signal and fault; set running w/ single-step */ *ctlp++ = PCRUN; *ctlp++ = PRCSIG | PRCFAULT | PRSTEP; /* wait for stop, cancel the fault */ *ctlp++ = PCWSTOP; *ctlp++ = PCCFAULT; /* restore the breakpoint trap */ *ctlp++ = PCWRITE; iovp = (priovec_t *)ctlp; iovp->pio_base = &bpt; iovp->pio_len = sizeof (bpt); iovp->pio_offset = address; ctlp += sizeof (priovec_t) / sizeof (long); /* restore fault tracing set */ if (!(prismember(faultset, FLTTRACE))) { *ctlp++ = PCSFAULT; *(fltset_t *)ctlp = *faultset; ctlp += sizeof (fltset_t) / sizeof (long); } /* restore the hold mask */ *ctlp++ = PCSHOLD; *(sigset_t *)ctlp = *sigmask; ctlp += sizeof (sigset_t) / sizeof (long); size = (char *)ctlp - (char *)ctl; if ((ssize = write(ctlfd, ctl, size)) != size) error = (ssize == -1)? errno : EINTR; (void) sigprocmask(SIG_SETMASK, &unblock, NULL); return (error);}/* * Step over a breakpoint, i.e., execute the instruction that * really belongs at the breakpoint location (the current %pc) * and leave the process stopped at the next instruction. */intPxecbkpt(struct ps_prochandle *P, ulong_t saved){ int ctlfd = (P->agentctlfd >= 0)? P->agentctlfd : P->ctlfd; int rv, error; if (P->state != PS_STOP) { errno = EBUSY; return (-1); } Psync(P); error = execute_bkpt(ctlfd, &P->status.pr_flttrace, &P->status.pr_lwp.pr_lwphold, P->status.pr_lwp.pr_reg[R_PC], saved); rv = Pstopstatus(P, PCNULL, 0); if (error != 0) { if (P->status.pr_lwp.pr_why == PR_JOBCONTROL && error == EBUSY) { /* jobcontrol stop -- back off */ P->state = PS_RUN; return (0); } if (error == ENOENT) return (0); errno = error; return (-1); } return (rv);}/* * Install the watchpoint described by wp. */intPsetwapt(struct ps_prochandle *P, const prwatch_t *wp){ long ctl[1 + sizeof (prwatch_t) / sizeof (long)]; prwatch_t *cwp = (prwatch_t *)&ctl[1]; if (P->state == PS_DEAD || P->state == PS_UNDEAD || P->state == PS_IDLE) { errno = ENOENT; return (-1); } ctl[0] = PCWATCH;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -