122510Sdist /* 222510Sdist * Copyright (c) 1980 Regents of the University of California. 322510Sdist * All rights reserved. The Berkeley software License Agreement 422510Sdist * specifies the terms and conditions for redistribution. 522510Sdist */ 610768Slinton 722510Sdist #ifndef lint 8*45437Sbostic static char sccsid[] = "@(#)ptrace.c 5.4 (Berkeley) 10/30/90"; 922510Sdist #endif not lint 105507Slinton 115507Slinton /* 125507Slinton * routines for tracing the execution of a process 135507Slinton * 145507Slinton * The system call "ptrace" does all the work, these 155507Slinton * routines just try to interface easily to it. 165507Slinton */ 175507Slinton 185507Slinton #include "defs.h" 195507Slinton #include <signal.h> 205507Slinton #include <sys/param.h> 2110004Ssam #include <machine/reg.h> 225507Slinton #include "process.h" 235507Slinton #include "object.h" 245507Slinton #include "process.rep" 255507Slinton 266072Slinton # include "pxinfo.h" 275507Slinton 2830848Smckusick #ifdef mc68000 2910768Slinton # define U_PAGE 0x2400 3010768Slinton # define U_AR0 (14*sizeof(int)) 3110768Slinton LOCAL int ar0val = -1; 3210768Slinton #endif 3310768Slinton 345507Slinton /* 355507Slinton * This magic macro enables us to look at the process' registers 365507Slinton * in its user structure. Very gross. 375507Slinton */ 385507Slinton 3930848Smckusick #if defined(vax) || defined(tahoe) 4010768Slinton # define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) )) 4110768Slinton #else 4210768Slinton # define regloc(reg) (ar0val + ( sizeof(int) * (reg) )) 4310768Slinton #endif 445507Slinton 456072Slinton #define WMASK (~(sizeof(WORD) - 1)) 466072Slinton #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE)) 475507Slinton 486072Slinton #define ischild(pid) ((pid) == 0) 496072Slinton #define traceme() ptrace(0, 0, 0, 0) 506072Slinton #define setrep(n) (1 << ((n)-1)) 516072Slinton #define istraced(p) (p->sigset&setrep(p->signo)) 525507Slinton 535507Slinton /* 545507Slinton * ptrace options (specified in first argument) 555507Slinton */ 565507Slinton 576072Slinton #define UREAD 3 /* read from process's user structure */ 586072Slinton #define UWRITE 6 /* write to process's user structure */ 596072Slinton #define IREAD 1 /* read from process's instruction space */ 606072Slinton #define IWRITE 4 /* write to process's instruction space */ 616072Slinton #define DREAD 2 /* read from process's data space */ 626072Slinton #define DWRITE 5 /* write to process's data space */ 636072Slinton #define CONT 7 /* continue stopped process */ 646072Slinton #define SSTEP 9 /* continue for approximately one instruction */ 656072Slinton #define PKILL 8 /* terminate the process */ 665507Slinton 675507Slinton /* 685507Slinton * Start up a new process by forking and exec-ing the 695507Slinton * given argument list, returning when the process is loaded 705507Slinton * and ready to execute. The PROCESS information (pointed to 715507Slinton * by the first argument) is appropriately filled. 725507Slinton * 735507Slinton * If the given PROCESS structure is associated with an already running 745507Slinton * process, we terminate it. 755507Slinton */ 765507Slinton 775507Slinton /* VARARGS2 */ 785607Slinton pstart(p, cmd, argv, infile, outfile) 795507Slinton PROCESS *p; 805607Slinton char *cmd; 815507Slinton char **argv; 825507Slinton char *infile; 835507Slinton char *outfile; 845507Slinton { 856072Slinton int status; 866072Slinton FILE *in, *out; 875507Slinton 886072Slinton if (p->pid != 0) { /* child already running? */ 896072Slinton ptrace(PKILL, p->pid, 0, 0); /* ... kill it! */ 906072Slinton } 9131082Smckusick #ifdef tahoe 9230848Smckusick INTFP = (ADDRESS)0; 9331082Smckusick #endif tahoe 946072Slinton psigtrace(p, SIGTRAP, TRUE); 956072Slinton if ((p->pid = fork()) == -1) { 966072Slinton panic("can't fork"); 976072Slinton } 986072Slinton if (ischild(p->pid)) { 996072Slinton traceme(); 1006072Slinton if (infile != NIL) { 1016072Slinton if ((in = fopen(infile, "r")) == NIL) { 1026072Slinton printf("can't read %s\n", infile); 1036072Slinton exit(1); 1046072Slinton } 1056072Slinton fswap(0, fileno(in)); 1065507Slinton } 1076072Slinton if (outfile != NIL) { 1086072Slinton if ((out = fopen(outfile, "w")) == NIL) { 1096072Slinton printf("can't write %s\n", outfile); 1106072Slinton exit(1); 1116072Slinton } 1126072Slinton fswap(1, fileno(out)); 1135507Slinton } 1146072Slinton execvp(cmd, argv); 1156072Slinton panic("can't exec %s", argv[0]); 1166072Slinton } 1176072Slinton pwait(p->pid, &status); 1186072Slinton getinfo(p, status); 1195507Slinton } 1205507Slinton 1215507Slinton /* 1225507Slinton * Continue a stopped process. The argument points to a PROCESS structure. 1235507Slinton * Before the process is restarted it's user area is modified according to 1245507Slinton * the values in the structure. When this routine finishes, 1255507Slinton * the structure has the new values from the process's user area. 1265507Slinton * 1275507Slinton * Pcont terminates when the process stops with a signal pending that 1285507Slinton * is being traced (via psigtrace), or when the process terminates. 1295507Slinton */ 1305507Slinton 1315507Slinton pcont(p) 1325507Slinton PROCESS *p; 1335507Slinton { 1346072Slinton int status; 1355507Slinton 1366072Slinton if (p->pid == 0) { 1376072Slinton error("program not active"); 1386072Slinton } 1396072Slinton do { 1406072Slinton setinfo(p); 1416072Slinton sigs_off(); 1426072Slinton if (ptrace(CONT, p->pid, p->pc, p->signo) < 0) { 1436072Slinton panic("can't continue process"); 1445507Slinton } 1456072Slinton pwait(p->pid, &status); 1466072Slinton sigs_on(); 1476072Slinton getinfo(p, status); 1486072Slinton } while (p->status == STOPPED && !istraced(p)); 1495507Slinton } 1505507Slinton 1515507Slinton /* 1525507Slinton * single step as best ptrace can 1535507Slinton */ 1545507Slinton 1555507Slinton pstep(p) 1565507Slinton PROCESS *p; 1575507Slinton { 1586072Slinton int status; 1595507Slinton 1606072Slinton setinfo(p); 1616072Slinton sigs_off(); 1626072Slinton ptrace(SSTEP, p->pid, p->pc, p->signo); 1636072Slinton pwait(p->pid, &status); 1646072Slinton sigs_on(); 1656072Slinton getinfo(p, status); 1665507Slinton } 1675507Slinton 1685507Slinton /* 1695507Slinton * Return from execution when the given signal is pending. 1705507Slinton */ 1715507Slinton 1725507Slinton psigtrace(p, sig, sw) 1735507Slinton PROCESS *p; 1745507Slinton int sig; 1755507Slinton int sw; 1765507Slinton { 1776072Slinton if (sw) { 1786072Slinton p->sigset |= setrep(sig); 1796072Slinton } else { 1806072Slinton p->sigset &= ~setrep(sig); 1816072Slinton } 1825507Slinton } 1835507Slinton 1845507Slinton /* 1855507Slinton * Don't catch any signals. 1865507Slinton * Particularly useful when letting a process finish uninhibited (i.e. px). 1875507Slinton */ 1885507Slinton 1895507Slinton unsetsigtraces(p) 1905507Slinton PROCESS *p; 1915507Slinton { 1926072Slinton p->sigset = 0; 1935507Slinton } 1945507Slinton 1955507Slinton /* 1965507Slinton * turn off attention to signals not being caught 1975507Slinton */ 1985507Slinton 199*45437Sbostic LOCAL void *onintr, *onquit; 2005507Slinton 2015507Slinton LOCAL sigs_off() 2025507Slinton { 20330848Smckusick onintr = signal(SIGINT, SIG_IGN); 20430848Smckusick onquit = signal(SIGQUIT, SIG_IGN); 2055507Slinton } 2065507Slinton 2075507Slinton /* 2085507Slinton * turn back on attention to signals 2095507Slinton */ 2105507Slinton 2115507Slinton LOCAL sigs_on() 2125507Slinton { 21330848Smckusick (void) signal(SIGINT, onintr); 21430848Smckusick (void) signal(SIGQUIT, onquit); 2155507Slinton } 2165507Slinton 2175507Slinton /* 2185507Slinton * get PROCESS information from process's user area 2195507Slinton */ 2205507Slinton 22110768Slinton #if vax 22210768Slinton LOCAL int rloc[] ={ 22310768Slinton R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, 22410768Slinton }; 22530848Smckusick #endif 22630848Smckusick #if tahoe 22710768Slinton LOCAL int rloc[] ={ 22830848Smckusick R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, 22930848Smckusick }; 23030848Smckusick #endif 23130848Smckusick #if mc68000 23230848Smckusick LOCAL int rloc[] ={ 23310768Slinton R0, R1, R2, R3, R4, R5, R6, R7, AR0, AR1, AR2, AR3, AR4, AR5, 23410768Slinton }; 23510768Slinton #endif 2365507Slinton 2375507Slinton LOCAL getinfo(p, status) 2385507Slinton register PROCESS *p; 2395507Slinton register int status; 2405507Slinton { 2416072Slinton register int i; 2425507Slinton 2436072Slinton p->signo = (status&0177); 2446072Slinton p->exitval = ((status >> 8)&0377); 2456072Slinton if (p->signo == STOPPED) { 2466072Slinton p->status = p->signo; 2476072Slinton p->signo = p->exitval; 2486072Slinton p->exitval = 0; 2496072Slinton } else { 2506072Slinton p->status = FINISHED; 2516072Slinton return; 2526072Slinton } 25330848Smckusick #if !defined(vax) && !defined(tahoe) 25410768Slinton if (ar0val < 0){ 25510768Slinton ar0val = ptrace(UREAD, p->pid, U_AR0, 0); 25610768Slinton ar0val -= U_PAGE; 25710768Slinton } 25810768Slinton #endif 2596072Slinton for (i = 0; i < NREG; i++) { 2606072Slinton p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0); 2616072Slinton p->oreg[i] = p->reg[i]; 2626072Slinton } 26330848Smckusick #if defined(vax) || defined(tahoe) 2646072Slinton p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0); 2656072Slinton p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); 2666072Slinton p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); 26730848Smckusick #endif 26830848Smckusick #ifdef vax 26930848Smckusick p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0); 27030848Smckusick #endif 27130848Smckusick #ifdef mc68000 27210768Slinton p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(AR6), 0); 27310768Slinton p->ap = p->oap = p->fp; 27410768Slinton p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); 27510768Slinton p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); 27610768Slinton #endif 2775507Slinton } 2785507Slinton 2795507Slinton /* 2805507Slinton * set process's user area information from given PROCESS structure 2815507Slinton */ 2825507Slinton 2835507Slinton LOCAL setinfo(p) 2845507Slinton register PROCESS *p; 2855507Slinton { 2866072Slinton register int i; 2876072Slinton register int r; 2885507Slinton 2896072Slinton if (istraced(p)) { 2906072Slinton p->signo = 0; 2916072Slinton } 2926072Slinton for (i = 0; i < NREG; i++) { 2936072Slinton if ((r = p->reg[i]) != p->oreg[i]) { 2946072Slinton ptrace(UWRITE, p->pid, regloc(rloc[i]), r); 2955507Slinton } 2966072Slinton } 29730848Smckusick #if vax || tahoe 2986072Slinton if ((r = p->fp) != p->ofp) { 2996072Slinton ptrace(UWRITE, p->pid, regloc(FP), r); 3006072Slinton } 30130848Smckusick #endif 30230848Smckusick #if vax 3036072Slinton if ((r = p->ap) != p->oap) { 3046072Slinton ptrace(UWRITE, p->pid, regloc(AP), r); 3056072Slinton } 30630848Smckusick #endif 30730848Smckusick #if mc68000 30810768Slinton if ((r = p->fp) != p->ofp) { 30910768Slinton ptrace(UWRITE, p->pid, regloc(AR6), r); 31010768Slinton } 31130848Smckusick #endif 31210768Slinton if ((r = p->sp) != p->osp) { 31310768Slinton ptrace(UWRITE, p->pid, regloc(SP), r); 31410768Slinton } 3156072Slinton if ((r = p->pc) != p->opc) { 3166072Slinton ptrace(UWRITE, p->pid, regloc(PC), r); 3176072Slinton } 3185507Slinton } 3195507Slinton 3205507Slinton /* 3215507Slinton * Structure for reading and writing by words, but dealing with bytes. 3225507Slinton */ 3235507Slinton 3245507Slinton typedef union { 3256072Slinton WORD pword; 3266072Slinton BYTE pbyte[sizeof(WORD)]; 3275507Slinton } PWORD; 3285507Slinton 3295507Slinton /* 3305507Slinton * Read (write) from (to) the process' address space. 3315507Slinton * We must deal with ptrace's inability to look anywhere other 3325507Slinton * than at a word boundary. 3335507Slinton */ 3345507Slinton 3355507Slinton LOCAL WORD fetch(); 3365507Slinton LOCAL store(); 3375507Slinton 3385507Slinton pio(p, op, seg, buff, addr, nbytes) 3395507Slinton PROCESS *p; 3405507Slinton PIO_OP op; 3415507Slinton PIO_SEG seg; 3425507Slinton char *buff; 3435507Slinton ADDRESS addr; 3445507Slinton int nbytes; 3455507Slinton { 34630848Smckusick register int i, k; 3476072Slinton register ADDRESS newaddr; 3486072Slinton register char *cp; 3496072Slinton char *bufend; 3506072Slinton PWORD w; 3516072Slinton ADDRESS wordaddr; 3526072Slinton int byteoff; 3535507Slinton 3546072Slinton if (p->status != STOPPED) { 3556072Slinton error("program is not active"); 3566072Slinton } 3576072Slinton cp = buff; 3586072Slinton newaddr = addr; 3596072Slinton wordaddr = (newaddr&WMASK); 3606072Slinton if (wordaddr != newaddr) { 3616072Slinton w.pword = fetch(p, seg, wordaddr); 3626072Slinton for (i = newaddr - wordaddr; i<sizeof(WORD) && nbytes>0; i++) { 3636072Slinton if (op == PREAD) { 3646072Slinton *cp++ = w.pbyte[i]; 3656072Slinton } else { 3666072Slinton w.pbyte[i] = *cp++; 3676072Slinton } 3686072Slinton nbytes--; 3695507Slinton } 3706072Slinton if (op == PWRITE) { 3716072Slinton store(p, seg, wordaddr, w.pword); 3725507Slinton } 3736072Slinton newaddr = wordaddr + sizeof(WORD); 3746072Slinton } 3756072Slinton byteoff = (nbytes&(~WMASK)); 3766072Slinton nbytes -= byteoff; 3776072Slinton bufend = cp + nbytes; 3786072Slinton while (cp < bufend) { 3796072Slinton if (op == PREAD) { 38030848Smckusick w.pword = fetch(p, seg, newaddr); 38130848Smckusick for (k = 0; k < sizeof(WORD); k++) { 38230848Smckusick *cp++ = w.pbyte[k]; 38330848Smckusick } 3846072Slinton } else { 38530848Smckusick for (k = 0; k < sizeof(WORD); k++) { 38630848Smckusick w.pbyte[k] = *cp++; 38730848Smckusick } 38830848Smckusick store(p, seg, newaddr, w.pword); 3895507Slinton } 3906072Slinton newaddr += sizeof(WORD); 3916072Slinton } 3926072Slinton if (byteoff > 0) { 3936072Slinton w.pword = fetch(p, seg, newaddr); 3946072Slinton for (i = 0; i < byteoff; i++) { 3956072Slinton if (op == PREAD) { 3966072Slinton *cp++ = w.pbyte[i]; 3976072Slinton } else { 3986072Slinton w.pbyte[i] = *cp++; 3996072Slinton } 4005507Slinton } 4016072Slinton if (op == PWRITE) { 4026072Slinton store(p, seg, newaddr, w.pword); 4036072Slinton } 4046072Slinton } 4055507Slinton } 4065507Slinton 4075507Slinton /* 4085507Slinton * Get a word from a process at the given address. 4095507Slinton * The address is assumed to be on a word boundary. 4105507Slinton * 4115507Slinton * We use a simple cache scheme to avoid redundant references to 4125507Slinton * the instruction space (which is assumed to be pure). In the 4135507Slinton * case of px, the "instruction" space lies between ENDOFF and 4145507Slinton * ENDOFF + objsize. 4155507Slinton * 4165507Slinton * It is necessary to use a write-through scheme so that 4175507Slinton * breakpoints right next to each other don't interfere. 4185507Slinton */ 4195507Slinton 4205507Slinton LOCAL WORD fetch(p, seg, addr) 4215507Slinton PROCESS *p; 4225507Slinton PIO_SEG seg; 4235507Slinton register int addr; 4245507Slinton { 4256072Slinton register CACHEWORD *wp; 4266072Slinton register WORD w; 4275507Slinton 4286072Slinton switch (seg) { 4296072Slinton case TEXTSEG: 43030848Smckusick panic("tried to fetch from px i-space"); 43130848Smckusick /* NOTREACHED */ 43230848Smckusick 43330848Smckusick case DATASEG: 43430848Smckusick if (addr >= ENDOFF && addr < ENDOFF + objsize) { 4356072Slinton wp = &p->word[cachehash(addr)]; 4366072Slinton if (addr == 0 || wp->addr != addr) { 43730848Smckusick w = ptrace(DREAD, p->pid, addr, 0); 4386072Slinton wp->addr = addr; 4396072Slinton wp->val = w; 4406072Slinton } else { 4416072Slinton w = wp->val; 4426072Slinton } 44330848Smckusick } else { 4446072Slinton w = ptrace(DREAD, p->pid, addr, 0); 44530848Smckusick } 4466072Slinton break; 4475507Slinton 4486072Slinton default: 4496072Slinton panic("fetch: bad seg %d", seg); 4506072Slinton /* NOTREACHED */ 4516072Slinton } 4526072Slinton return(w); 4535507Slinton } 4545507Slinton 4555507Slinton /* 4565507Slinton * Put a word into the process' address space at the given address. 4575507Slinton * The address is assumed to be on a word boundary. 4585507Slinton */ 4595507Slinton 4605507Slinton LOCAL store(p, seg, addr, data) 4615507Slinton PROCESS *p; 4625507Slinton PIO_SEG seg; 4635507Slinton int addr; 4645507Slinton WORD data; 4655507Slinton { 4666072Slinton register CACHEWORD *wp; 4675507Slinton 4686072Slinton switch (seg) { 4696072Slinton case TEXTSEG: 4706072Slinton wp = &p->word[cachehash(addr)]; 4716072Slinton wp->addr = addr; 4726072Slinton wp->val = data; 4736072Slinton ptrace(IWRITE, p->pid, addr, data); 4746072Slinton break; 4755507Slinton 4766072Slinton case DATASEG: 47730848Smckusick if (addr >= ENDOFF && addr < ENDOFF + objsize) { 47830848Smckusick wp = &p->word[cachehash(addr)]; 47930848Smckusick wp->addr = addr; 48030848Smckusick wp->val = data; 48130848Smckusick } 4826072Slinton ptrace(DWRITE, p->pid, addr, data); 4836072Slinton break; 4845507Slinton 4856072Slinton default: 4866072Slinton panic("store: bad seg %d", seg); 4876072Slinton /*NOTREACHED*/ 4886072Slinton } 4895507Slinton } 4905507Slinton 4915507Slinton /* 4926072Slinton * Initialize the instruction cache for a process. 4936072Slinton * This is particularly necessary after the program has been remade. 4946072Slinton */ 4956072Slinton 4966072Slinton initcache(process) 4976072Slinton PROCESS *process; 4986072Slinton { 4996072Slinton register int i; 5006072Slinton 5016072Slinton for (i = 0; i < CSIZE; i++) { 5026072Slinton process->word[i].addr = 0; 5036072Slinton } 5046072Slinton } 5056072Slinton 5066072Slinton /* 5075507Slinton * Swap file numbers so as to redirect standard input and output. 5085507Slinton */ 5095507Slinton 5105507Slinton LOCAL fswap(oldfd, newfd) 5115507Slinton int oldfd; 5125507Slinton int newfd; 5135507Slinton { 5146072Slinton if (oldfd != newfd) { 5156072Slinton close(oldfd); 5166072Slinton dup(newfd); 5176072Slinton close(newfd); 5186072Slinton } 5195507Slinton } 52030848Smckusick 52130848Smckusick #ifdef tahoe 52230848Smckusick BOOLEAN didret; 52330848Smckusick 52430848Smckusick void 52530848Smckusick chkret(p, status) 52630848Smckusick PROCESS *p; 52730848Smckusick int status; 52830848Smckusick { 52930848Smckusick if (((status == (SIGILL << 8) | STOPPED) || 53030848Smckusick (status == (SIGTRAP << 8) | STOPPED))) { 53130848Smckusick didret = FALSE; 53230848Smckusick } else { 53330848Smckusick didret = TRUE; 53430848Smckusick } 53530848Smckusick } 53630848Smckusick 53730848Smckusick void 53830848Smckusick doret(p) 53930848Smckusick PROCESS *p; 54030848Smckusick { 54130848Smckusick register count = 0; 54230848Smckusick 54330848Smckusick if (!didret) { 54430848Smckusick do { 54530848Smckusick if (++count > 5) { 54630848Smckusick panic("px would not return to interpreter"); 54730848Smckusick } 54830848Smckusick p->pc = RETLOC; 54930848Smckusick pstep(p); 55030848Smckusick } while(INTFP && p->fp != INTFP); 55130848Smckusick didret = TRUE; 55230848Smckusick } 55330848Smckusick } 55430848Smckusick #endif 555