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