15507Slinton /* Copyright (c) 1982 Regents of the University of California */
25507Slinton 
3*5607Slinton static char sccsid[] = "@(#)ptrace.c 1.2 01/23/82";
45507Slinton 
55507Slinton /*
65507Slinton  * routines for tracing the execution of a process
75507Slinton  *
85507Slinton  * The system call "ptrace" does all the work, these
95507Slinton  * routines just try to interface easily to it.
105507Slinton  */
115507Slinton 
125507Slinton #include "defs.h"
135507Slinton #include <signal.h>
145507Slinton #include <sys/param.h>
155507Slinton #include <sys/reg.h>
165507Slinton #include "process.h"
175507Slinton #include "object.h"
185507Slinton #include "process.rep"
195507Slinton 
205507Slinton #	if (isvaxpx)
215507Slinton #		include "pxinfo.h"
225507Slinton #	endif
235507Slinton 
245507Slinton /*
255507Slinton  * This magic macro enables us to look at the process' registers
265507Slinton  * in its user structure.  Very gross.
275507Slinton  */
285507Slinton 
295507Slinton #define regloc(reg)		(ctob(UPAGES) + ( sizeof(int) * (reg) ))
305507Slinton 
315507Slinton #define WMASK			(~(sizeof(WORD) - 1))
325507Slinton #define cachehash(addr)	((unsigned) ((addr >> 2) % CSIZE))
335507Slinton 
345507Slinton #define FIRSTSIG		SIGINT
355507Slinton #define LASTSIG			SIGQUIT
365507Slinton #define ischild(pid)	((pid) == 0)
375507Slinton #define traceme()		ptrace(0, 0, 0, 0)
385507Slinton #define setrep(n)		(1 << ((n)-1))
395507Slinton #define istraced(p)		(p->sigset&setrep(p->signo))
405507Slinton 
415507Slinton /*
425507Slinton  * ptrace options (specified in first argument)
435507Slinton  */
445507Slinton 
455507Slinton #define UREAD	3		/* read from process's user structure */
465507Slinton #define UWRITE	6		/* write to process's user structure */
475507Slinton #define IREAD	1		/* read from process's instruction space */
485507Slinton #define IWRITE	4		/* write to process's instruction space */
495507Slinton #define DREAD	2		/* read from process's data space */
505507Slinton #define DWRITE	5		/* write to process's data space */
515507Slinton #define CONT	7		/* continue stopped process */
525507Slinton #define SSTEP	9		/* continue for approximately one instruction */
535507Slinton #define PKILL	8		/* terminate the process */
545507Slinton 
555507Slinton /*
565507Slinton  * Start up a new process by forking and exec-ing the
575507Slinton  * given argument list, returning when the process is loaded
585507Slinton  * and ready to execute.  The PROCESS information (pointed to
595507Slinton  * by the first argument) is appropriately filled.
605507Slinton  *
615507Slinton  * If the given PROCESS structure is associated with an already running
625507Slinton  * process, we terminate it.
635507Slinton  */
645507Slinton 
655507Slinton /* VARARGS2 */
66*5607Slinton pstart(p, cmd, argv, infile, outfile)
675507Slinton PROCESS *p;
68*5607Slinton char *cmd;
695507Slinton char **argv;
705507Slinton char *infile;
715507Slinton char *outfile;
725507Slinton {
735507Slinton 	int status;
745507Slinton 	FILE *in, *out;
755507Slinton 
765507Slinton 	if (p->pid != 0) {					/* child already running? */
775507Slinton 		ptrace(PKILL, p->pid, 0, 0);	/* ... kill it! */
785507Slinton 	}
795507Slinton 	psigtrace(p, SIGTRAP, TRUE);
805507Slinton 	if ((p->pid = fork()) == -1) {
815507Slinton 		panic("can't fork");
825507Slinton 	}
835507Slinton 	if (ischild(p->pid)) {
845507Slinton 		traceme();
855507Slinton 		if (infile != NIL) {
865507Slinton 			if ((in = fopen(infile, "r")) == NIL) {
875507Slinton 				printf("can't read %s\n", infile);
885507Slinton 				exit(1);
895507Slinton 			}
905507Slinton 			fswap(0, fileno(in));
915507Slinton 		}
925507Slinton 		if (outfile != NIL) {
935507Slinton 			if ((out = fopen(outfile, "w")) == NIL) {
945507Slinton 				printf("can't write %s\n", outfile);
955507Slinton 				exit(1);
965507Slinton 			}
975507Slinton 			fswap(1, fileno(out));
985507Slinton 		}
99*5607Slinton 		execvp(cmd, argv);
1005507Slinton 		panic("can't exec %s", argv[0]);
1015507Slinton 	}
1025507Slinton 	pwait(p->pid, &status);
1035507Slinton 	getinfo(p, status);
1045507Slinton }
1055507Slinton 
1065507Slinton /*
1075507Slinton  * Continue a stopped process.  The argument points to a PROCESS structure.
1085507Slinton  * Before the process is restarted it's user area is modified according to
1095507Slinton  * the values in the structure.  When this routine finishes,
1105507Slinton  * the structure has the new values from the process's user area.
1115507Slinton  *
1125507Slinton  * Pcont terminates when the process stops with a signal pending that
1135507Slinton  * is being traced (via psigtrace), or when the process terminates.
1145507Slinton  */
1155507Slinton 
1165507Slinton pcont(p)
1175507Slinton PROCESS *p;
1185507Slinton {
1195507Slinton 	int status;
1205507Slinton 
1215507Slinton 	if (p->pid == 0) {
1225507Slinton 		error("program not active");
1235507Slinton 	}
1245507Slinton 	do {
1255507Slinton 		setinfo(p);
1265507Slinton 		sigs_off();
1275507Slinton 		if (ptrace(CONT, p->pid, p->pc, p->signo) < 0) {
1285507Slinton 			panic("can't continue process");
1295507Slinton 		}
1305507Slinton 		pwait(p->pid, &status);
1315507Slinton 		sigs_on();
1325507Slinton 		getinfo(p, status);
1335507Slinton 	} while (p->status == STOPPED && !istraced(p));
1345507Slinton }
1355507Slinton 
1365507Slinton /*
1375507Slinton  * single step as best ptrace can
1385507Slinton  */
1395507Slinton 
1405507Slinton pstep(p)
1415507Slinton PROCESS *p;
1425507Slinton {
1435507Slinton 	int status;
1445507Slinton 
1455507Slinton 	setinfo(p);
1465507Slinton 	sigs_off();
1475507Slinton 	ptrace(SSTEP, p->pid, p->pc, p->signo);
1485507Slinton 	pwait(p->pid, &status);
1495507Slinton 	sigs_on();
1505507Slinton 	getinfo(p, status);
1515507Slinton }
1525507Slinton 
1535507Slinton /*
1545507Slinton  * Return from execution when the given signal is pending.
1555507Slinton  */
1565507Slinton 
1575507Slinton psigtrace(p, sig, sw)
1585507Slinton PROCESS *p;
1595507Slinton int sig;
1605507Slinton int sw;
1615507Slinton {
1625507Slinton 	if (sw) {
1635507Slinton 		p->sigset |= setrep(sig);
1645507Slinton 	} else {
1655507Slinton 		p->sigset &= ~setrep(sig);
1665507Slinton 	}
1675507Slinton }
1685507Slinton 
1695507Slinton /*
1705507Slinton  * Don't catch any signals.
1715507Slinton  * Particularly useful when letting a process finish uninhibited (i.e. px).
1725507Slinton  */
1735507Slinton 
1745507Slinton unsetsigtraces(p)
1755507Slinton PROCESS *p;
1765507Slinton {
1775507Slinton 	p->sigset = 0;
1785507Slinton }
1795507Slinton 
1805507Slinton /*
1815507Slinton  * turn off attention to signals not being caught
1825507Slinton  */
1835507Slinton 
1845507Slinton typedef int INTFUNC();
1855507Slinton 
1865507Slinton LOCAL INTFUNC *sigfunc[NSIG];
1875507Slinton 
1885507Slinton LOCAL sigs_off()
1895507Slinton {
1905507Slinton 	register int i;
1915507Slinton 
1925507Slinton 	for (i = FIRSTSIG; i < LASTSIG; i++) {
1935507Slinton 		if (i != SIGKILL) {
1945507Slinton 			sigfunc[i] = signal(i, SIG_IGN);
1955507Slinton 		}
1965507Slinton 	}
1975507Slinton }
1985507Slinton 
1995507Slinton /*
2005507Slinton  * turn back on attention to signals
2015507Slinton  */
2025507Slinton 
2035507Slinton LOCAL sigs_on()
2045507Slinton {
2055507Slinton 	register int i;
2065507Slinton 
2075507Slinton 	for (i = FIRSTSIG; i < LASTSIG; i++) {
2085507Slinton 		if (i != SIGKILL) {
2095507Slinton 			signal(i, sigfunc[i]);
2105507Slinton 		}
2115507Slinton 	}
2125507Slinton }
2135507Slinton 
2145507Slinton /*
2155507Slinton  * get PROCESS information from process's user area
2165507Slinton  */
2175507Slinton 
2185507Slinton LOCAL int rloc[] ={
2195507Slinton 	R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11,
2205507Slinton };
2215507Slinton 
2225507Slinton LOCAL getinfo(p, status)
2235507Slinton register PROCESS *p;
2245507Slinton register int status;
2255507Slinton {
2265507Slinton 	register int i;
2275507Slinton 
2285507Slinton 	p->signo = (status&0177);
2295507Slinton 	p->exitval = ((status >> 8)&0377);
2305507Slinton 	if (p->signo == STOPPED) {
2315507Slinton 		p->status = p->signo;
2325507Slinton 		p->signo = p->exitval;
2335507Slinton 		p->exitval = 0;
2345507Slinton 	} else {
2355507Slinton 		p->status = FINISHED;
2365507Slinton 		return;
2375507Slinton 	}
2385507Slinton 	for (i = 0; i < NREG; i++) {
2395507Slinton 		p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0);
2405507Slinton 		p->oreg[i] = p->reg[i];
2415507Slinton 	}
2425507Slinton 	p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0);
2435507Slinton 	p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0);
2445507Slinton 	p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0);
2455507Slinton 	p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0);
2465507Slinton }
2475507Slinton 
2485507Slinton /*
2495507Slinton  * set process's user area information from given PROCESS structure
2505507Slinton  */
2515507Slinton 
2525507Slinton LOCAL setinfo(p)
2535507Slinton register PROCESS *p;
2545507Slinton {
2555507Slinton 	register int i;
2565507Slinton 	register int r;
2575507Slinton 
2585507Slinton 	if (istraced(p)) {
2595507Slinton 		p->signo = 0;
2605507Slinton 	}
2615507Slinton 	for (i = 0; i < NREG; i++) {
2625507Slinton 		if ((r = p->reg[i]) != p->oreg[i]) {
2635507Slinton 			ptrace(UWRITE, p->pid, regloc(rloc[i]), r);
2645507Slinton 		}
2655507Slinton 	}
2665507Slinton 	if ((r = p->fp) != p->ofp) {
2675507Slinton 		ptrace(UWRITE, p->pid, regloc(FP), r);
2685507Slinton 	}
2695507Slinton 	if ((r = p->sp) != p->osp) {
2705507Slinton 		ptrace(UWRITE, p->pid, regloc(SP), r);
2715507Slinton 	}
2725507Slinton 	if ((r = p->ap) != p->oap) {
2735507Slinton 		ptrace(UWRITE, p->pid, regloc(AP), r);
2745507Slinton 	}
2755507Slinton 	if ((r = p->pc) != p->opc) {
2765507Slinton 		ptrace(UWRITE, p->pid, regloc(PC), r);
2775507Slinton 	}
2785507Slinton }
2795507Slinton 
2805507Slinton /*
2815507Slinton  * Structure for reading and writing by words, but dealing with bytes.
2825507Slinton  */
2835507Slinton 
2845507Slinton typedef union {
2855507Slinton 	WORD pword;
2865507Slinton 	BYTE pbyte[sizeof(WORD)];
2875507Slinton } PWORD;
2885507Slinton 
2895507Slinton /*
2905507Slinton  * Read (write) from (to) the process' address space.
2915507Slinton  * We must deal with ptrace's inability to look anywhere other
2925507Slinton  * than at a word boundary.
2935507Slinton  */
2945507Slinton 
2955507Slinton LOCAL WORD fetch();
2965507Slinton LOCAL store();
2975507Slinton 
2985507Slinton pio(p, op, seg, buff, addr, nbytes)
2995507Slinton PROCESS *p;
3005507Slinton PIO_OP op;
3015507Slinton PIO_SEG seg;
3025507Slinton char *buff;
3035507Slinton ADDRESS addr;
3045507Slinton int nbytes;
3055507Slinton {
3065507Slinton 	register int i;
3075507Slinton 	register ADDRESS newaddr;
3085507Slinton 	register char *cp;
3095507Slinton 	char *bufend;
3105507Slinton 	PWORD w;
3115507Slinton 	ADDRESS wordaddr;
3125507Slinton 	int byteoff;
3135507Slinton 
3145507Slinton 	if (p->status != STOPPED) {
3155507Slinton 		error("program is not active");
3165507Slinton 	}
3175507Slinton 	cp = buff;
3185507Slinton 	newaddr = addr;
3195507Slinton 	wordaddr = (newaddr&WMASK);
3205507Slinton 	if (wordaddr != newaddr) {
3215507Slinton 		w.pword = fetch(p, seg, wordaddr);
3225507Slinton 		for (i = newaddr - wordaddr; i<sizeof(WORD) && nbytes>0; i++) {
3235507Slinton 			if (op == PREAD) {
3245507Slinton 				*cp++ = w.pbyte[i];
3255507Slinton 			} else {
3265507Slinton 				w.pbyte[i] = *cp++;
3275507Slinton 			}
3285507Slinton 			nbytes--;
3295507Slinton 		}
3305507Slinton 		if (op == PWRITE) {
3315507Slinton 			store(p, seg, wordaddr, w.pword);
3325507Slinton 		}
3335507Slinton 		newaddr = wordaddr + sizeof(WORD);
3345507Slinton 	}
3355507Slinton 	byteoff = (nbytes&(~WMASK));
3365507Slinton 	nbytes -= byteoff;
3375507Slinton 	bufend = cp + nbytes;
3385507Slinton 	while (cp < bufend) {
3395507Slinton 		if (op == PREAD) {
3405507Slinton 			*((WORD *) cp) = fetch(p, seg, newaddr);
3415507Slinton 		} else {
3425507Slinton 			store(p, seg, newaddr, *((WORD *) cp));
3435507Slinton 		}
3445507Slinton 		cp += sizeof(WORD);
3455507Slinton 		newaddr += sizeof(WORD);
3465507Slinton 	}
3475507Slinton 	if (byteoff > 0) {
3485507Slinton 		w.pword = fetch(p, seg, newaddr);
3495507Slinton 		for (i = 0; i < byteoff; i++) {
3505507Slinton 			if (op == PREAD) {
3515507Slinton 				*cp++ = w.pbyte[i];
3525507Slinton 			} else {
3535507Slinton 				w.pbyte[i] = *cp++;
3545507Slinton 			}
3555507Slinton 		}
3565507Slinton 		if (op == PWRITE) {
3575507Slinton 			store(p, seg, newaddr, w.pword);
3585507Slinton 		}
3595507Slinton 	}
3605507Slinton }
3615507Slinton 
3625507Slinton /*
3635507Slinton  * Get a word from a process at the given address.
3645507Slinton  * The address is assumed to be on a word boundary.
3655507Slinton  *
3665507Slinton  * We use a simple cache scheme to avoid redundant references to
3675507Slinton  * the instruction space (which is assumed to be pure).  In the
3685507Slinton  * case of px, the "instruction" space lies between ENDOFF and
3695507Slinton  * ENDOFF + objsize.
3705507Slinton  *
3715507Slinton  * It is necessary to use a write-through scheme so that
3725507Slinton  * breakpoints right next to each other don't interfere.
3735507Slinton  */
3745507Slinton 
3755507Slinton LOCAL WORD fetch(p, seg, addr)
3765507Slinton PROCESS *p;
3775507Slinton PIO_SEG seg;
3785507Slinton register int addr;
3795507Slinton {
3805507Slinton 	register CACHEWORD *wp;
3815507Slinton 	register WORD w;
3825507Slinton 
3835507Slinton 	switch (seg) {
3845507Slinton 		case TEXTSEG:
3855507Slinton #			if (isvaxpx)
3865507Slinton 				panic("tried to fetch from px i-space");
3875507Slinton 				/* NOTREACHED */
3885507Slinton #			else
3895507Slinton 				wp = &p->word[cachehash(addr)];
3905507Slinton 				if (addr == 0 || wp->addr != addr) {
3915507Slinton 					w = ptrace(IREAD, p->pid, addr, 0);
3925507Slinton 					wp->addr = addr;
3935507Slinton 					wp->val = w;
3945507Slinton 				} else {
3955507Slinton 					w = wp->val;
3965507Slinton 				}
3975507Slinton 				break;
3985507Slinton #			endif
3995507Slinton 
4005507Slinton 		case DATASEG:
4015507Slinton #			if (isvaxpx)
4025507Slinton 				if (addr >= ENDOFF && addr < ENDOFF + objsize) {
4035507Slinton 					wp = &p->word[cachehash(addr)];
4045507Slinton 					if (addr == 0 || wp->addr != addr) {
4055507Slinton 						w = ptrace(DREAD, p->pid, addr, 0);
4065507Slinton 						wp->addr = addr;
4075507Slinton 						wp->val = w;
4085507Slinton 					} else {
4095507Slinton 						w = wp->val;
4105507Slinton 					}
4115507Slinton 				} else {
4125507Slinton 					w = ptrace(DREAD, p->pid, addr, 0);
4135507Slinton 				}
4145507Slinton #			else
4155507Slinton 				w = ptrace(DREAD, p->pid, addr, 0);
4165507Slinton #			endif
4175507Slinton 			break;
4185507Slinton 
4195507Slinton 		default:
4205507Slinton 			panic("fetch: bad seg %d", seg);
4215507Slinton 			/* NOTREACHED */
4225507Slinton 	}
4235507Slinton 	return(w);
4245507Slinton }
4255507Slinton 
4265507Slinton /*
4275507Slinton  * Put a word into the process' address space at the given address.
4285507Slinton  * The address is assumed to be on a word boundary.
4295507Slinton  */
4305507Slinton 
4315507Slinton LOCAL store(p, seg, addr, data)
4325507Slinton PROCESS *p;
4335507Slinton PIO_SEG seg;
4345507Slinton int addr;
4355507Slinton WORD data;
4365507Slinton {
4375507Slinton 	register CACHEWORD *wp;
4385507Slinton 
4395507Slinton 	switch (seg) {
4405507Slinton 		case TEXTSEG:
4415507Slinton 			wp = &p->word[cachehash(addr)];
4425507Slinton 			wp->addr = addr;
4435507Slinton 			wp->val = data;
4445507Slinton 			ptrace(IWRITE, p->pid, addr, data);
4455507Slinton 			break;
4465507Slinton 
4475507Slinton 		case DATASEG:
4485507Slinton #			if (isvaxpx)
4495507Slinton 				if (addr >= ENDOFF && addr < ENDOFF + objsize) {
4505507Slinton 					wp = &p->word[cachehash(addr)];
4515507Slinton 					wp->addr = addr;
4525507Slinton 					wp->val = data;
4535507Slinton 				}
4545507Slinton #			endif
4555507Slinton 			ptrace(DWRITE, p->pid, addr, data);
4565507Slinton 			break;
4575507Slinton 
4585507Slinton 		default:
4595507Slinton 			panic("store: bad seg %d", seg);
4605507Slinton 			/*NOTREACHED*/
4615507Slinton 	}
4625507Slinton }
4635507Slinton 
4645507Slinton /*
4655507Slinton  * Swap file numbers so as to redirect standard input and output.
4665507Slinton  */
4675507Slinton 
4685507Slinton LOCAL fswap(oldfd, newfd)
4695507Slinton int oldfd;
4705507Slinton int newfd;
4715507Slinton {
4725507Slinton 	if (oldfd != newfd) {
4735507Slinton 		close(oldfd);
4745507Slinton 		dup(newfd);
4755507Slinton 		close(newfd);
4765507Slinton 	}
4775507Slinton }
478