1 2 /* Copyright (c) 1982 Regents of the University of California */ 3 4 static char sccsid[] = "@(#)runtime.c 1.8 08/10/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 + 76. 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 + 76, 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 Boolean done; 151 152 frp = &frame; 153 getcurframe(frp); 154 if (f != nil) { 155 done = false; 156 do { 157 p = whatblock(frp->save_pc); 158 if (p == f) { 159 done = true; 160 } else if (p == program) { 161 done = true; 162 frp = nil; 163 } else { 164 frp = nextframe(frp); 165 if (frp == nil) { 166 done = true; 167 } 168 } 169 } while (not done); 170 } 171 return frp; 172 } 173 174 /* 175 * Find the return address of the current procedure/function. 176 */ 177 178 public Address return_addr() 179 { 180 Frame frp; 181 Address addr; 182 struct Frame frame; 183 184 frp = &frame; 185 getcurframe(frp); 186 frp = nextframe(frp); 187 if (frp == nil) { 188 addr = 0; 189 } else { 190 addr = frp->save_pc; 191 } 192 return addr; 193 } 194 195 /* 196 * Push the value associated with the current function. 197 */ 198 199 public pushretval(len, isindirect) 200 Integer len; 201 Boolean isindirect; 202 { 203 Word r0; 204 205 r0 = reg(0); 206 if (isindirect) { 207 rpush((Address) r0, len); 208 } else { 209 switch (len) { 210 case sizeof(char): 211 push(char, r0); 212 break; 213 214 case sizeof(short): 215 push(short, r0); 216 break; 217 218 default: 219 if (len == sizeof(Word)) { 220 push(Word, r0); 221 } else if (len == 2*sizeof(Word)) { 222 push(Word, r0); 223 push(Word, reg(1)); 224 } else { 225 panic("not indirect in pushretval?"); 226 } 227 break; 228 } 229 } 230 } 231 232 /* 233 * Return the base address for locals in the given frame. 234 */ 235 236 public Address locals_base(frp) 237 register Frame frp; 238 { 239 return (frp == nil) ? reg(FRP) : frp->save_fp; 240 } 241 242 /* 243 * Return the base address for arguments in the given frame. 244 */ 245 246 public Address args_base(frp) 247 register Frame frp; 248 { 249 return (frp == nil) ? reg(ARGP) : frp->save_ap; 250 } 251 252 /* 253 * Return saved register n from the given frame. 254 */ 255 256 public Word savereg(n, frp) 257 register Integer n; 258 register Frame frp; 259 { 260 register Word w; 261 262 if (frp == nil) { 263 w = reg(n); 264 } else { 265 switch (n) { 266 case ARGP: 267 w = frp->save_ap; 268 break; 269 270 case FRP: 271 w = frp->save_fp; 272 break; 273 274 case STKP: 275 w = reg(STKP); 276 break; 277 278 case PROGCTR: 279 w = frp->save_pc; 280 break; 281 282 default: 283 assert(n >= 0 and n < NSAVEREG); 284 w = frp->save_reg[n]; 285 break; 286 } 287 } 288 return w; 289 } 290 291 /* 292 * Return the nth argument to the current procedure. 293 */ 294 295 public Word argn(n, frp) 296 Integer n; 297 Frame frp; 298 { 299 Word w; 300 301 dread(&w, args_base(frp) + (n * sizeof(Word)), sizeof(w)); 302 return w; 303 } 304 305 /* 306 * Calculate the entry address for a procedure or function parameter, 307 * given the address of the descriptor. 308 */ 309 310 public Address fparamaddr(a) 311 Address a; 312 { 313 Address r; 314 315 dread(&r, a, sizeof(r)); 316 return r; 317 } 318 319 /* 320 * Print a list of currently active blocks starting with most recent. 321 */ 322 323 public wherecmd() 324 { 325 walkstack(false); 326 } 327 328 /* 329 * Dump the world to the given file. 330 * Like "where", but variables are dumped also. 331 */ 332 333 public dump() 334 { 335 walkstack(true); 336 } 337 338 /* 339 * Walk the stack of active procedures printing information 340 * about each active procedure. 341 */ 342 343 private walkstack(dumpvariables) 344 Boolean dumpvariables; 345 { 346 register Frame frp; 347 register Symbol f; 348 register Boolean save; 349 register Lineno line; 350 struct Frame frame; 351 352 if (notstarted(process)) { 353 error("program is not active"); 354 } else { 355 save = walkingstack; 356 walkingstack = true; 357 frp = &frame; 358 getcurframe(frp); 359 f = whatblock(frp->save_pc); 360 do { 361 printf("%s", symname(f)); 362 if (not isinline(f)) { 363 printparams(f, frp); 364 } 365 line = srcline(frp->save_pc - 1); 366 if (line != 0) { 367 printf(", line %d", line); 368 printf(" in \"%s\"\n", srcfilename(frp->save_pc - 1)); 369 } else { 370 printf(" at 0x%x\n", frp->save_pc); 371 } 372 if (dumpvariables) { 373 dumpvars(f, frp); 374 putchar('\n'); 375 } 376 if (isinline(f)) { 377 f = container(f); 378 } else { 379 frp = nextframe(frp); 380 if (frp != nil) { 381 f = whatblock(frp->save_pc); 382 } 383 } 384 } while (frp != nil and f != program); 385 if (dumpvariables) { 386 printf("in \"%s\":\n", symname(program)); 387 dumpvars(program, nil); 388 putchar('\n'); 389 } 390 walkingstack = save; 391 } 392 } 393 394 /* 395 * Find the entry point of a procedure or function. 396 */ 397 398 public findbeginning(f) 399 Symbol f; 400 { 401 f->symvalue.funcv.beginaddr += 2; 402 } 403 404 /* 405 * Return the address corresponding to the first line in a function. 406 */ 407 408 public Address firstline(f) 409 Symbol f; 410 { 411 Address addr; 412 413 addr = codeloc(f); 414 while (linelookup(addr) == 0 and addr < objsize) { 415 ++addr; 416 } 417 if (addr == objsize) { 418 addr = -1; 419 } 420 return addr; 421 } 422 423 /* 424 * Catcher drops strike three ... 425 */ 426 427 public runtofirst() 428 { 429 Address addr; 430 431 addr = pc; 432 while (linelookup(addr) == 0 and addr < objsize) { 433 ++addr; 434 } 435 if (addr < objsize) { 436 stepto(addr); 437 } 438 } 439 440 /* 441 * Return the address corresponding to the end of the program. 442 * 443 * We look for the entry to "exit". 444 */ 445 446 public Address lastaddr() 447 { 448 register Symbol s; 449 450 s = lookup(identname("exit", true)); 451 if (s == nil) { 452 panic("can't find exit"); 453 } 454 return codeloc(s); 455 } 456 457 /* 458 * Decide if the given function is currently active. 459 * 460 * We avoid calls to "findframe" during a stack trace for efficiency. 461 * Presumably information evaluated while walking the stack is active. 462 */ 463 464 public Boolean isactive(f) 465 Symbol f; 466 { 467 register Boolean b; 468 469 if (isfinished(process)) { 470 b = false; 471 } else { 472 if (walkingstack or f == program or 473 (ismodule(f) and isactive(container(f)))) { 474 b = true; 475 } else { 476 b = (Boolean) (findframe(f) != nil); 477 } 478 } 479 return b; 480 } 481 482 /* 483 * Evaluate a call to a procedure. 484 */ 485 486 public callproc(procnode, arglist) 487 Node procnode; 488 Node arglist; 489 { 490 Symbol proc; 491 Integer argc; 492 493 if (procnode->op != O_SYM) { 494 beginerrmsg(); 495 fprintf(stderr, "can't call \""); 496 prtree(stderr, procnode); 497 fprintf(stderr, "\""); 498 enderrmsg(); 499 } 500 assert(procnode->op == O_SYM); 501 proc = procnode->value.sym; 502 if (not isblock(proc)) { 503 error("\"%s\" is not a procedure or function", symname(proc)); 504 } 505 pushenv(); 506 pc = codeloc(proc); 507 argc = pushargs(proc, arglist); 508 beginproc(proc, argc); 509 isstopped = true; 510 event_once(build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)), 511 buildcmdlist(build(O_PROCRTN, proc))); 512 cont(); 513 /* NOTREACHED */ 514 } 515 516 /* 517 * Push the arguments on the process' stack. We do this by first 518 * evaluating them on the "eval" stack, then copying into the process' 519 * space. 520 */ 521 522 private Integer pushargs(proc, arglist) 523 Symbol proc; 524 Node arglist; 525 { 526 Stack *savesp; 527 int argc, args_size; 528 529 savesp = sp; 530 argc = evalargs(proc, arglist); 531 args_size = sp - savesp; 532 setreg(STKP, reg(STKP) - args_size); 533 dwrite(savesp, reg(STKP), args_size); 534 sp = savesp; 535 return argc; 536 } 537 538 /* 539 * Evaluate arguments left-to-right. 540 */ 541 542 private Integer evalargs(proc, arglist) 543 Symbol proc; 544 Node arglist; 545 { 546 Node p, exp; 547 Symbol arg; 548 Stack *savesp; 549 Address addr; 550 Integer count; 551 552 savesp = sp; 553 count = 0; 554 arg = proc->chain; 555 for (p = arglist; p != nil; p = p->value.arg[1]) { 556 if (p->op != O_COMMA) { 557 panic("evalargs: arglist missing comma"); 558 } 559 if (arg == nil) { 560 sp = savesp; 561 error("too many parameters to %s", symname(proc)); 562 } 563 exp = p->value.arg[0]; 564 if (not compatible(arg->type, exp->nodetype)) { 565 sp = savesp; 566 error("expression for parameter %s is of wrong type", symname(arg)); 567 } 568 if (arg->class == REF) { 569 if (exp->op != O_RVAL) { 570 sp = savesp; 571 error("variable expected for parameter \"%s\"", symname(arg)); 572 } 573 addr = lval(exp->value.arg[0]); 574 push(Address, addr); 575 } else { 576 eval(exp); 577 } 578 arg = arg->chain; 579 ++count; 580 } 581 if (arg != nil) { 582 sp = savesp; 583 error("not enough parameters to %s", symname(proc)); 584 } 585 return count; 586 } 587 588 public procreturn(f) 589 Symbol f; 590 { 591 flushoutput(); 592 putchar('\n'); 593 printname(stdout, f); 594 printf(" returns successfully\n", symname(f)); 595 popenv(); 596 erecover(); 597 } 598 599 /* 600 * Push the current environment. 601 */ 602 603 private pushenv() 604 { 605 push(Address, pc); 606 push(Lineno, curline); 607 push(String, cursource); 608 push(Boolean, isstopped); 609 push(Symbol, curfunc); 610 push(Word, reg(PROGCTR)); 611 push(Word, reg(STKP)); 612 } 613 614 /* 615 * Pop back to the real world. 616 */ 617 618 public popenv() 619 { 620 register String filename; 621 622 setreg(STKP, pop(Word)); 623 setreg(PROGCTR, pop(Word)); 624 curfunc = pop(Symbol); 625 isstopped = pop(Boolean); 626 filename = pop(String); 627 curline = pop(Lineno); 628 pc = pop(Address); 629 setsource(filename); 630 } 631 632 /* 633 * Flush the debuggee's standard output. 634 * 635 * This is VERY dependent on the use of stdio. 636 */ 637 638 public flushoutput() 639 { 640 register Symbol p, iob; 641 register Stack *savesp; 642 643 p = lookup(identname("fflush", true)); 644 while (p != nil and not isblock(p)) { 645 p = p->next_sym; 646 } 647 if (p != nil) { 648 iob = lookup(identname("_iob", true)); 649 if (iob != nil) { 650 pushenv(); 651 pc = codeloc(p); 652 savesp = sp; 653 push(long, address(iob, nil) + sizeof(struct _iobuf)); 654 setreg(STKP, reg(STKP) - sizeof(long)); 655 dwrite(savesp, reg(STKP), sizeof(long)); 656 sp = savesp; 657 beginproc(p, 1); 658 stepto(return_addr()); 659 popenv(); 660 } 661 } 662 } 663