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