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