xref: /original-bsd/old/dbx/runtime.vax.c (revision 39c8fdd5)
1 
2 /* Copyright (c) 1982 Regents of the University of California */
3 
4 static char sccsid[] = "@(#)runtime.vax.c 1.10 12/30/83";
5 
6 /*
7  * Runtime organization dependent routines, mostly dealing with
8  * activation records.
9  */
10 
11 #include "defs.h"
12 #include "runtime.h"
13 #include "process.h"
14 #include "machine.h"
15 #include "events.h"
16 #include "mappings.h"
17 #include "symbols.h"
18 #include "tree.h"
19 #include "eval.h"
20 #include "operators.h"
21 #include "object.h"
22 #include <sys/param.h>
23 
24 #ifndef public
25 typedef struct Frame *Frame;
26 
27 #include "machine.h"
28 #endif
29 
30 #define NSAVEREG 12
31 
32 struct Frame {
33     Integer condition_handler;
34     Integer mask;
35     Address save_ap;		/* argument pointer */
36     Address save_fp;		/* frame pointer */
37     Address save_pc;		/* program counter */
38     Word save_reg[NSAVEREG];	/* not necessarily there */
39 };
40 
41 private Boolean walkingstack = false;
42 
43 /*
44  * Set a frame to the current activation record.
45  */
46 
47 private getcurframe(frp)
48 register Frame frp;
49 {
50     register int i;
51 
52     checkref(frp);
53     frp->mask = reg(NREG);
54     frp->save_ap = reg(ARGP);
55     frp->save_fp = reg(FRP);
56     frp->save_pc = reg(PROGCTR) + 1;
57     for (i = 0; i < NSAVEREG; i++) {
58 	frp->save_reg[i] = reg(i);
59     }
60 }
61 
62 /*
63  * Return a pointer to the next activation record up the stack.
64  * Return nil if there is none.
65  * Writes over space pointed to by given argument.
66  */
67 
68 #define bis(b, n) ((b & (1 << (n))) != 0)
69 
70 private Frame nextframe(frp)
71 Frame frp;
72 {
73     register Frame newfrp;
74     struct Frame frame;
75     register Integer i, j, mask;
76     Address prev_frame, callpc;
77     static Integer ntramp = 0;
78 
79     newfrp = frp;
80     prev_frame = frp->save_fp;
81 
82 /*
83  *  The check for interrupt generated frames is taken from adb with only
84  *  partial understanding.  If you're in "sub" and on a sigxxx "sigsub"
85  *  gets control, then the stack does NOT look like <main, sub, sigsub>.
86  *
87  *  As best I can make out it looks like:
88  *
89  *     <main, (machine check exception block + sub), sysframe, sigsub>.
90  *
91  *  When the signal occurs an exception block and a frame for the routine
92  *  in which it occured are pushed on the user stack.  Then another frame
93  *  is pushed corresponding to a call from the kernel to sigsub.
94  *
95  *  The addr in sub at which the exception occured is not in sub.save_pc
96  *  but in the machine check exception block.  It is at the magic address
97  *  fp + 84.
98  *
99  *  The current approach ignores the sys_frame (what adb reports as sigtramp)
100  *  and takes the pc for sub from the exception block.  This allows the
101  *  "where" command to report <main, sub, sigsub>, which seems reasonable.
102  */
103 
104 nextf:
105     dread(&frame, prev_frame, sizeof(struct Frame));
106     if (ntramp == 1) {
107 	dread(&callpc, prev_frame + 84, sizeof(callpc));
108     } else {
109 	callpc = frame.save_pc;
110     }
111     if (frame.save_fp == nil) {
112 	newfrp = nil;
113     } else if (callpc > 0x80000000 - 0x200 * UPAGES ) {
114 	 ntramp++;
115 	 prev_frame = frame.save_fp;
116 	 goto nextf;
117     } else {
118 	frame.save_pc = callpc;
119         ntramp = 0;
120 	mask = ((frame.mask >> 16) & 0x0fff);
121 	j = 0;
122 	for (i = 0; i < NSAVEREG; i++) {
123 	    if (bis(mask, i)) {
124 		newfrp->save_reg[i] = frame.save_reg[j];
125 		++j;
126 	    }
127 	}
128 	newfrp->condition_handler = frame.condition_handler;
129 	newfrp->mask = mask;
130 	newfrp->save_ap = frame.save_ap;
131 	newfrp->save_fp = frame.save_fp;
132 	newfrp->save_pc = frame.save_pc;
133     }
134     return newfrp;
135 }
136 
137 /*
138  * Return the frame associated with the given function.
139  * If the function is nil, return the most recently activated frame.
140  *
141  * Static allocation for the frame.
142  */
143 
144 public Frame findframe(f)
145 Symbol f;
146 {
147     register Frame frp;
148     static struct Frame frame;
149     Symbol p;
150 
151     frp = &frame;
152     getcurframe(frp);
153     if (f == nil)
154 	return (frp);
155     /*
156      * Starting at the current stack frame,
157      * walk backwards looking for a symbol
158      * match.  Beware of local blocks which
159      * have a back pointer but no stack frame.
160      */
161     p = whatblock(frp->save_pc);
162     while (p != f) {
163 	if (p == program) {
164 	    frp = nil;
165 	    break;
166 	}
167 	if (isinline(p)) {
168 	    p = container(p);
169 	    continue;
170 	}
171 	frp = nextframe(frp);
172 	if (frp == nil)
173 	   break;
174 	p = whatblock(frp->save_pc);
175     }
176     return (frp);
177 }
178 
179 /*
180  * Find the return address of the current procedure/function.
181  */
182 
183 public Address return_addr()
184 {
185     Frame frp;
186     Address addr;
187     struct Frame frame;
188 
189     frp = &frame;
190     getcurframe(frp);
191     frp = nextframe(frp);
192     if (frp == nil) {
193 	addr = 0;
194     } else {
195 	addr = frp->save_pc;
196     }
197     return addr;
198 }
199 
200 /*
201  * Push the value associated with the current function.
202  */
203 
204 public pushretval(len, isindirect)
205 Integer len;
206 Boolean isindirect;
207 {
208     Word r0;
209 
210     r0 = reg(0);
211     if (isindirect) {
212 	rpush((Address) r0, len);
213     } else {
214 	switch (len) {
215 	    case sizeof(char):
216 		push(char, r0);
217 		break;
218 
219 	    case sizeof(short):
220 		push(short, r0);
221 		break;
222 
223 	    default:
224 		if (len == sizeof(Word)) {
225 		    push(Word, r0);
226 		} else if (len == 2*sizeof(Word)) {
227 		    push(Word, r0);
228 		    push(Word, reg(1));
229 		} else {
230 		    panic("not indirect in pushretval?");
231 		}
232 		break;
233 	}
234     }
235 }
236 
237 /*
238  * Return the base address for locals in the given frame.
239  */
240 
241 public Address locals_base(frp)
242 register Frame frp;
243 {
244     return (frp == nil) ? reg(FRP) : frp->save_fp;
245 }
246 
247 /*
248  * Return the base address for arguments in the given frame.
249  */
250 
251 public Address args_base(frp)
252 register Frame frp;
253 {
254     return (frp == nil) ? reg(ARGP) : frp->save_ap;
255 }
256 
257 /*
258  * Return saved register n from the given frame.
259  */
260 
261 public Word savereg(n, frp)
262 register Integer n;
263 register Frame frp;
264 {
265     register Word w;
266 
267     if (frp == nil) {
268 	w = reg(n);
269     } else {
270 	switch (n) {
271 	    case ARGP:
272 		w = frp->save_ap;
273 		break;
274 
275 	    case FRP:
276 		w = frp->save_fp;
277 		break;
278 
279 	    case STKP:
280 		w = reg(STKP);
281 		break;
282 
283 	    case PROGCTR:
284 		w = frp->save_pc;
285 		break;
286 
287 	    default:
288 		assert(n >= 0 and n < NSAVEREG);
289 		w = frp->save_reg[n];
290 		break;
291 	}
292     }
293     return w;
294 }
295 
296 /*
297  * Return the nth argument to the current procedure.
298  */
299 
300 public Word argn(n, frp)
301 Integer n;
302 Frame frp;
303 {
304     Word w;
305 
306     dread(&w, args_base(frp) + (n * sizeof(Word)), sizeof(w));
307     return w;
308 }
309 
310 /*
311  * Calculate the entry address for a procedure or function parameter,
312  * given the address of the descriptor.
313  */
314 
315 public Address fparamaddr(a)
316 Address a;
317 {
318     Address r;
319 
320     dread(&r, a, sizeof(r));
321     return r;
322 }
323 
324 /*
325  * Print a list of currently active blocks starting with most recent.
326  */
327 
328 public wherecmd()
329 {
330     walkstack(false);
331 }
332 
333 /*
334  * Dump the world to the given file.
335  * Like "where", but variables are dumped also.
336  */
337 
338 public dump()
339 {
340     walkstack(true);
341 }
342 
343 /*
344  * Walk the stack of active procedures printing information
345  * about each active procedure.
346  */
347 
348 private walkstack(dumpvariables)
349 Boolean dumpvariables;
350 {
351     register Frame frp;
352     register Symbol f;
353     register Boolean save;
354     register Lineno line;
355     struct Frame frame;
356 
357     if (notstarted(process)) {
358 	error("program is not active");
359     } else {
360 	save = walkingstack;
361 	walkingstack = true;
362 	frp = &frame;
363 	getcurframe(frp);
364 	f = whatblock(frp->save_pc);
365 	do {
366 	    printf("%s", symname(f));
367 	    if (not isinline(f)) {
368 		printparams(f, frp);
369 	    }
370 	    line = srcline(frp->save_pc - 1);
371 	    if (line != 0) {
372 		printf(", line %d", line);
373 		printf(" in \"%s\"\n", srcfilename(frp->save_pc - 1));
374 	    } else {
375 		printf(" at 0x%x\n", frp->save_pc);
376 	    }
377 	    if (dumpvariables) {
378 		dumpvars(f, frp);
379 		putchar('\n');
380 	    }
381 	    if (isinline(f)) {
382 		f = container(f);
383 	    } else {
384 		frp = nextframe(frp);
385 		if (frp != nil) {
386 		    f = whatblock(frp->save_pc);
387 		}
388 	    }
389 	} while (frp != nil and f != program);
390 	if (dumpvariables) {
391 	    printf("in \"%s\":\n", symname(program));
392 	    dumpvars(program, nil);
393 	    putchar('\n');
394 	}
395 	walkingstack = save;
396     }
397 }
398 
399 /*
400  * Find the entry point of a procedure or function.
401  */
402 
403 public findbeginning(f)
404 Symbol f;
405 {
406     f->symvalue.funcv.beginaddr += 2;
407 }
408 
409 /*
410  * Return the address corresponding to the first line in a function.
411  */
412 
413 public Address firstline(f)
414 Symbol f;
415 {
416     Address addr;
417 
418     addr = codeloc(f);
419     while (linelookup(addr) == 0 and addr < objsize) {
420 	++addr;
421     }
422     if (addr == objsize) {
423 	addr = -1;
424     }
425     return addr;
426 }
427 
428 /*
429  * Catcher drops strike three ...
430  */
431 
432 public runtofirst()
433 {
434     Address addr;
435 
436     addr = pc;
437     while (linelookup(addr) == 0 and addr < objsize) {
438 	++addr;
439     }
440     if (addr < objsize) {
441 	stepto(addr);
442     }
443 }
444 
445 /*
446  * Return the address corresponding to the end of the program.
447  *
448  * We look for the entry to "exit".
449  */
450 
451 public Address lastaddr()
452 {
453     register Symbol s;
454 
455     s = lookup(identname("exit", true));
456     if (s == nil) {
457 	panic("can't find exit");
458     }
459     return codeloc(s);
460 }
461 
462 /*
463  * Decide if the given function is currently active.
464  *
465  * We avoid calls to "findframe" during a stack trace for efficiency.
466  * Presumably information evaluated while walking the stack is active.
467  */
468 
469 public Boolean isactive(f)
470 Symbol f;
471 {
472     register Boolean b;
473 
474     if (isfinished(process)) {
475 	b = false;
476     } else {
477 	if (walkingstack or f == program or
478 	  (ismodule(f) and isactive(container(f)))) {
479 	    b = true;
480 	} else {
481 	    b = (Boolean) (findframe(f) != nil);
482 	}
483     }
484     return b;
485 }
486 
487 /*
488  * Evaluate a call to a procedure.
489  */
490 
491 public callproc(procnode, arglist)
492 Node procnode;
493 Node arglist;
494 {
495     Symbol proc;
496     Integer argc;
497 
498     if (procnode->op != O_SYM) {
499 	beginerrmsg();
500 	fprintf(stderr, "can't call \"");
501 	prtree(stderr, procnode);
502 	fprintf(stderr, "\"");
503 	enderrmsg();
504     }
505     assert(procnode->op == O_SYM);
506     proc = procnode->value.sym;
507     if (not isblock(proc)) {
508 	error("\"%s\" is not a procedure or function", symname(proc));
509     }
510     pushenv();
511     pc = codeloc(proc);
512     argc = pushargs(proc, arglist);
513     beginproc(proc, argc);
514     isstopped = true;
515     event_once(build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)),
516 	buildcmdlist(build(O_PROCRTN, proc)));
517     cont();
518     /* NOTREACHED */
519 }
520 
521 /*
522  * Push the arguments on the process' stack.  We do this by first
523  * evaluating them on the "eval" stack, then copying into the process'
524  * space.
525  */
526 
527 private Integer pushargs(proc, arglist)
528 Symbol proc;
529 Node arglist;
530 {
531     Stack *savesp;
532     int argc, args_size;
533 
534     savesp = sp;
535     argc = evalargs(proc, arglist);
536     args_size = sp - savesp;
537     setreg(STKP, reg(STKP) - args_size);
538     dwrite(savesp, reg(STKP), args_size);
539     sp = savesp;
540     return argc;
541 }
542 
543 /*
544  * Evaluate arguments left-to-right.
545  */
546 
547 private Integer evalargs(proc, arglist)
548 Symbol proc;
549 Node arglist;
550 {
551     Node p, exp;
552     Symbol arg;
553     Stack *savesp;
554     Address addr;
555     Integer count;
556 
557     savesp = sp;
558     count = 0;
559     arg = proc->chain;
560     for (p = arglist; p != nil; p = p->value.arg[1]) {
561 	if (p->op != O_COMMA) {
562 	    panic("evalargs: arglist missing comma");
563 	}
564 	if (arg == nil) {
565 	    sp = savesp;
566 	    error("too many parameters to %s", symname(proc));
567 	}
568 	exp = p->value.arg[0];
569 	if (not compatible(arg->type, exp->nodetype)) {
570 	    sp = savesp;
571 	    error("expression for parameter %s is of wrong type", symname(arg));
572 	}
573 	if (arg->class == REF) {
574 	    if (exp->op != O_RVAL) {
575 		sp = savesp;
576 		error("variable expected for parameter \"%s\"", symname(arg));
577 	    }
578 	    addr = lval(exp->value.arg[0]);
579 	    push(Address, addr);
580 	} else {
581 	    eval(exp);
582 	}
583 	arg = arg->chain;
584 	++count;
585     }
586     if (arg != nil) {
587 	sp = savesp;
588 	error("not enough parameters to %s", symname(proc));
589     }
590     return count;
591 }
592 
593 public procreturn(f)
594 Symbol f;
595 {
596     flushoutput();
597     putchar('\n');
598     printname(stdout, f);
599     printf(" returns successfully\n", symname(f));
600     popenv();
601     erecover();
602 }
603 
604 /*
605  * Push the current environment.
606  */
607 
608 private pushenv()
609 {
610     push(Address, pc);
611     push(Lineno, curline);
612     push(String, cursource);
613     push(Boolean, isstopped);
614     push(Symbol, curfunc);
615     push(Word, reg(PROGCTR));
616     push(Word, reg(STKP));
617 }
618 
619 /*
620  * Pop back to the real world.
621  */
622 
623 public popenv()
624 {
625     register String filename;
626 
627     setreg(STKP, pop(Word));
628     setreg(PROGCTR, pop(Word));
629     curfunc = pop(Symbol);
630     isstopped = pop(Boolean);
631     filename = pop(String);
632     curline = pop(Lineno);
633     pc = pop(Address);
634     setsource(filename);
635 }
636 
637 /*
638  * Flush the debuggee's standard output.
639  *
640  * This is VERY dependent on the use of stdio.
641  */
642 
643 public flushoutput()
644 {
645     register Symbol p, iob;
646     register Stack *savesp;
647 
648     p = lookup(identname("fflush", true));
649     while (p != nil and not isblock(p)) {
650 	p = p->next_sym;
651     }
652     if (p != nil) {
653 	iob = lookup(identname("_iob", true));
654 	if (iob != nil) {
655 	    pushenv();
656 	    pc = codeloc(p);
657 	    savesp = sp;
658 	    push(long, address(iob, nil) + sizeof(struct _iobuf));
659 	    setreg(STKP, reg(STKP) - sizeof(long));
660 	    dwrite(savesp, reg(STKP), sizeof(long));
661 	    sp = savesp;
662 	    beginproc(p, 1);
663 	    stepto(return_addr());
664 	    popenv();
665 	}
666     }
667 }
668