15507Slinton /* Copyright (c) 1982 Regents of the University of California */
25507Slinton 
3*10004Ssam static char sccsid[] = "@(#)ptrace.c 1.4 12/29/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>
15*10004Ssam #include <machine/reg.h>
165507Slinton #include "process.h"
175507Slinton #include "object.h"
185507Slinton #include "process.rep"
195507Slinton 
206072Slinton #   if (isvaxpx)
216072Slinton #       include "pxinfo.h"
226072Slinton #   endif
235507Slinton 
245507Slinton /*
255507Slinton  * This magic macro enables us to look at the process' registers
265507Slinton  * in its user structure.  Very gross.
275507Slinton  */
285507Slinton 
296072Slinton #define regloc(reg)     (ctob(UPAGES) + ( sizeof(int) * (reg) ))
305507Slinton 
316072Slinton #define WMASK           (~(sizeof(WORD) - 1))
326072Slinton #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE))
335507Slinton 
346072Slinton #define FIRSTSIG        SIGINT
356072Slinton #define LASTSIG         SIGQUIT
366072Slinton #define ischild(pid)    ((pid) == 0)
376072Slinton #define traceme()       ptrace(0, 0, 0, 0)
386072Slinton #define setrep(n)       (1 << ((n)-1))
396072Slinton #define istraced(p)     (p->sigset&setrep(p->signo))
405507Slinton 
415507Slinton /*
425507Slinton  * ptrace options (specified in first argument)
435507Slinton  */
445507Slinton 
456072Slinton #define UREAD   3       /* read from process's user structure */
466072Slinton #define UWRITE  6       /* write to process's user structure */
476072Slinton #define IREAD   1       /* read from process's instruction space */
486072Slinton #define IWRITE  4       /* write to process's instruction space */
496072Slinton #define DREAD   2       /* read from process's data space */
506072Slinton #define DWRITE  5       /* write to process's data space */
516072Slinton #define CONT    7       /* continue stopped process */
526072Slinton #define SSTEP   9       /* continue for approximately one instruction */
536072Slinton #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 */
665607Slinton pstart(p, cmd, argv, infile, outfile)
675507Slinton PROCESS *p;
685607Slinton char *cmd;
695507Slinton char **argv;
705507Slinton char *infile;
715507Slinton char *outfile;
725507Slinton {
736072Slinton     int status;
746072Slinton     FILE *in, *out;
755507Slinton 
766072Slinton     if (p->pid != 0) {                  /* child already running? */
776072Slinton 	ptrace(PKILL, p->pid, 0, 0);    /* ... kill it! */
786072Slinton     }
796072Slinton     psigtrace(p, SIGTRAP, TRUE);
806072Slinton     if ((p->pid = fork()) == -1) {
816072Slinton 	panic("can't fork");
826072Slinton     }
836072Slinton     if (ischild(p->pid)) {
846072Slinton 	traceme();
856072Slinton 	if (infile != NIL) {
866072Slinton 	    if ((in = fopen(infile, "r")) == NIL) {
876072Slinton 		printf("can't read %s\n", infile);
886072Slinton 		exit(1);
896072Slinton 	    }
906072Slinton 	    fswap(0, fileno(in));
915507Slinton 	}
926072Slinton 	if (outfile != NIL) {
936072Slinton 	    if ((out = fopen(outfile, "w")) == NIL) {
946072Slinton 		printf("can't write %s\n", outfile);
956072Slinton 		exit(1);
966072Slinton 	    }
976072Slinton 	    fswap(1, fileno(out));
985507Slinton 	}
996072Slinton 	execvp(cmd, argv);
1006072Slinton 	panic("can't exec %s", argv[0]);
1016072Slinton     }
1026072Slinton     pwait(p->pid, &status);
1036072Slinton     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 {
1196072Slinton     int status;
1205507Slinton 
1216072Slinton     if (p->pid == 0) {
1226072Slinton 	error("program not active");
1236072Slinton     }
1246072Slinton     do {
1256072Slinton 	setinfo(p);
1266072Slinton 	sigs_off();
1276072Slinton 	if (ptrace(CONT, p->pid, p->pc, p->signo) < 0) {
1286072Slinton 	    panic("can't continue process");
1295507Slinton 	}
1306072Slinton 	pwait(p->pid, &status);
1316072Slinton 	sigs_on();
1326072Slinton 	getinfo(p, status);
1336072Slinton     } 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 {
1436072Slinton     int status;
1445507Slinton 
1456072Slinton     setinfo(p);
1466072Slinton     sigs_off();
1476072Slinton     ptrace(SSTEP, p->pid, p->pc, p->signo);
1486072Slinton     pwait(p->pid, &status);
1496072Slinton     sigs_on();
1506072Slinton     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 {
1626072Slinton     if (sw) {
1636072Slinton 	p->sigset |= setrep(sig);
1646072Slinton     } else {
1656072Slinton 	p->sigset &= ~setrep(sig);
1666072Slinton     }
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 {
1776072Slinton     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 {
1906072Slinton     register int i;
1915507Slinton 
1926072Slinton     for (i = FIRSTSIG; i < LASTSIG; i++) {
1936072Slinton 	if (i != SIGKILL) {
1946072Slinton 	    sigfunc[i] = signal(i, SIG_IGN);
1955507Slinton 	}
1966072Slinton     }
1975507Slinton }
1985507Slinton 
1995507Slinton /*
2005507Slinton  * turn back on attention to signals
2015507Slinton  */
2025507Slinton 
2035507Slinton LOCAL sigs_on()
2045507Slinton {
2056072Slinton     register int i;
2065507Slinton 
2076072Slinton     for (i = FIRSTSIG; i < LASTSIG; i++) {
2086072Slinton 	if (i != SIGKILL) {
2096072Slinton 	    signal(i, sigfunc[i]);
2105507Slinton 	}
2116072Slinton     }
2125507Slinton }
2135507Slinton 
2145507Slinton /*
2155507Slinton  * get PROCESS information from process's user area
2165507Slinton  */
2175507Slinton 
2185507Slinton LOCAL int rloc[] ={
2196072Slinton     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 {
2266072Slinton     register int i;
2275507Slinton 
2286072Slinton     p->signo = (status&0177);
2296072Slinton     p->exitval = ((status >> 8)&0377);
2306072Slinton     if (p->signo == STOPPED) {
2316072Slinton 	p->status = p->signo;
2326072Slinton 	p->signo = p->exitval;
2336072Slinton 	p->exitval = 0;
2346072Slinton     } else {
2356072Slinton 	p->status = FINISHED;
2366072Slinton 	return;
2376072Slinton     }
2386072Slinton     for (i = 0; i < NREG; i++) {
2396072Slinton 	p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0);
2406072Slinton 	p->oreg[i] = p->reg[i];
2416072Slinton     }
2426072Slinton     p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0);
2436072Slinton     p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0);
2446072Slinton     p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0);
2456072Slinton     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 {
2556072Slinton     register int i;
2566072Slinton     register int r;
2575507Slinton 
2586072Slinton     if (istraced(p)) {
2596072Slinton 	p->signo = 0;
2606072Slinton     }
2616072Slinton     for (i = 0; i < NREG; i++) {
2626072Slinton 	if ((r = p->reg[i]) != p->oreg[i]) {
2636072Slinton 	    ptrace(UWRITE, p->pid, regloc(rloc[i]), r);
2645507Slinton 	}
2656072Slinton     }
2666072Slinton     if ((r = p->fp) != p->ofp) {
2676072Slinton 	ptrace(UWRITE, p->pid, regloc(FP), r);
2686072Slinton     }
2696072Slinton     if ((r = p->sp) != p->osp) {
2706072Slinton 	ptrace(UWRITE, p->pid, regloc(SP), r);
2716072Slinton     }
2726072Slinton     if ((r = p->ap) != p->oap) {
2736072Slinton 	ptrace(UWRITE, p->pid, regloc(AP), r);
2746072Slinton     }
2756072Slinton     if ((r = p->pc) != p->opc) {
2766072Slinton 	ptrace(UWRITE, p->pid, regloc(PC), r);
2776072Slinton     }
2785507Slinton }
2795507Slinton 
2805507Slinton /*
2815507Slinton  * Structure for reading and writing by words, but dealing with bytes.
2825507Slinton  */
2835507Slinton 
2845507Slinton typedef union {
2856072Slinton     WORD pword;
2866072Slinton     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 {
3066072Slinton     register int i;
3076072Slinton     register ADDRESS newaddr;
3086072Slinton     register char *cp;
3096072Slinton     char *bufend;
3106072Slinton     PWORD w;
3116072Slinton     ADDRESS wordaddr;
3126072Slinton     int byteoff;
3135507Slinton 
3146072Slinton     if (p->status != STOPPED) {
3156072Slinton 	error("program is not active");
3166072Slinton     }
3176072Slinton     cp = buff;
3186072Slinton     newaddr = addr;
3196072Slinton     wordaddr = (newaddr&WMASK);
3206072Slinton     if (wordaddr != newaddr) {
3216072Slinton 	w.pword = fetch(p, seg, wordaddr);
3226072Slinton 	for (i = newaddr - wordaddr; i<sizeof(WORD) && nbytes>0; i++) {
3236072Slinton 	    if (op == PREAD) {
3246072Slinton 		*cp++ = w.pbyte[i];
3256072Slinton 	    } else {
3266072Slinton 		w.pbyte[i] = *cp++;
3276072Slinton 	    }
3286072Slinton 	    nbytes--;
3295507Slinton 	}
3306072Slinton 	if (op == PWRITE) {
3316072Slinton 	    store(p, seg, wordaddr, w.pword);
3325507Slinton 	}
3336072Slinton 	newaddr = wordaddr + sizeof(WORD);
3346072Slinton     }
3356072Slinton     byteoff = (nbytes&(~WMASK));
3366072Slinton     nbytes -= byteoff;
3376072Slinton     bufend = cp + nbytes;
3386072Slinton     while (cp < bufend) {
3396072Slinton 	if (op == PREAD) {
3406072Slinton 	    *((WORD *) cp) = fetch(p, seg, newaddr);
3416072Slinton 	} else {
3426072Slinton 	    store(p, seg, newaddr, *((WORD *) cp));
3435507Slinton 	}
3446072Slinton 	cp += sizeof(WORD);
3456072Slinton 	newaddr += sizeof(WORD);
3466072Slinton     }
3476072Slinton     if (byteoff > 0) {
3486072Slinton 	w.pword = fetch(p, seg, newaddr);
3496072Slinton 	for (i = 0; i < byteoff; i++) {
3506072Slinton 	    if (op == PREAD) {
3516072Slinton 		*cp++ = w.pbyte[i];
3526072Slinton 	    } else {
3536072Slinton 		w.pbyte[i] = *cp++;
3546072Slinton 	    }
3555507Slinton 	}
3566072Slinton 	if (op == PWRITE) {
3576072Slinton 	    store(p, seg, newaddr, w.pword);
3586072Slinton 	}
3596072Slinton     }
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 {
3806072Slinton     register CACHEWORD *wp;
3816072Slinton     register WORD w;
3825507Slinton 
3836072Slinton     switch (seg) {
3846072Slinton 	case TEXTSEG:
3856072Slinton #           if (isvaxpx)
3866072Slinton 		panic("tried to fetch from px i-space");
3876072Slinton 		/* NOTREACHED */
3886072Slinton #           else
3896072Slinton 		wp = &p->word[cachehash(addr)];
3906072Slinton 		if (addr == 0 || wp->addr != addr) {
3916072Slinton 		    w = ptrace(IREAD, p->pid, addr, 0);
3926072Slinton 		    wp->addr = addr;
3936072Slinton 		    wp->val = w;
3946072Slinton 		} else {
3956072Slinton 		    w = wp->val;
3966072Slinton 		}
3976072Slinton 		break;
3986072Slinton #           endif
3995507Slinton 
4006072Slinton 	case DATASEG:
4016072Slinton #           if (isvaxpx)
4026072Slinton 		if (addr >= ENDOFF && addr < ENDOFF + objsize) {
4036072Slinton 		    wp = &p->word[cachehash(addr)];
4046072Slinton 		    if (addr == 0 || wp->addr != addr) {
4056072Slinton 			w = ptrace(DREAD, p->pid, addr, 0);
4066072Slinton 			wp->addr = addr;
4076072Slinton 			wp->val = w;
4086072Slinton 		    } else {
4096072Slinton 			w = wp->val;
4106072Slinton 		    }
4116072Slinton 		} else {
4126072Slinton 		    w = ptrace(DREAD, p->pid, addr, 0);
4136072Slinton 		}
4146072Slinton #           else
4156072Slinton 		w = ptrace(DREAD, p->pid, addr, 0);
4166072Slinton #           endif
4176072Slinton 	    break;
4185507Slinton 
4196072Slinton 	default:
4206072Slinton 	    panic("fetch: bad seg %d", seg);
4216072Slinton 	    /* NOTREACHED */
4226072Slinton     }
4236072Slinton     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 {
4376072Slinton     register CACHEWORD *wp;
4385507Slinton 
4396072Slinton     switch (seg) {
4406072Slinton 	case TEXTSEG:
4416072Slinton 	    wp = &p->word[cachehash(addr)];
4426072Slinton 	    wp->addr = addr;
4436072Slinton 	    wp->val = data;
4446072Slinton 	    ptrace(IWRITE, p->pid, addr, data);
4456072Slinton 	    break;
4465507Slinton 
4476072Slinton 	case DATASEG:
4486072Slinton #           if (isvaxpx)
4496072Slinton 		if (addr >= ENDOFF && addr < ENDOFF + objsize) {
4506072Slinton 		    wp = &p->word[cachehash(addr)];
4516072Slinton 		    wp->addr = addr;
4526072Slinton 		    wp->val = data;
4536072Slinton 		}
4546072Slinton #           endif
4556072Slinton 	    ptrace(DWRITE, p->pid, addr, data);
4566072Slinton 	    break;
4575507Slinton 
4586072Slinton 	default:
4596072Slinton 	    panic("store: bad seg %d", seg);
4606072Slinton 	    /*NOTREACHED*/
4616072Slinton     }
4625507Slinton }
4635507Slinton 
4645507Slinton /*
4656072Slinton  * Initialize the instruction cache for a process.
4666072Slinton  * This is particularly necessary after the program has been remade.
4676072Slinton  */
4686072Slinton 
4696072Slinton initcache(process)
4706072Slinton PROCESS *process;
4716072Slinton {
4726072Slinton     register int i;
4736072Slinton 
4746072Slinton     for (i = 0; i < CSIZE; i++) {
4756072Slinton 	process->word[i].addr = 0;
4766072Slinton     }
4776072Slinton }
4786072Slinton 
4796072Slinton /*
4805507Slinton  * Swap file numbers so as to redirect standard input and output.
4815507Slinton  */
4825507Slinton 
4835507Slinton LOCAL fswap(oldfd, newfd)
4845507Slinton int oldfd;
4855507Slinton int newfd;
4865507Slinton {
4876072Slinton     if (oldfd != newfd) {
4886072Slinton 	close(oldfd);
4896072Slinton 	dup(newfd);
4906072Slinton 	close(newfd);
4916072Slinton     }
4925507Slinton }
493