/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef lint static char sccsid[] = "@(#)ptrace.c 5.1 (Berkeley) 6/6/85"; #endif not lint /* * routines for tracing the execution of a process * * The system call "ptrace" does all the work, these * routines just try to interface easily to it. */ #include "defs.h" #include #include #include #include "process.h" #include "object.h" #include "process.rep" # if (isvaxpx) # include "pxinfo.h" # endif #ifndef vax # define U_PAGE 0x2400 # define U_AR0 (14*sizeof(int)) LOCAL int ar0val = -1; #endif /* * This magic macro enables us to look at the process' registers * in its user structure. Very gross. */ #ifdef vax # define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) )) #else # define regloc(reg) (ar0val + ( sizeof(int) * (reg) )) #endif #define WMASK (~(sizeof(WORD) - 1)) #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE)) #define FIRSTSIG SIGINT #define LASTSIG SIGQUIT #define ischild(pid) ((pid) == 0) #define traceme() ptrace(0, 0, 0, 0) #define setrep(n) (1 << ((n)-1)) #define istraced(p) (p->sigset&setrep(p->signo)) /* * ptrace options (specified in first argument) */ #define UREAD 3 /* read from process's user structure */ #define UWRITE 6 /* write to process's user structure */ #define IREAD 1 /* read from process's instruction space */ #define IWRITE 4 /* write to process's instruction space */ #define DREAD 2 /* read from process's data space */ #define DWRITE 5 /* write to process's data space */ #define CONT 7 /* continue stopped process */ #define SSTEP 9 /* continue for approximately one instruction */ #define PKILL 8 /* terminate the process */ /* * Start up a new process by forking and exec-ing the * given argument list, returning when the process is loaded * and ready to execute. The PROCESS information (pointed to * by the first argument) is appropriately filled. * * If the given PROCESS structure is associated with an already running * process, we terminate it. */ /* VARARGS2 */ pstart(p, cmd, argv, infile, outfile) PROCESS *p; char *cmd; char **argv; char *infile; char *outfile; { int status; FILE *in, *out; if (p->pid != 0) { /* child already running? */ ptrace(PKILL, p->pid, 0, 0); /* ... kill it! */ } psigtrace(p, SIGTRAP, TRUE); if ((p->pid = fork()) == -1) { panic("can't fork"); } if (ischild(p->pid)) { traceme(); if (infile != NIL) { if ((in = fopen(infile, "r")) == NIL) { printf("can't read %s\n", infile); exit(1); } fswap(0, fileno(in)); } if (outfile != NIL) { if ((out = fopen(outfile, "w")) == NIL) { printf("can't write %s\n", outfile); exit(1); } fswap(1, fileno(out)); } execvp(cmd, argv); panic("can't exec %s", argv[0]); } pwait(p->pid, &status); getinfo(p, status); } /* * Continue a stopped process. The argument points to a PROCESS structure. * Before the process is restarted it's user area is modified according to * the values in the structure. When this routine finishes, * the structure has the new values from the process's user area. * * Pcont terminates when the process stops with a signal pending that * is being traced (via psigtrace), or when the process terminates. */ pcont(p) PROCESS *p; { int status; if (p->pid == 0) { error("program not active"); } do { setinfo(p); sigs_off(); if (ptrace(CONT, p->pid, p->pc, p->signo) < 0) { panic("can't continue process"); } pwait(p->pid, &status); sigs_on(); getinfo(p, status); } while (p->status == STOPPED && !istraced(p)); } /* * single step as best ptrace can */ pstep(p) PROCESS *p; { int status; setinfo(p); sigs_off(); ptrace(SSTEP, p->pid, p->pc, p->signo); pwait(p->pid, &status); sigs_on(); getinfo(p, status); } /* * Return from execution when the given signal is pending. */ psigtrace(p, sig, sw) PROCESS *p; int sig; int sw; { if (sw) { p->sigset |= setrep(sig); } else { p->sigset &= ~setrep(sig); } } /* * Don't catch any signals. * Particularly useful when letting a process finish uninhibited (i.e. px). */ unsetsigtraces(p) PROCESS *p; { p->sigset = 0; } /* * turn off attention to signals not being caught */ typedef int INTFUNC(); LOCAL INTFUNC *sigfunc[NSIG]; LOCAL sigs_off() { register int i; for (i = FIRSTSIG; i < LASTSIG; i++) { if (i != SIGKILL) { sigfunc[i] = signal(i, SIG_IGN); } } } /* * turn back on attention to signals */ LOCAL sigs_on() { register int i; for (i = FIRSTSIG; i < LASTSIG; i++) { if (i != SIGKILL) { signal(i, sigfunc[i]); } } } /* * get PROCESS information from process's user area */ #if vax LOCAL int rloc[] ={ R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, }; #else LOCAL int rloc[] ={ R0, R1, R2, R3, R4, R5, R6, R7, AR0, AR1, AR2, AR3, AR4, AR5, }; #endif LOCAL getinfo(p, status) register PROCESS *p; register int status; { register int i; p->signo = (status&0177); p->exitval = ((status >> 8)&0377); if (p->signo == STOPPED) { p->status = p->signo; p->signo = p->exitval; p->exitval = 0; } else { p->status = FINISHED; return; } #ifndef vax if (ar0val < 0){ ar0val = ptrace(UREAD, p->pid, U_AR0, 0); ar0val -= U_PAGE; } #endif for (i = 0; i < NREG; i++) { p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0); p->oreg[i] = p->reg[i]; } #ifdef vax p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0); p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0); p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); #else p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(AR6), 0); p->ap = p->oap = p->fp; p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); #endif } /* * set process's user area information from given PROCESS structure */ LOCAL setinfo(p) register PROCESS *p; { register int i; register int r; if (istraced(p)) { p->signo = 0; } for (i = 0; i < NREG; i++) { if ((r = p->reg[i]) != p->oreg[i]) { ptrace(UWRITE, p->pid, regloc(rloc[i]), r); } } #if vax if ((r = p->fp) != p->ofp) { ptrace(UWRITE, p->pid, regloc(FP), r); } if ((r = p->sp) != p->osp) { ptrace(UWRITE, p->pid, regloc(SP), r); } if ((r = p->ap) != p->oap) { ptrace(UWRITE, p->pid, regloc(AP), r); } #else if ((r = p->fp) != p->ofp) { ptrace(UWRITE, p->pid, regloc(AR6), r); } if ((r = p->sp) != p->osp) { ptrace(UWRITE, p->pid, regloc(SP), r); } #endif if ((r = p->pc) != p->opc) { ptrace(UWRITE, p->pid, regloc(PC), r); } } /* * Structure for reading and writing by words, but dealing with bytes. */ typedef union { WORD pword; BYTE pbyte[sizeof(WORD)]; } PWORD; /* * Read (write) from (to) the process' address space. * We must deal with ptrace's inability to look anywhere other * than at a word boundary. */ LOCAL WORD fetch(); LOCAL store(); pio(p, op, seg, buff, addr, nbytes) PROCESS *p; PIO_OP op; PIO_SEG seg; char *buff; ADDRESS addr; int nbytes; { register int i; register ADDRESS newaddr; register char *cp; char *bufend; PWORD w; ADDRESS wordaddr; int byteoff; if (p->status != STOPPED) { error("program is not active"); } cp = buff; newaddr = addr; wordaddr = (newaddr&WMASK); if (wordaddr != newaddr) { w.pword = fetch(p, seg, wordaddr); for (i = newaddr - wordaddr; i0; i++) { if (op == PREAD) { *cp++ = w.pbyte[i]; } else { w.pbyte[i] = *cp++; } nbytes--; } if (op == PWRITE) { store(p, seg, wordaddr, w.pword); } newaddr = wordaddr + sizeof(WORD); } byteoff = (nbytes&(~WMASK)); nbytes -= byteoff; bufend = cp + nbytes; while (cp < bufend) { if (op == PREAD) { *((WORD *) cp) = fetch(p, seg, newaddr); } else { store(p, seg, newaddr, *((WORD *) cp)); } cp += sizeof(WORD); newaddr += sizeof(WORD); } if (byteoff > 0) { w.pword = fetch(p, seg, newaddr); for (i = 0; i < byteoff; i++) { if (op == PREAD) { *cp++ = w.pbyte[i]; } else { w.pbyte[i] = *cp++; } } if (op == PWRITE) { store(p, seg, newaddr, w.pword); } } } /* * Get a word from a process at the given address. * The address is assumed to be on a word boundary. * * We use a simple cache scheme to avoid redundant references to * the instruction space (which is assumed to be pure). In the * case of px, the "instruction" space lies between ENDOFF and * ENDOFF + objsize. * * It is necessary to use a write-through scheme so that * breakpoints right next to each other don't interfere. */ LOCAL WORD fetch(p, seg, addr) PROCESS *p; PIO_SEG seg; register int addr; { register CACHEWORD *wp; register WORD w; switch (seg) { case TEXTSEG: # if (isvaxpx) panic("tried to fetch from px i-space"); /* NOTREACHED */ # else wp = &p->word[cachehash(addr)]; if (addr == 0 || wp->addr != addr) { w = ptrace(IREAD, p->pid, addr, 0); wp->addr = addr; wp->val = w; } else { w = wp->val; } break; # endif case DATASEG: # if (isvaxpx) if (addr >= ENDOFF && addr < ENDOFF + objsize) { wp = &p->word[cachehash(addr)]; if (addr == 0 || wp->addr != addr) { w = ptrace(DREAD, p->pid, addr, 0); wp->addr = addr; wp->val = w; } else { w = wp->val; } } else { w = ptrace(DREAD, p->pid, addr, 0); } # else w = ptrace(DREAD, p->pid, addr, 0); # endif break; default: panic("fetch: bad seg %d", seg); /* NOTREACHED */ } return(w); } /* * Put a word into the process' address space at the given address. * The address is assumed to be on a word boundary. */ LOCAL store(p, seg, addr, data) PROCESS *p; PIO_SEG seg; int addr; WORD data; { register CACHEWORD *wp; switch (seg) { case TEXTSEG: wp = &p->word[cachehash(addr)]; wp->addr = addr; wp->val = data; ptrace(IWRITE, p->pid, addr, data); break; case DATASEG: # if (isvaxpx) if (addr >= ENDOFF && addr < ENDOFF + objsize) { wp = &p->word[cachehash(addr)]; wp->addr = addr; wp->val = data; } # endif ptrace(DWRITE, p->pid, addr, data); break; default: panic("store: bad seg %d", seg); /*NOTREACHED*/ } } /* * Initialize the instruction cache for a process. * This is particularly necessary after the program has been remade. */ initcache(process) PROCESS *process; { register int i; for (i = 0; i < CSIZE; i++) { process->word[i].addr = 0; } } /* * Swap file numbers so as to redirect standard input and output. */ LOCAL fswap(oldfd, newfd) int oldfd; int newfd; { if (oldfd != newfd) { close(oldfd); dup(newfd); close(newfd); } }