1*10768Slinton 25507Slinton /* Copyright (c) 1982 Regents of the University of California */ 35507Slinton 410004Ssam static char sccsid[] = "@(#)ptrace.c 1.4 12/29/82"; 55507Slinton 65507Slinton /* 75507Slinton * routines for tracing the execution of a process 85507Slinton * 95507Slinton * The system call "ptrace" does all the work, these 105507Slinton * routines just try to interface easily to it. 115507Slinton */ 125507Slinton 135507Slinton #include "defs.h" 145507Slinton #include <signal.h> 155507Slinton #include <sys/param.h> 1610004Ssam #include <machine/reg.h> 175507Slinton #include "process.h" 185507Slinton #include "object.h" 195507Slinton #include "process.rep" 205507Slinton 216072Slinton # if (isvaxpx) 226072Slinton # include "pxinfo.h" 236072Slinton # endif 245507Slinton 25*10768Slinton #ifndef vax 26*10768Slinton # define U_PAGE 0x2400 27*10768Slinton # define U_AR0 (14*sizeof(int)) 28*10768Slinton LOCAL int ar0val = -1; 29*10768Slinton #endif 30*10768Slinton 315507Slinton /* 325507Slinton * This magic macro enables us to look at the process' registers 335507Slinton * in its user structure. Very gross. 345507Slinton */ 355507Slinton 36*10768Slinton #ifdef vax 37*10768Slinton # define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) )) 38*10768Slinton #else 39*10768Slinton # define regloc(reg) (ar0val + ( sizeof(int) * (reg) )) 40*10768Slinton #endif 415507Slinton 426072Slinton #define WMASK (~(sizeof(WORD) - 1)) 436072Slinton #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE)) 445507Slinton 456072Slinton #define FIRSTSIG SIGINT 466072Slinton #define LASTSIG SIGQUIT 476072Slinton #define ischild(pid) ((pid) == 0) 486072Slinton #define traceme() ptrace(0, 0, 0, 0) 496072Slinton #define setrep(n) (1 << ((n)-1)) 506072Slinton #define istraced(p) (p->sigset&setrep(p->signo)) 515507Slinton 525507Slinton /* 535507Slinton * ptrace options (specified in first argument) 545507Slinton */ 555507Slinton 566072Slinton #define UREAD 3 /* read from process's user structure */ 576072Slinton #define UWRITE 6 /* write to process's user structure */ 586072Slinton #define IREAD 1 /* read from process's instruction space */ 596072Slinton #define IWRITE 4 /* write to process's instruction space */ 606072Slinton #define DREAD 2 /* read from process's data space */ 616072Slinton #define DWRITE 5 /* write to process's data space */ 626072Slinton #define CONT 7 /* continue stopped process */ 636072Slinton #define SSTEP 9 /* continue for approximately one instruction */ 646072Slinton #define PKILL 8 /* terminate the process */ 655507Slinton 665507Slinton /* 675507Slinton * Start up a new process by forking and exec-ing the 685507Slinton * given argument list, returning when the process is loaded 695507Slinton * and ready to execute. The PROCESS information (pointed to 705507Slinton * by the first argument) is appropriately filled. 715507Slinton * 725507Slinton * If the given PROCESS structure is associated with an already running 735507Slinton * process, we terminate it. 745507Slinton */ 755507Slinton 765507Slinton /* VARARGS2 */ 775607Slinton pstart(p, cmd, argv, infile, outfile) 785507Slinton PROCESS *p; 795607Slinton char *cmd; 805507Slinton char **argv; 815507Slinton char *infile; 825507Slinton char *outfile; 835507Slinton { 846072Slinton int status; 856072Slinton FILE *in, *out; 865507Slinton 876072Slinton if (p->pid != 0) { /* child already running? */ 886072Slinton ptrace(PKILL, p->pid, 0, 0); /* ... kill it! */ 896072Slinton } 906072Slinton psigtrace(p, SIGTRAP, TRUE); 916072Slinton if ((p->pid = fork()) == -1) { 926072Slinton panic("can't fork"); 936072Slinton } 946072Slinton if (ischild(p->pid)) { 956072Slinton traceme(); 966072Slinton if (infile != NIL) { 976072Slinton if ((in = fopen(infile, "r")) == NIL) { 986072Slinton printf("can't read %s\n", infile); 996072Slinton exit(1); 1006072Slinton } 1016072Slinton fswap(0, fileno(in)); 1025507Slinton } 1036072Slinton if (outfile != NIL) { 1046072Slinton if ((out = fopen(outfile, "w")) == NIL) { 1056072Slinton printf("can't write %s\n", outfile); 1066072Slinton exit(1); 1076072Slinton } 1086072Slinton fswap(1, fileno(out)); 1095507Slinton } 1106072Slinton execvp(cmd, argv); 1116072Slinton panic("can't exec %s", argv[0]); 1126072Slinton } 1136072Slinton pwait(p->pid, &status); 1146072Slinton getinfo(p, status); 1155507Slinton } 1165507Slinton 1175507Slinton /* 1185507Slinton * Continue a stopped process. The argument points to a PROCESS structure. 1195507Slinton * Before the process is restarted it's user area is modified according to 1205507Slinton * the values in the structure. When this routine finishes, 1215507Slinton * the structure has the new values from the process's user area. 1225507Slinton * 1235507Slinton * Pcont terminates when the process stops with a signal pending that 1245507Slinton * is being traced (via psigtrace), or when the process terminates. 1255507Slinton */ 1265507Slinton 1275507Slinton pcont(p) 1285507Slinton PROCESS *p; 1295507Slinton { 1306072Slinton int status; 1315507Slinton 1326072Slinton if (p->pid == 0) { 1336072Slinton error("program not active"); 1346072Slinton } 1356072Slinton do { 1366072Slinton setinfo(p); 1376072Slinton sigs_off(); 1386072Slinton if (ptrace(CONT, p->pid, p->pc, p->signo) < 0) { 1396072Slinton panic("can't continue process"); 1405507Slinton } 1416072Slinton pwait(p->pid, &status); 1426072Slinton sigs_on(); 1436072Slinton getinfo(p, status); 1446072Slinton } while (p->status == STOPPED && !istraced(p)); 1455507Slinton } 1465507Slinton 1475507Slinton /* 1485507Slinton * single step as best ptrace can 1495507Slinton */ 1505507Slinton 1515507Slinton pstep(p) 1525507Slinton PROCESS *p; 1535507Slinton { 1546072Slinton int status; 1555507Slinton 1566072Slinton setinfo(p); 1576072Slinton sigs_off(); 1586072Slinton ptrace(SSTEP, p->pid, p->pc, p->signo); 1596072Slinton pwait(p->pid, &status); 1606072Slinton sigs_on(); 1616072Slinton getinfo(p, status); 1625507Slinton } 1635507Slinton 1645507Slinton /* 1655507Slinton * Return from execution when the given signal is pending. 1665507Slinton */ 1675507Slinton 1685507Slinton psigtrace(p, sig, sw) 1695507Slinton PROCESS *p; 1705507Slinton int sig; 1715507Slinton int sw; 1725507Slinton { 1736072Slinton if (sw) { 1746072Slinton p->sigset |= setrep(sig); 1756072Slinton } else { 1766072Slinton p->sigset &= ~setrep(sig); 1776072Slinton } 1785507Slinton } 1795507Slinton 1805507Slinton /* 1815507Slinton * Don't catch any signals. 1825507Slinton * Particularly useful when letting a process finish uninhibited (i.e. px). 1835507Slinton */ 1845507Slinton 1855507Slinton unsetsigtraces(p) 1865507Slinton PROCESS *p; 1875507Slinton { 1886072Slinton p->sigset = 0; 1895507Slinton } 1905507Slinton 1915507Slinton /* 1925507Slinton * turn off attention to signals not being caught 1935507Slinton */ 1945507Slinton 1955507Slinton typedef int INTFUNC(); 1965507Slinton 1975507Slinton LOCAL INTFUNC *sigfunc[NSIG]; 1985507Slinton 1995507Slinton LOCAL sigs_off() 2005507Slinton { 2016072Slinton register int i; 2025507Slinton 2036072Slinton for (i = FIRSTSIG; i < LASTSIG; i++) { 2046072Slinton if (i != SIGKILL) { 2056072Slinton sigfunc[i] = signal(i, SIG_IGN); 2065507Slinton } 2076072Slinton } 2085507Slinton } 2095507Slinton 2105507Slinton /* 2115507Slinton * turn back on attention to signals 2125507Slinton */ 2135507Slinton 2145507Slinton LOCAL sigs_on() 2155507Slinton { 2166072Slinton register int i; 2175507Slinton 2186072Slinton for (i = FIRSTSIG; i < LASTSIG; i++) { 2196072Slinton if (i != SIGKILL) { 2206072Slinton signal(i, sigfunc[i]); 2215507Slinton } 2226072Slinton } 2235507Slinton } 2245507Slinton 2255507Slinton /* 2265507Slinton * get PROCESS information from process's user area 2275507Slinton */ 2285507Slinton 229*10768Slinton #if vax 230*10768Slinton LOCAL int rloc[] ={ 231*10768Slinton R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, 232*10768Slinton }; 233*10768Slinton #else 234*10768Slinton LOCAL int rloc[] ={ 235*10768Slinton R0, R1, R2, R3, R4, R5, R6, R7, AR0, AR1, AR2, AR3, AR4, AR5, 236*10768Slinton }; 237*10768Slinton #endif 2385507Slinton 2395507Slinton LOCAL getinfo(p, status) 2405507Slinton register PROCESS *p; 2415507Slinton register int status; 2425507Slinton { 2436072Slinton register int i; 2445507Slinton 2456072Slinton p->signo = (status&0177); 2466072Slinton p->exitval = ((status >> 8)&0377); 2476072Slinton if (p->signo == STOPPED) { 2486072Slinton p->status = p->signo; 2496072Slinton p->signo = p->exitval; 2506072Slinton p->exitval = 0; 2516072Slinton } else { 2526072Slinton p->status = FINISHED; 2536072Slinton return; 2546072Slinton } 255*10768Slinton #ifndef vax 256*10768Slinton if (ar0val < 0){ 257*10768Slinton ar0val = ptrace(UREAD, p->pid, U_AR0, 0); 258*10768Slinton ar0val -= U_PAGE; 259*10768Slinton } 260*10768Slinton #endif 2616072Slinton for (i = 0; i < NREG; i++) { 2626072Slinton p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0); 2636072Slinton p->oreg[i] = p->reg[i]; 2646072Slinton } 265*10768Slinton #ifdef vax 2666072Slinton p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0); 2676072Slinton p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0); 2686072Slinton p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); 2696072Slinton p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); 270*10768Slinton #else 271*10768Slinton p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(AR6), 0); 272*10768Slinton p->ap = p->oap = p->fp; 273*10768Slinton p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); 274*10768Slinton p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); 275*10768Slinton #endif 2765507Slinton } 2775507Slinton 2785507Slinton /* 2795507Slinton * set process's user area information from given PROCESS structure 2805507Slinton */ 2815507Slinton 2825507Slinton LOCAL setinfo(p) 2835507Slinton register PROCESS *p; 2845507Slinton { 2856072Slinton register int i; 2866072Slinton register int r; 2875507Slinton 2886072Slinton if (istraced(p)) { 2896072Slinton p->signo = 0; 2906072Slinton } 2916072Slinton for (i = 0; i < NREG; i++) { 2926072Slinton if ((r = p->reg[i]) != p->oreg[i]) { 2936072Slinton ptrace(UWRITE, p->pid, regloc(rloc[i]), r); 2945507Slinton } 2956072Slinton } 296*10768Slinton #if vax 2976072Slinton if ((r = p->fp) != p->ofp) { 2986072Slinton ptrace(UWRITE, p->pid, regloc(FP), r); 2996072Slinton } 3006072Slinton if ((r = p->sp) != p->osp) { 3016072Slinton ptrace(UWRITE, p->pid, regloc(SP), r); 3026072Slinton } 3036072Slinton if ((r = p->ap) != p->oap) { 3046072Slinton ptrace(UWRITE, p->pid, regloc(AP), r); 3056072Slinton } 306*10768Slinton #else 307*10768Slinton if ((r = p->fp) != p->ofp) { 308*10768Slinton ptrace(UWRITE, p->pid, regloc(AR6), r); 309*10768Slinton } 310*10768Slinton if ((r = p->sp) != p->osp) { 311*10768Slinton ptrace(UWRITE, p->pid, regloc(SP), r); 312*10768Slinton } 313*10768Slinton #endif 3146072Slinton if ((r = p->pc) != p->opc) { 3156072Slinton ptrace(UWRITE, p->pid, regloc(PC), r); 3166072Slinton } 3175507Slinton } 3185507Slinton 3195507Slinton /* 3205507Slinton * Structure for reading and writing by words, but dealing with bytes. 3215507Slinton */ 3225507Slinton 3235507Slinton typedef union { 3246072Slinton WORD pword; 3256072Slinton BYTE pbyte[sizeof(WORD)]; 3265507Slinton } PWORD; 3275507Slinton 3285507Slinton /* 3295507Slinton * Read (write) from (to) the process' address space. 3305507Slinton * We must deal with ptrace's inability to look anywhere other 3315507Slinton * than at a word boundary. 3325507Slinton */ 3335507Slinton 3345507Slinton LOCAL WORD fetch(); 3355507Slinton LOCAL store(); 3365507Slinton 3375507Slinton pio(p, op, seg, buff, addr, nbytes) 3385507Slinton PROCESS *p; 3395507Slinton PIO_OP op; 3405507Slinton PIO_SEG seg; 3415507Slinton char *buff; 3425507Slinton ADDRESS addr; 3435507Slinton int nbytes; 3445507Slinton { 3456072Slinton register int i; 3466072Slinton register ADDRESS newaddr; 3476072Slinton register char *cp; 3486072Slinton char *bufend; 3496072Slinton PWORD w; 3506072Slinton ADDRESS wordaddr; 3516072Slinton int byteoff; 3525507Slinton 3536072Slinton if (p->status != STOPPED) { 3546072Slinton error("program is not active"); 3556072Slinton } 3566072Slinton cp = buff; 3576072Slinton newaddr = addr; 3586072Slinton wordaddr = (newaddr&WMASK); 3596072Slinton if (wordaddr != newaddr) { 3606072Slinton w.pword = fetch(p, seg, wordaddr); 3616072Slinton for (i = newaddr - wordaddr; i<sizeof(WORD) && nbytes>0; i++) { 3626072Slinton if (op == PREAD) { 3636072Slinton *cp++ = w.pbyte[i]; 3646072Slinton } else { 3656072Slinton w.pbyte[i] = *cp++; 3666072Slinton } 3676072Slinton nbytes--; 3685507Slinton } 3696072Slinton if (op == PWRITE) { 3706072Slinton store(p, seg, wordaddr, w.pword); 3715507Slinton } 3726072Slinton newaddr = wordaddr + sizeof(WORD); 3736072Slinton } 3746072Slinton byteoff = (nbytes&(~WMASK)); 3756072Slinton nbytes -= byteoff; 3766072Slinton bufend = cp + nbytes; 3776072Slinton while (cp < bufend) { 3786072Slinton if (op == PREAD) { 3796072Slinton *((WORD *) cp) = fetch(p, seg, newaddr); 3806072Slinton } else { 3816072Slinton store(p, seg, newaddr, *((WORD *) cp)); 3825507Slinton } 3836072Slinton cp += sizeof(WORD); 3846072Slinton newaddr += sizeof(WORD); 3856072Slinton } 3866072Slinton if (byteoff > 0) { 3876072Slinton w.pword = fetch(p, seg, newaddr); 3886072Slinton for (i = 0; i < byteoff; i++) { 3896072Slinton if (op == PREAD) { 3906072Slinton *cp++ = w.pbyte[i]; 3916072Slinton } else { 3926072Slinton w.pbyte[i] = *cp++; 3936072Slinton } 3945507Slinton } 3956072Slinton if (op == PWRITE) { 3966072Slinton store(p, seg, newaddr, w.pword); 3976072Slinton } 3986072Slinton } 3995507Slinton } 4005507Slinton 4015507Slinton /* 4025507Slinton * Get a word from a process at the given address. 4035507Slinton * The address is assumed to be on a word boundary. 4045507Slinton * 4055507Slinton * We use a simple cache scheme to avoid redundant references to 4065507Slinton * the instruction space (which is assumed to be pure). In the 4075507Slinton * case of px, the "instruction" space lies between ENDOFF and 4085507Slinton * ENDOFF + objsize. 4095507Slinton * 4105507Slinton * It is necessary to use a write-through scheme so that 4115507Slinton * breakpoints right next to each other don't interfere. 4125507Slinton */ 4135507Slinton 4145507Slinton LOCAL WORD fetch(p, seg, addr) 4155507Slinton PROCESS *p; 4165507Slinton PIO_SEG seg; 4175507Slinton register int addr; 4185507Slinton { 4196072Slinton register CACHEWORD *wp; 4206072Slinton register WORD w; 4215507Slinton 4226072Slinton switch (seg) { 4236072Slinton case TEXTSEG: 4246072Slinton # if (isvaxpx) 4256072Slinton panic("tried to fetch from px i-space"); 4266072Slinton /* NOTREACHED */ 4276072Slinton # else 4286072Slinton wp = &p->word[cachehash(addr)]; 4296072Slinton if (addr == 0 || wp->addr != addr) { 4306072Slinton w = ptrace(IREAD, p->pid, addr, 0); 4316072Slinton wp->addr = addr; 4326072Slinton wp->val = w; 4336072Slinton } else { 4346072Slinton w = wp->val; 4356072Slinton } 4366072Slinton break; 4376072Slinton # endif 4385507Slinton 4396072Slinton case DATASEG: 4406072Slinton # if (isvaxpx) 4416072Slinton if (addr >= ENDOFF && addr < ENDOFF + objsize) { 4426072Slinton wp = &p->word[cachehash(addr)]; 4436072Slinton if (addr == 0 || wp->addr != addr) { 4446072Slinton w = ptrace(DREAD, p->pid, addr, 0); 4456072Slinton wp->addr = addr; 4466072Slinton wp->val = w; 4476072Slinton } else { 4486072Slinton w = wp->val; 4496072Slinton } 4506072Slinton } else { 4516072Slinton w = ptrace(DREAD, p->pid, addr, 0); 4526072Slinton } 4536072Slinton # else 4546072Slinton w = ptrace(DREAD, p->pid, addr, 0); 4556072Slinton # endif 4566072Slinton break; 4575507Slinton 4586072Slinton default: 4596072Slinton panic("fetch: bad seg %d", seg); 4606072Slinton /* NOTREACHED */ 4616072Slinton } 4626072Slinton return(w); 4635507Slinton } 4645507Slinton 4655507Slinton /* 4665507Slinton * Put a word into the process' address space at the given address. 4675507Slinton * The address is assumed to be on a word boundary. 4685507Slinton */ 4695507Slinton 4705507Slinton LOCAL store(p, seg, addr, data) 4715507Slinton PROCESS *p; 4725507Slinton PIO_SEG seg; 4735507Slinton int addr; 4745507Slinton WORD data; 4755507Slinton { 4766072Slinton register CACHEWORD *wp; 4775507Slinton 4786072Slinton switch (seg) { 4796072Slinton case TEXTSEG: 4806072Slinton wp = &p->word[cachehash(addr)]; 4816072Slinton wp->addr = addr; 4826072Slinton wp->val = data; 4836072Slinton ptrace(IWRITE, p->pid, addr, data); 4846072Slinton break; 4855507Slinton 4866072Slinton case DATASEG: 4876072Slinton # if (isvaxpx) 4886072Slinton if (addr >= ENDOFF && addr < ENDOFF + objsize) { 4896072Slinton wp = &p->word[cachehash(addr)]; 4906072Slinton wp->addr = addr; 4916072Slinton wp->val = data; 4926072Slinton } 4936072Slinton # endif 4946072Slinton ptrace(DWRITE, p->pid, addr, data); 4956072Slinton break; 4965507Slinton 4976072Slinton default: 4986072Slinton panic("store: bad seg %d", seg); 4996072Slinton /*NOTREACHED*/ 5006072Slinton } 5015507Slinton } 5025507Slinton 5035507Slinton /* 5046072Slinton * Initialize the instruction cache for a process. 5056072Slinton * This is particularly necessary after the program has been remade. 5066072Slinton */ 5076072Slinton 5086072Slinton initcache(process) 5096072Slinton PROCESS *process; 5106072Slinton { 5116072Slinton register int i; 5126072Slinton 5136072Slinton for (i = 0; i < CSIZE; i++) { 5146072Slinton process->word[i].addr = 0; 5156072Slinton } 5166072Slinton } 5176072Slinton 5186072Slinton /* 5195507Slinton * Swap file numbers so as to redirect standard input and output. 5205507Slinton */ 5215507Slinton 5225507Slinton LOCAL fswap(oldfd, newfd) 5235507Slinton int oldfd; 5245507Slinton int newfd; 5255507Slinton { 5266072Slinton if (oldfd != newfd) { 5276072Slinton close(oldfd); 5286072Slinton dup(newfd); 5296072Slinton close(newfd); 5306072Slinton } 5315507Slinton } 532