1 2 /* Copyright (c) 1982 Regents of the University of California */ 3 4 static char sccsid[] = "@(#)runtime.c 1.7 07/15/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 printparams(f, frp); 363 line = srcline(frp->save_pc - 1); 364 if (line != 0) { 365 printf(", line %d", line); 366 printf(" in \"%s\"\n", srcfilename(frp->save_pc - 1)); 367 } else { 368 printf(" at 0x%x\n", frp->save_pc); 369 } 370 if (dumpvariables) { 371 dumpvars(f, frp); 372 putchar('\n'); 373 } 374 frp = nextframe(frp); 375 if (frp != nil) { 376 f = whatblock(frp->save_pc); 377 } 378 } while (frp != nil and f != program); 379 if (dumpvariables) { 380 printf("in \"%s\":\n", symname(program)); 381 dumpvars(program, nil); 382 putchar('\n'); 383 } 384 walkingstack = save; 385 } 386 } 387 388 /* 389 * Find the entry point of a procedure or function. 390 */ 391 392 public findbeginning(f) 393 Symbol f; 394 { 395 f->symvalue.funcv.beginaddr += 2; 396 } 397 398 /* 399 * Return the address corresponding to the first line in a function. 400 */ 401 402 public Address firstline(f) 403 Symbol f; 404 { 405 Address addr; 406 407 addr = codeloc(f); 408 while (linelookup(addr) == 0 and addr < objsize) { 409 ++addr; 410 } 411 if (addr == objsize) { 412 addr = -1; 413 } 414 return addr; 415 } 416 417 /* 418 * Catcher drops strike three ... 419 */ 420 421 public runtofirst() 422 { 423 Address addr; 424 425 addr = pc; 426 while (linelookup(addr) == 0 and addr < objsize) { 427 ++addr; 428 } 429 if (addr < objsize) { 430 stepto(addr); 431 } 432 } 433 434 /* 435 * Return the address corresponding to the end of the program. 436 * 437 * We look for the entry to "exit". 438 */ 439 440 public Address lastaddr() 441 { 442 register Symbol s; 443 444 s = lookup(identname("exit", true)); 445 if (s == nil) { 446 panic("can't find exit"); 447 } 448 return codeloc(s); 449 } 450 451 /* 452 * Decide if the given function is currently active. 453 * 454 * We avoid calls to "findframe" during a stack trace for efficiency. 455 * Presumably information evaluated while walking the stack is active. 456 */ 457 458 public Boolean isactive(f) 459 Symbol f; 460 { 461 register Boolean b; 462 463 if (isfinished(process)) { 464 b = false; 465 } else { 466 if (walkingstack or f == program or 467 (ismodule(f) and isactive(container(f)))) { 468 b = true; 469 } else { 470 b = (Boolean) (findframe(f) != nil); 471 } 472 } 473 return b; 474 } 475 476 /* 477 * Evaluate a call to a procedure. 478 */ 479 480 public callproc(procnode, arglist) 481 Node procnode; 482 Node arglist; 483 { 484 Symbol proc; 485 Integer argc; 486 487 if (procnode->op != O_SYM) { 488 beginerrmsg(); 489 fprintf(stderr, "can't call \""); 490 prtree(stderr, procnode); 491 fprintf(stderr, "\""); 492 enderrmsg(); 493 } 494 assert(procnode->op == O_SYM); 495 proc = procnode->value.sym; 496 if (not isblock(proc)) { 497 error("\"%s\" is not a procedure or function", symname(proc)); 498 } 499 pushenv(); 500 pc = codeloc(proc); 501 argc = pushargs(proc, arglist); 502 beginproc(proc, argc); 503 isstopped = true; 504 event_once(build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)), 505 buildcmdlist(build(O_PROCRTN, proc))); 506 cont(); 507 /* NOTREACHED */ 508 } 509 510 /* 511 * Push the arguments on the process' stack. We do this by first 512 * evaluating them on the "eval" stack, then copying into the process' 513 * space. 514 */ 515 516 private Integer pushargs(proc, arglist) 517 Symbol proc; 518 Node arglist; 519 { 520 Stack *savesp; 521 int argc, args_size; 522 523 savesp = sp; 524 argc = evalargs(proc, arglist); 525 args_size = sp - savesp; 526 setreg(STKP, reg(STKP) - args_size); 527 dwrite(savesp, reg(STKP), args_size); 528 sp = savesp; 529 return argc; 530 } 531 532 /* 533 * Evaluate arguments left-to-right. 534 */ 535 536 private Integer evalargs(proc, arglist) 537 Symbol proc; 538 Node arglist; 539 { 540 Node p, exp; 541 Symbol arg; 542 Stack *savesp; 543 Address addr; 544 Integer count; 545 546 savesp = sp; 547 count = 0; 548 arg = proc->chain; 549 for (p = arglist; p != nil; p = p->value.arg[1]) { 550 if (p->op != O_COMMA) { 551 panic("evalargs: arglist missing comma"); 552 } 553 if (arg == nil) { 554 sp = savesp; 555 error("too many parameters to %s", symname(proc)); 556 } 557 exp = p->value.arg[0]; 558 if (not compatible(arg->type, exp->nodetype)) { 559 sp = savesp; 560 error("expression for parameter %s is of wrong type", symname(arg)); 561 } 562 if (arg->class == REF) { 563 if (exp->op != O_RVAL) { 564 sp = savesp; 565 error("variable expected for parameter \"%s\"", symname(arg)); 566 } 567 addr = lval(exp->value.arg[0]); 568 push(Address, addr); 569 } else { 570 eval(exp); 571 } 572 arg = arg->chain; 573 ++count; 574 } 575 if (arg != nil) { 576 sp = savesp; 577 error("not enough parameters to %s", symname(proc)); 578 } 579 return count; 580 } 581 582 public procreturn(f) 583 Symbol f; 584 { 585 flushoutput(); 586 putchar('\n'); 587 printname(stdout, f); 588 printf(" returns successfully\n", symname(f)); 589 popenv(); 590 erecover(); 591 } 592 593 /* 594 * Push the current environment. 595 */ 596 597 private pushenv() 598 { 599 push(Address, pc); 600 push(Lineno, curline); 601 push(String, cursource); 602 push(Boolean, isstopped); 603 push(Symbol, curfunc); 604 push(Word, reg(PROGCTR)); 605 push(Word, reg(STKP)); 606 } 607 608 /* 609 * Pop back to the real world. 610 */ 611 612 public popenv() 613 { 614 register String filename; 615 616 setreg(STKP, pop(Word)); 617 setreg(PROGCTR, pop(Word)); 618 curfunc = pop(Symbol); 619 isstopped = pop(Boolean); 620 filename = pop(String); 621 curline = pop(Lineno); 622 pc = pop(Address); 623 setsource(filename); 624 } 625 626 /* 627 * Flush the debuggee's standard output. 628 * 629 * This is VERY dependent on the use of stdio. 630 */ 631 632 public flushoutput() 633 { 634 register Symbol p, iob; 635 register Stack *savesp; 636 637 p = lookup(identname("fflush", true)); 638 while (p != nil and not isblock(p)) { 639 p = p->next_sym; 640 } 641 if (p != nil) { 642 iob = lookup(identname("_iob", true)); 643 if (iob != nil) { 644 pushenv(); 645 pc = codeloc(p); 646 savesp = sp; 647 push(long, address(iob, nil) + sizeof(struct _iobuf)); 648 setreg(STKP, reg(STKP) - sizeof(long)); 649 dwrite(savesp, reg(STKP), sizeof(long)); 650 sp = savesp; 651 beginproc(p, 1); 652 stepto(return_addr()); 653 popenv(); 654 } 655 } 656 } 657