1*22510Sdist /* 2*22510Sdist * Copyright (c) 1980 Regents of the University of California. 3*22510Sdist * All rights reserved. The Berkeley software License Agreement 4*22510Sdist * specifies the terms and conditions for redistribution. 5*22510Sdist */ 610768Slinton 7*22510Sdist #ifndef lint 8*22510Sdist static char sccsid[] = "@(#)ptrace.c 5.1 (Berkeley) 06/06/85"; 9*22510Sdist #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 # if (isvaxpx) 276072Slinton # include "pxinfo.h" 286072Slinton # endif 295507Slinton 3010768Slinton #ifndef vax 3110768Slinton # define U_PAGE 0x2400 3210768Slinton # define U_AR0 (14*sizeof(int)) 3310768Slinton LOCAL int ar0val = -1; 3410768Slinton #endif 3510768Slinton 365507Slinton /* 375507Slinton * This magic macro enables us to look at the process' registers 385507Slinton * in its user structure. Very gross. 395507Slinton */ 405507Slinton 4110768Slinton #ifdef vax 4210768Slinton # define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) )) 4310768Slinton #else 4410768Slinton # define regloc(reg) (ar0val + ( sizeof(int) * (reg) )) 4510768Slinton #endif 465507Slinton 476072Slinton #define WMASK (~(sizeof(WORD) - 1)) 486072Slinton #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE)) 495507Slinton 506072Slinton #define FIRSTSIG SIGINT 516072Slinton #define LASTSIG SIGQUIT 526072Slinton #define ischild(pid) ((pid) == 0) 536072Slinton #define traceme() ptrace(0, 0, 0, 0) 546072Slinton #define setrep(n) (1 << ((n)-1)) 556072Slinton #define istraced(p) (p->sigset&setrep(p->signo)) 565507Slinton 575507Slinton /* 585507Slinton * ptrace options (specified in first argument) 595507Slinton */ 605507Slinton 616072Slinton #define UREAD 3 /* read from process's user structure */ 626072Slinton #define UWRITE 6 /* write to process's user structure */ 636072Slinton #define IREAD 1 /* read from process's instruction space */ 646072Slinton #define IWRITE 4 /* write to process's instruction space */ 656072Slinton #define DREAD 2 /* read from process's data space */ 666072Slinton #define DWRITE 5 /* write to process's data space */ 676072Slinton #define CONT 7 /* continue stopped process */ 686072Slinton #define SSTEP 9 /* continue for approximately one instruction */ 696072Slinton #define PKILL 8 /* terminate the process */ 705507Slinton 715507Slinton /* 725507Slinton * Start up a new process by forking and exec-ing the 735507Slinton * given argument list, returning when the process is loaded 745507Slinton * and ready to execute. The PROCESS information (pointed to 755507Slinton * by the first argument) is appropriately filled. 765507Slinton * 775507Slinton * If the given PROCESS structure is associated with an already running 785507Slinton * process, we terminate it. 795507Slinton */ 805507Slinton 815507Slinton /* VARARGS2 */ 825607Slinton pstart(p, cmd, argv, infile, outfile) 835507Slinton PROCESS *p; 845607Slinton char *cmd; 855507Slinton char **argv; 865507Slinton char *infile; 875507Slinton char *outfile; 885507Slinton { 896072Slinton int status; 906072Slinton FILE *in, *out; 915507Slinton 926072Slinton if (p->pid != 0) { /* child already running? */ 936072Slinton ptrace(PKILL, p->pid, 0, 0); /* ... kill it! */ 946072Slinton } 956072Slinton psigtrace(p, SIGTRAP, TRUE); 966072Slinton if ((p->pid = fork()) == -1) { 976072Slinton panic("can't fork"); 986072Slinton } 996072Slinton if (ischild(p->pid)) { 1006072Slinton traceme(); 1016072Slinton if (infile != NIL) { 1026072Slinton if ((in = fopen(infile, "r")) == NIL) { 1036072Slinton printf("can't read %s\n", infile); 1046072Slinton exit(1); 1056072Slinton } 1066072Slinton fswap(0, fileno(in)); 1075507Slinton } 1086072Slinton if (outfile != NIL) { 1096072Slinton if ((out = fopen(outfile, "w")) == NIL) { 1106072Slinton printf("can't write %s\n", outfile); 1116072Slinton exit(1); 1126072Slinton } 1136072Slinton fswap(1, fileno(out)); 1145507Slinton } 1156072Slinton execvp(cmd, argv); 1166072Slinton panic("can't exec %s", argv[0]); 1176072Slinton } 1186072Slinton pwait(p->pid, &status); 1196072Slinton getinfo(p, status); 1205507Slinton } 1215507Slinton 1225507Slinton /* 1235507Slinton * Continue a stopped process. The argument points to a PROCESS structure. 1245507Slinton * Before the process is restarted it's user area is modified according to 1255507Slinton * the values in the structure. When this routine finishes, 1265507Slinton * the structure has the new values from the process's user area. 1275507Slinton * 1285507Slinton * Pcont terminates when the process stops with a signal pending that 1295507Slinton * is being traced (via psigtrace), or when the process terminates. 1305507Slinton */ 1315507Slinton 1325507Slinton pcont(p) 1335507Slinton PROCESS *p; 1345507Slinton { 1356072Slinton int status; 1365507Slinton 1376072Slinton if (p->pid == 0) { 1386072Slinton error("program not active"); 1396072Slinton } 1406072Slinton do { 1416072Slinton setinfo(p); 1426072Slinton sigs_off(); 1436072Slinton if (ptrace(CONT, p->pid, p->pc, p->signo) < 0) { 1446072Slinton panic("can't continue process"); 1455507Slinton } 1466072Slinton pwait(p->pid, &status); 1476072Slinton sigs_on(); 1486072Slinton getinfo(p, status); 1496072Slinton } while (p->status == STOPPED && !istraced(p)); 1505507Slinton } 1515507Slinton 1525507Slinton /* 1535507Slinton * single step as best ptrace can 1545507Slinton */ 1555507Slinton 1565507Slinton pstep(p) 1575507Slinton PROCESS *p; 1585507Slinton { 1596072Slinton int status; 1605507Slinton 1616072Slinton setinfo(p); 1626072Slinton sigs_off(); 1636072Slinton ptrace(SSTEP, p->pid, p->pc, p->signo); 1646072Slinton pwait(p->pid, &status); 1656072Slinton sigs_on(); 1666072Slinton getinfo(p, status); 1675507Slinton } 1685507Slinton 1695507Slinton /* 1705507Slinton * Return from execution when the given signal is pending. 1715507Slinton */ 1725507Slinton 1735507Slinton psigtrace(p, sig, sw) 1745507Slinton PROCESS *p; 1755507Slinton int sig; 1765507Slinton int sw; 1775507Slinton { 1786072Slinton if (sw) { 1796072Slinton p->sigset |= setrep(sig); 1806072Slinton } else { 1816072Slinton p->sigset &= ~setrep(sig); 1826072Slinton } 1835507Slinton } 1845507Slinton 1855507Slinton /* 1865507Slinton * Don't catch any signals. 1875507Slinton * Particularly useful when letting a process finish uninhibited (i.e. px). 1885507Slinton */ 1895507Slinton 1905507Slinton unsetsigtraces(p) 1915507Slinton PROCESS *p; 1925507Slinton { 1936072Slinton p->sigset = 0; 1945507Slinton } 1955507Slinton 1965507Slinton /* 1975507Slinton * turn off attention to signals not being caught 1985507Slinton */ 1995507Slinton 2005507Slinton typedef int INTFUNC(); 2015507Slinton 2025507Slinton LOCAL INTFUNC *sigfunc[NSIG]; 2035507Slinton 2045507Slinton LOCAL sigs_off() 2055507Slinton { 2066072Slinton register int i; 2075507Slinton 2086072Slinton for (i = FIRSTSIG; i < LASTSIG; i++) { 2096072Slinton if (i != SIGKILL) { 2106072Slinton sigfunc[i] = signal(i, SIG_IGN); 2115507Slinton } 2126072Slinton } 2135507Slinton } 2145507Slinton 2155507Slinton /* 2165507Slinton * turn back on attention to signals 2175507Slinton */ 2185507Slinton 2195507Slinton LOCAL sigs_on() 2205507Slinton { 2216072Slinton register int i; 2225507Slinton 2236072Slinton for (i = FIRSTSIG; i < LASTSIG; i++) { 2246072Slinton if (i != SIGKILL) { 2256072Slinton signal(i, sigfunc[i]); 2265507Slinton } 2276072Slinton } 2285507Slinton } 2295507Slinton 2305507Slinton /* 2315507Slinton * get PROCESS information from process's user area 2325507Slinton */ 2335507Slinton 23410768Slinton #if vax 23510768Slinton LOCAL int rloc[] ={ 23610768Slinton R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, 23710768Slinton }; 23810768Slinton #else 23910768Slinton LOCAL int rloc[] ={ 24010768Slinton R0, R1, R2, R3, R4, R5, R6, R7, AR0, AR1, AR2, AR3, AR4, AR5, 24110768Slinton }; 24210768Slinton #endif 2435507Slinton 2445507Slinton LOCAL getinfo(p, status) 2455507Slinton register PROCESS *p; 2465507Slinton register int status; 2475507Slinton { 2486072Slinton register int i; 2495507Slinton 2506072Slinton p->signo = (status&0177); 2516072Slinton p->exitval = ((status >> 8)&0377); 2526072Slinton if (p->signo == STOPPED) { 2536072Slinton p->status = p->signo; 2546072Slinton p->signo = p->exitval; 2556072Slinton p->exitval = 0; 2566072Slinton } else { 2576072Slinton p->status = FINISHED; 2586072Slinton return; 2596072Slinton } 26010768Slinton #ifndef vax 26110768Slinton if (ar0val < 0){ 26210768Slinton ar0val = ptrace(UREAD, p->pid, U_AR0, 0); 26310768Slinton ar0val -= U_PAGE; 26410768Slinton } 26510768Slinton #endif 2666072Slinton for (i = 0; i < NREG; i++) { 2676072Slinton p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0); 2686072Slinton p->oreg[i] = p->reg[i]; 2696072Slinton } 27010768Slinton #ifdef vax 2716072Slinton p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0); 2726072Slinton p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0); 2736072Slinton p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); 2746072Slinton p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); 27510768Slinton #else 27610768Slinton p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(AR6), 0); 27710768Slinton p->ap = p->oap = p->fp; 27810768Slinton p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0); 27910768Slinton p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0); 28010768Slinton #endif 2815507Slinton } 2825507Slinton 2835507Slinton /* 2845507Slinton * set process's user area information from given PROCESS structure 2855507Slinton */ 2865507Slinton 2875507Slinton LOCAL setinfo(p) 2885507Slinton register PROCESS *p; 2895507Slinton { 2906072Slinton register int i; 2916072Slinton register int r; 2925507Slinton 2936072Slinton if (istraced(p)) { 2946072Slinton p->signo = 0; 2956072Slinton } 2966072Slinton for (i = 0; i < NREG; i++) { 2976072Slinton if ((r = p->reg[i]) != p->oreg[i]) { 2986072Slinton ptrace(UWRITE, p->pid, regloc(rloc[i]), r); 2995507Slinton } 3006072Slinton } 30110768Slinton #if vax 3026072Slinton if ((r = p->fp) != p->ofp) { 3036072Slinton ptrace(UWRITE, p->pid, regloc(FP), r); 3046072Slinton } 3056072Slinton if ((r = p->sp) != p->osp) { 3066072Slinton ptrace(UWRITE, p->pid, regloc(SP), r); 3076072Slinton } 3086072Slinton if ((r = p->ap) != p->oap) { 3096072Slinton ptrace(UWRITE, p->pid, regloc(AP), r); 3106072Slinton } 31110768Slinton #else 31210768Slinton if ((r = p->fp) != p->ofp) { 31310768Slinton ptrace(UWRITE, p->pid, regloc(AR6), r); 31410768Slinton } 31510768Slinton if ((r = p->sp) != p->osp) { 31610768Slinton ptrace(UWRITE, p->pid, regloc(SP), r); 31710768Slinton } 31810768Slinton #endif 3196072Slinton if ((r = p->pc) != p->opc) { 3206072Slinton ptrace(UWRITE, p->pid, regloc(PC), r); 3216072Slinton } 3225507Slinton } 3235507Slinton 3245507Slinton /* 3255507Slinton * Structure for reading and writing by words, but dealing with bytes. 3265507Slinton */ 3275507Slinton 3285507Slinton typedef union { 3296072Slinton WORD pword; 3306072Slinton BYTE pbyte[sizeof(WORD)]; 3315507Slinton } PWORD; 3325507Slinton 3335507Slinton /* 3345507Slinton * Read (write) from (to) the process' address space. 3355507Slinton * We must deal with ptrace's inability to look anywhere other 3365507Slinton * than at a word boundary. 3375507Slinton */ 3385507Slinton 3395507Slinton LOCAL WORD fetch(); 3405507Slinton LOCAL store(); 3415507Slinton 3425507Slinton pio(p, op, seg, buff, addr, nbytes) 3435507Slinton PROCESS *p; 3445507Slinton PIO_OP op; 3455507Slinton PIO_SEG seg; 3465507Slinton char *buff; 3475507Slinton ADDRESS addr; 3485507Slinton int nbytes; 3495507Slinton { 3506072Slinton register int i; 3516072Slinton register ADDRESS newaddr; 3526072Slinton register char *cp; 3536072Slinton char *bufend; 3546072Slinton PWORD w; 3556072Slinton ADDRESS wordaddr; 3566072Slinton int byteoff; 3575507Slinton 3586072Slinton if (p->status != STOPPED) { 3596072Slinton error("program is not active"); 3606072Slinton } 3616072Slinton cp = buff; 3626072Slinton newaddr = addr; 3636072Slinton wordaddr = (newaddr&WMASK); 3646072Slinton if (wordaddr != newaddr) { 3656072Slinton w.pword = fetch(p, seg, wordaddr); 3666072Slinton for (i = newaddr - wordaddr; i<sizeof(WORD) && nbytes>0; i++) { 3676072Slinton if (op == PREAD) { 3686072Slinton *cp++ = w.pbyte[i]; 3696072Slinton } else { 3706072Slinton w.pbyte[i] = *cp++; 3716072Slinton } 3726072Slinton nbytes--; 3735507Slinton } 3746072Slinton if (op == PWRITE) { 3756072Slinton store(p, seg, wordaddr, w.pword); 3765507Slinton } 3776072Slinton newaddr = wordaddr + sizeof(WORD); 3786072Slinton } 3796072Slinton byteoff = (nbytes&(~WMASK)); 3806072Slinton nbytes -= byteoff; 3816072Slinton bufend = cp + nbytes; 3826072Slinton while (cp < bufend) { 3836072Slinton if (op == PREAD) { 3846072Slinton *((WORD *) cp) = fetch(p, seg, newaddr); 3856072Slinton } else { 3866072Slinton store(p, seg, newaddr, *((WORD *) cp)); 3875507Slinton } 3886072Slinton cp += sizeof(WORD); 3896072Slinton newaddr += sizeof(WORD); 3906072Slinton } 3916072Slinton if (byteoff > 0) { 3926072Slinton w.pword = fetch(p, seg, newaddr); 3936072Slinton for (i = 0; i < byteoff; i++) { 3946072Slinton if (op == PREAD) { 3956072Slinton *cp++ = w.pbyte[i]; 3966072Slinton } else { 3976072Slinton w.pbyte[i] = *cp++; 3986072Slinton } 3995507Slinton } 4006072Slinton if (op == PWRITE) { 4016072Slinton store(p, seg, newaddr, w.pword); 4026072Slinton } 4036072Slinton } 4045507Slinton } 4055507Slinton 4065507Slinton /* 4075507Slinton * Get a word from a process at the given address. 4085507Slinton * The address is assumed to be on a word boundary. 4095507Slinton * 4105507Slinton * We use a simple cache scheme to avoid redundant references to 4115507Slinton * the instruction space (which is assumed to be pure). In the 4125507Slinton * case of px, the "instruction" space lies between ENDOFF and 4135507Slinton * ENDOFF + objsize. 4145507Slinton * 4155507Slinton * It is necessary to use a write-through scheme so that 4165507Slinton * breakpoints right next to each other don't interfere. 4175507Slinton */ 4185507Slinton 4195507Slinton LOCAL WORD fetch(p, seg, addr) 4205507Slinton PROCESS *p; 4215507Slinton PIO_SEG seg; 4225507Slinton register int addr; 4235507Slinton { 4246072Slinton register CACHEWORD *wp; 4256072Slinton register WORD w; 4265507Slinton 4276072Slinton switch (seg) { 4286072Slinton case TEXTSEG: 4296072Slinton # if (isvaxpx) 4306072Slinton panic("tried to fetch from px i-space"); 4316072Slinton /* NOTREACHED */ 4326072Slinton # else 4336072Slinton wp = &p->word[cachehash(addr)]; 4346072Slinton if (addr == 0 || wp->addr != addr) { 4356072Slinton w = ptrace(IREAD, p->pid, addr, 0); 4366072Slinton wp->addr = addr; 4376072Slinton wp->val = w; 4386072Slinton } else { 4396072Slinton w = wp->val; 4406072Slinton } 4416072Slinton break; 4426072Slinton # endif 4435507Slinton 4446072Slinton case DATASEG: 4456072Slinton # if (isvaxpx) 4466072Slinton if (addr >= ENDOFF && addr < ENDOFF + objsize) { 4476072Slinton wp = &p->word[cachehash(addr)]; 4486072Slinton if (addr == 0 || wp->addr != addr) { 4496072Slinton w = ptrace(DREAD, p->pid, addr, 0); 4506072Slinton wp->addr = addr; 4516072Slinton wp->val = w; 4526072Slinton } else { 4536072Slinton w = wp->val; 4546072Slinton } 4556072Slinton } else { 4566072Slinton w = ptrace(DREAD, p->pid, addr, 0); 4576072Slinton } 4586072Slinton # else 4596072Slinton w = ptrace(DREAD, p->pid, addr, 0); 4606072Slinton # endif 4616072Slinton break; 4625507Slinton 4636072Slinton default: 4646072Slinton panic("fetch: bad seg %d", seg); 4656072Slinton /* NOTREACHED */ 4666072Slinton } 4676072Slinton return(w); 4685507Slinton } 4695507Slinton 4705507Slinton /* 4715507Slinton * Put a word into the process' address space at the given address. 4725507Slinton * The address is assumed to be on a word boundary. 4735507Slinton */ 4745507Slinton 4755507Slinton LOCAL store(p, seg, addr, data) 4765507Slinton PROCESS *p; 4775507Slinton PIO_SEG seg; 4785507Slinton int addr; 4795507Slinton WORD data; 4805507Slinton { 4816072Slinton register CACHEWORD *wp; 4825507Slinton 4836072Slinton switch (seg) { 4846072Slinton case TEXTSEG: 4856072Slinton wp = &p->word[cachehash(addr)]; 4866072Slinton wp->addr = addr; 4876072Slinton wp->val = data; 4886072Slinton ptrace(IWRITE, p->pid, addr, data); 4896072Slinton break; 4905507Slinton 4916072Slinton case DATASEG: 4926072Slinton # if (isvaxpx) 4936072Slinton if (addr >= ENDOFF && addr < ENDOFF + objsize) { 4946072Slinton wp = &p->word[cachehash(addr)]; 4956072Slinton wp->addr = addr; 4966072Slinton wp->val = data; 4976072Slinton } 4986072Slinton # endif 4996072Slinton ptrace(DWRITE, p->pid, addr, data); 5006072Slinton break; 5015507Slinton 5026072Slinton default: 5036072Slinton panic("store: bad seg %d", seg); 5046072Slinton /*NOTREACHED*/ 5056072Slinton } 5065507Slinton } 5075507Slinton 5085507Slinton /* 5096072Slinton * Initialize the instruction cache for a process. 5106072Slinton * This is particularly necessary after the program has been remade. 5116072Slinton */ 5126072Slinton 5136072Slinton initcache(process) 5146072Slinton PROCESS *process; 5156072Slinton { 5166072Slinton register int i; 5176072Slinton 5186072Slinton for (i = 0; i < CSIZE; i++) { 5196072Slinton process->word[i].addr = 0; 5206072Slinton } 5216072Slinton } 5226072Slinton 5236072Slinton /* 5245507Slinton * Swap file numbers so as to redirect standard input and output. 5255507Slinton */ 5265507Slinton 5275507Slinton LOCAL fswap(oldfd, newfd) 5285507Slinton int oldfd; 5295507Slinton int newfd; 5305507Slinton { 5316072Slinton if (oldfd != newfd) { 5326072Slinton close(oldfd); 5336072Slinton dup(newfd); 5346072Slinton close(newfd); 5356072Slinton } 5365507Slinton } 537