1 2 /* Copyright (c) 1982 Regents of the University of California */ 3 4 static char sccsid[] = "@(#)runtime.c 1.11 (Berkeley) 06/23/84"; 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 Frame curframe = nil; 42 private struct Frame curframerec; 43 private Boolean walkingstack = false; 44 45 #define frameeq(f1, f2) ((f1)->save_fp == (f2)->save_fp) 46 47 /* 48 * Set a frame to the current activation record. 49 */ 50 51 private getcurframe(frp) 52 register Frame frp; 53 { 54 register int i; 55 56 checkref(frp); 57 frp->mask = reg(NREG); 58 frp->save_ap = reg(ARGP); 59 frp->save_fp = reg(FRP); 60 frp->save_pc = reg(PROGCTR) + 1; 61 for (i = 0; i < NSAVEREG; i++) { 62 frp->save_reg[i] = reg(i); 63 } 64 } 65 66 /* 67 * Return a pointer to the next activation record up the stack. 68 * Return nil if there is none. 69 * Writes over space pointed to by given argument. 70 */ 71 72 #define bis(b, n) ((b & (1 << (n))) != 0) 73 74 private Frame nextframe(frp) 75 Frame frp; 76 { 77 register Frame newfrp; 78 struct Frame frame; 79 register Integer i, j, mask; 80 Address prev_frame, callpc; 81 static Integer ntramp = 0; 82 83 newfrp = frp; 84 prev_frame = frp->save_fp; 85 86 /* 87 * The check for interrupt generated frames is taken from adb with only 88 * partial understanding. If you're in "sub" and on a sigxxx "sigsub" 89 * gets control, then the stack does NOT look like <main, sub, sigsub>. 90 * 91 * As best I can make out it looks like: 92 * 93 * <main, (machine check exception block + sub), sysframe, sigsub>. 94 * 95 * When the signal occurs an exception block and a frame for the routine 96 * in which it occured are pushed on the user stack. Then another frame 97 * is pushed corresponding to a call from the kernel to sigsub. 98 * 99 * The addr in sub at which the exception occured is not in sub.save_pc 100 * but in the machine check exception block. It is at the magic address 101 * fp + 84. 102 * 103 * The current approach ignores the sys_frame (what adb reports as sigtramp) 104 * and takes the pc for sub from the exception block. This allows the 105 * "where" command to report <main, sub, sigsub>, which seems reasonable. 106 */ 107 108 nextf: 109 dread(&frame, prev_frame, sizeof(struct Frame)); 110 if (ntramp == 1) { 111 dread(&callpc, prev_frame + 84, sizeof(callpc)); 112 } else { 113 callpc = frame.save_pc; 114 } 115 if (frame.save_fp == nil) { 116 newfrp = nil; 117 } else if (callpc > 0x80000000 - 0x200 * UPAGES ) { 118 ntramp++; 119 prev_frame = frame.save_fp; 120 goto nextf; 121 } else { 122 frame.save_pc = callpc; 123 ntramp = 0; 124 mask = ((frame.mask >> 16) & 0x0fff); 125 j = 0; 126 for (i = 0; i < NSAVEREG; i++) { 127 if (bis(mask, i)) { 128 newfrp->save_reg[i] = frame.save_reg[j]; 129 ++j; 130 } 131 } 132 newfrp->condition_handler = frame.condition_handler; 133 newfrp->mask = mask; 134 newfrp->save_ap = frame.save_ap; 135 newfrp->save_fp = frame.save_fp; 136 newfrp->save_pc = frame.save_pc; 137 } 138 return newfrp; 139 } 140 141 /* 142 * Get the current frame information in the given Frame and store the 143 * associated function in the given value-result parameter. 144 */ 145 146 private getcurfunc (frp, fp) 147 Frame frp; 148 Symbol *fp; 149 { 150 getcurframe(frp); 151 *fp = whatblock(frp->save_pc); 152 } 153 154 /* 155 * Return the frame associated with the next function up the call stack, or 156 * nil if there is none. The function is returned in a value-result parameter. 157 * For "inline" functions the statically outer function and same frame 158 * are returned. 159 */ 160 161 private Frame nextfunc (frp, fp) 162 Frame frp; 163 Symbol *fp; 164 { 165 Symbol t; 166 Frame nfrp; 167 168 t = *fp; 169 checkref(t); 170 if (isinline(t)) { 171 t = container(t); 172 nfrp = frp; 173 } else { 174 nfrp = nextframe(frp); 175 if (nfrp == nil) { 176 t = nil; 177 } else { 178 t = whatblock(nfrp->save_pc); 179 } 180 } 181 *fp = t; 182 return nfrp; 183 } 184 185 /* 186 * Return the frame associated with the given function. 187 * If the function is nil, return the most recently activated frame. 188 * 189 * Static allocation for the frame. 190 */ 191 192 public Frame findframe(f) 193 Symbol f; 194 { 195 register Frame frp; 196 static struct Frame frame; 197 Symbol p; 198 199 frp = &frame; 200 getcurframe(frp); 201 if (f == nil) 202 return (frp); 203 /* 204 * Starting at the current stack frame, 205 * walk backwards looking for a symbol 206 * match. Beware of local blocks which 207 * have a back pointer but no stack frame. 208 */ 209 p = whatblock(frp->save_pc); 210 while (p != f) { 211 if (p == program) { 212 frp = nil; 213 break; 214 } 215 if (isinline(p)) { 216 p = container(p); 217 continue; 218 } 219 frp = nextframe(frp); 220 if (frp == nil) 221 break; 222 p = whatblock(frp->save_pc); 223 } 224 return (frp); 225 } 226 227 /* 228 * Find the return address of the current procedure/function. 229 */ 230 231 public Address return_addr() 232 { 233 Frame frp; 234 Address addr; 235 struct Frame frame; 236 237 frp = &frame; 238 getcurframe(frp); 239 frp = nextframe(frp); 240 if (frp == nil) { 241 addr = 0; 242 } else { 243 addr = frp->save_pc; 244 } 245 return addr; 246 } 247 248 /* 249 * Push the value associated with the current function. 250 */ 251 252 public pushretval(len, isindirect) 253 Integer len; 254 Boolean isindirect; 255 { 256 Word r0; 257 258 r0 = reg(0); 259 if (isindirect) { 260 rpush((Address) r0, len); 261 } else { 262 switch (len) { 263 case sizeof(char): 264 push(char, r0); 265 break; 266 267 case sizeof(short): 268 push(short, r0); 269 break; 270 271 default: 272 if (len == sizeof(Word)) { 273 push(Word, r0); 274 } else if (len == 2*sizeof(Word)) { 275 push(Word, r0); 276 push(Word, reg(1)); 277 } else { 278 panic("not indirect in pushretval?"); 279 } 280 break; 281 } 282 } 283 } 284 285 /* 286 * Return the base address for locals in the given frame. 287 */ 288 289 public Address locals_base(frp) 290 register Frame frp; 291 { 292 return (frp == nil) ? reg(FRP) : frp->save_fp; 293 } 294 295 /* 296 * Return the base address for arguments in the given frame. 297 */ 298 299 public Address args_base(frp) 300 register Frame frp; 301 { 302 return (frp == nil) ? reg(ARGP) : frp->save_ap; 303 } 304 305 /* 306 * Return saved register n from the given frame. 307 */ 308 309 public Word savereg(n, frp) 310 register Integer n; 311 register Frame frp; 312 { 313 register Word w; 314 315 if (frp == nil) { 316 w = reg(n); 317 } else { 318 switch (n) { 319 case ARGP: 320 w = frp->save_ap; 321 break; 322 323 case FRP: 324 w = frp->save_fp; 325 break; 326 327 case STKP: 328 w = reg(STKP); 329 break; 330 331 case PROGCTR: 332 w = frp->save_pc; 333 break; 334 335 default: 336 assert(n >= 0 and n < NSAVEREG); 337 w = frp->save_reg[n]; 338 break; 339 } 340 } 341 return w; 342 } 343 344 /* 345 * Return the nth argument to the current procedure. 346 */ 347 348 public Word argn(n, frp) 349 Integer n; 350 Frame frp; 351 { 352 Word w; 353 354 dread(&w, args_base(frp) + (n * sizeof(Word)), sizeof(w)); 355 return w; 356 } 357 358 /* 359 * Calculate the entry address for a procedure or function parameter, 360 * given the address of the descriptor. 361 */ 362 363 public Address fparamaddr(a) 364 Address a; 365 { 366 Address r; 367 368 dread(&r, a, sizeof(r)); 369 return r; 370 } 371 372 /* 373 * Print a list of currently active blocks starting with most recent. 374 */ 375 376 public wherecmd() 377 { 378 walkstack(false); 379 } 380 381 /* 382 * Dump the world to the given file. 383 * Like "where", but variables are dumped also. 384 */ 385 386 public dump() 387 { 388 walkstack(true); 389 } 390 391 /* 392 * Walk the stack of active procedures printing information 393 * about each active procedure. 394 */ 395 396 private walkstack(dumpvariables) 397 Boolean dumpvariables; 398 { 399 register Frame frp; 400 register Boolean save; 401 register Lineno line; 402 Symbol f; 403 struct Frame frame; 404 405 if (notstarted(process)) { 406 error("program is not active"); 407 } else { 408 save = walkingstack; 409 walkingstack = true; 410 frp = &frame; 411 getcurfunc(frp, &f); 412 do { 413 printf("%s", symname(f)); 414 if (not isinline(f)) { 415 printparams(f, frp); 416 } 417 line = srcline(frp->save_pc - 1); 418 if (line != 0) { 419 printf(", line %d", line); 420 printf(" in \"%s\"\n", srcfilename(frp->save_pc - 1)); 421 } else { 422 printf(" at 0x%x\n", frp->save_pc); 423 } 424 if (dumpvariables) { 425 dumpvars(f, frp); 426 putchar('\n'); 427 } 428 frp = nextfunc(frp, &f); 429 } while (frp != nil and f != program); 430 if (dumpvariables) { 431 printf("in \"%s\":\n", symname(program)); 432 dumpvars(program, nil); 433 putchar('\n'); 434 } 435 walkingstack = save; 436 } 437 } 438 439 /* 440 * Set the current function to the given symbol. 441 * We must adjust "curframe" so that subsequent operations are 442 * not confused; for simplicity we simply clear it. 443 */ 444 445 public setcurfunc (f) 446 Symbol f; 447 { 448 curfunc = f; 449 curframe = nil; 450 } 451 452 /* 453 * Set curfunc to be N up/down the stack from its current value. 454 */ 455 456 public up (n) 457 integer n; 458 { 459 integer i; 460 Symbol f; 461 Frame frp; 462 boolean done; 463 464 if (not isactive(program)) { 465 error("program is not active"); 466 } else if (curfunc == nil) { 467 error("no current function"); 468 } else { 469 i = 0; 470 f = curfunc; 471 if (curframe != nil) { 472 frp = curframe; 473 } else { 474 frp = findframe(f); 475 } 476 done = false; 477 do { 478 if (frp == nil) { 479 done = true; 480 error("not that many levels"); 481 } else if (i >= n) { 482 done = true; 483 curfunc = f; 484 curframe = &curframerec; 485 *curframe = *frp; 486 } else if (f == program) { 487 done = true; 488 error("not that many levels"); 489 } else { 490 frp = nextfunc(frp, &f); 491 } 492 ++i; 493 } while (not done); 494 } 495 } 496 497 public down (n) 498 integer n; 499 { 500 integer i, depth; 501 register Frame frp; 502 Symbol f; 503 struct Frame frame; 504 505 if (not isactive(program)) { 506 error("program is not active"); 507 } else if (curfunc == nil) { 508 error("no current function"); 509 } else { 510 depth = 0; 511 frp = &frame; 512 getcurfunc(frp, &f); 513 if (curframe == nil) { 514 curframe = &curframerec; 515 *curframe = *(findframe(curfunc)); 516 } 517 while ((f != curfunc or !frameeq(frp, curframe)) and f != nil) { 518 frp = nextfunc(frp, &f); 519 ++depth; 520 } 521 if (f == nil or n > depth) { 522 error("not that many levels"); 523 } else { 524 depth -= n; 525 frp = &frame; 526 getcurfunc(frp, &f); 527 for (i = 0; i < depth; i++) { 528 frp = nextfunc(frp, &f); 529 assert(frp != nil); 530 } 531 curfunc = f; 532 *curframe = *frp; 533 } 534 } 535 } 536 537 /* 538 * Find the entry point of a procedure or function. 539 */ 540 541 public findbeginning(f) 542 Symbol f; 543 { 544 if (isinternal(f)) { 545 f->symvalue.funcv.beginaddr += 15; 546 } else { 547 f->symvalue.funcv.beginaddr += 2; 548 } 549 } 550 551 /* 552 * Return the address corresponding to the first line in a function. 553 */ 554 555 public Address firstline(f) 556 Symbol f; 557 { 558 Address addr; 559 560 addr = codeloc(f); 561 while (linelookup(addr) == 0 and addr < objsize) { 562 ++addr; 563 } 564 if (addr == objsize) { 565 addr = -1; 566 } 567 return addr; 568 } 569 570 /* 571 * Catcher drops strike three ... 572 */ 573 574 public runtofirst() 575 { 576 Address addr; 577 578 addr = pc; 579 while (linelookup(addr) == 0 and addr < objsize) { 580 ++addr; 581 } 582 if (addr < objsize) { 583 stepto(addr); 584 } 585 } 586 587 /* 588 * Return the address corresponding to the end of the program. 589 * 590 * We look for the entry to "exit". 591 */ 592 593 public Address lastaddr() 594 { 595 register Symbol s; 596 597 s = lookup(identname("exit", true)); 598 if (s == nil) { 599 panic("can't find exit"); 600 } 601 return codeloc(s); 602 } 603 604 /* 605 * Decide if the given function is currently active. 606 * 607 * We avoid calls to "findframe" during a stack trace for efficiency. 608 * Presumably information evaluated while walking the stack is active. 609 */ 610 611 public Boolean isactive(f) 612 Symbol f; 613 { 614 register Boolean b; 615 616 if (isfinished(process)) { 617 b = false; 618 } else { 619 if (walkingstack or f == program or 620 (ismodule(f) and isactive(container(f)))) { 621 b = true; 622 } else { 623 b = (Boolean) (findframe(f) != nil); 624 } 625 } 626 return b; 627 } 628 629 /* 630 * Evaluate a call to a procedure. 631 */ 632 633 public callproc(procnode, arglist) 634 Node procnode; 635 Node arglist; 636 { 637 Symbol proc; 638 Integer argc; 639 640 if (procnode->op != O_SYM) { 641 beginerrmsg(); 642 fprintf(stderr, "can't call \""); 643 prtree(stderr, procnode); 644 fprintf(stderr, "\""); 645 enderrmsg(); 646 } 647 assert(procnode->op == O_SYM); 648 proc = procnode->value.sym; 649 if (not isblock(proc)) { 650 error("\"%s\" is not a procedure or function", symname(proc)); 651 } 652 pushenv(); 653 pc = codeloc(proc); 654 argc = pushargs(proc, arglist); 655 beginproc(proc, argc); 656 isstopped = true; 657 event_once(build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)), 658 buildcmdlist(build(O_PROCRTN, proc))); 659 cont(0); 660 /* NOTREACHED */ 661 } 662 663 /* 664 * Push the arguments on the process' stack. We do this by first 665 * evaluating them on the "eval" stack, then copying into the process' 666 * space. 667 */ 668 669 private Integer pushargs(proc, arglist) 670 Symbol proc; 671 Node arglist; 672 { 673 Stack *savesp; 674 int argc, args_size; 675 676 savesp = sp; 677 argc = evalargs(proc, arglist); 678 args_size = sp - savesp; 679 setreg(STKP, reg(STKP) - args_size); 680 dwrite(savesp, reg(STKP), args_size); 681 sp = savesp; 682 return argc; 683 } 684 685 /* 686 * Check to see if an expression is correct for a given parameter. 687 * If the given parameter is false, don't worry about type inconsistencies. 688 * 689 * Return whether or not it is ok. 690 */ 691 692 private boolean chkparam (actual, formal, chk) 693 Node actual; 694 Symbol formal; 695 boolean chk; 696 { 697 boolean b; 698 699 b = true; 700 if (chk) { 701 if (formal == nil) { 702 beginerrmsg(); 703 fprintf(stderr, "too many parameters"); 704 b = false; 705 } else if (not compatible(formal->type, actual->nodetype)) { 706 beginerrmsg(); 707 fprintf(stderr, "type mismatch for %s", symname(formal)); 708 b = false; 709 } 710 } 711 if (b and formal != nil and isvarparam(formal) and 712 not isopenarray(formal->type) and actual->op != O_RVAL) 713 { 714 beginerrmsg(); 715 fprintf(stderr, "expected variable, found \""); 716 prtree(stderr, actual); 717 fprintf(stderr, "\""); 718 b = false; 719 } 720 return b; 721 } 722 723 /* 724 * Pass an expression to a particular parameter. 725 * 726 * Normally we pass either the address or value, but in some cases 727 * (such as C strings) we want to copy the value onto the stack and 728 * pass its address. 729 */ 730 731 private passparam (actual, formal) 732 Node actual; 733 Symbol formal; 734 { 735 boolean b; 736 Address addr; 737 Stack *savesp; 738 integer paramsize; 739 740 if (isvarparam(formal) and not isopenarray(formal->type)) { 741 addr = lval(actual->value.arg[0]); 742 push(Address, addr); 743 } else if (passaddr(formal, actual->nodetype)) { 744 savesp = sp; 745 eval(actual); 746 paramsize = sp - savesp; 747 setreg(STKP, reg(STKP) - paramsize); 748 dwrite(savesp, reg(STKP), paramsize); 749 sp = savesp; 750 push(Address, reg(STKP)); 751 if (formal != nil and isopenarray(formal->type)) { 752 push(integer, paramsize div size(formal->type->type)); 753 } 754 } else { 755 eval(actual); 756 } 757 } 758 759 /* 760 * Evaluate an argument list left-to-right. 761 */ 762 763 private Integer evalargs(proc, arglist) 764 Symbol proc; 765 Node arglist; 766 { 767 Node p, actual; 768 Symbol formal; 769 Stack *savesp; 770 Integer count; 771 boolean chk; 772 773 savesp = sp; 774 count = 0; 775 formal = proc->chain; 776 chk = (boolean) (not nosource(proc)); 777 for (p = arglist; p != nil; p = p->value.arg[1]) { 778 assert(p->op == O_COMMA); 779 actual = p->value.arg[0]; 780 if (not chkparam(actual, formal, chk)) { 781 fprintf(stderr, " in call to %s", symname(proc)); 782 sp = savesp; 783 enderrmsg(); 784 } 785 passparam(actual, formal); 786 if (formal != nil) { 787 formal = formal->chain; 788 } 789 ++count; 790 } 791 if (chk) { 792 if (formal != nil) { 793 sp = savesp; 794 error("not enough parameters to %s", symname(proc)); 795 } 796 } 797 return count; 798 } 799 800 public procreturn(f) 801 Symbol f; 802 { 803 flushoutput(); 804 putchar('\n'); 805 printname(stdout, f); 806 printf(" returns successfully\n", symname(f)); 807 popenv(); 808 erecover(); 809 } 810 811 /* 812 * Push the current environment. 813 */ 814 815 private pushenv() 816 { 817 push(Address, pc); 818 push(Lineno, curline); 819 push(String, cursource); 820 push(Boolean, isstopped); 821 push(Symbol, curfunc); 822 push(Frame, curframe); 823 push(struct Frame, curframerec); 824 push(Word, reg(PROGCTR)); 825 push(Word, reg(STKP)); 826 } 827 828 /* 829 * Pop back to the real world. 830 */ 831 832 public popenv() 833 { 834 register String filename; 835 836 setreg(STKP, pop(Word)); 837 setreg(PROGCTR, pop(Word)); 838 curframerec = pop(struct Frame); 839 curframe = pop(Frame); 840 curfunc = pop(Symbol); 841 isstopped = pop(Boolean); 842 filename = pop(String); 843 curline = pop(Lineno); 844 pc = pop(Address); 845 setsource(filename); 846 } 847 848 /* 849 * Flush the debuggee's standard output. 850 * 851 * This is VERY dependent on the use of stdio. 852 */ 853 854 public flushoutput() 855 { 856 register Symbol p, iob; 857 register Stack *savesp; 858 859 p = lookup(identname("fflush", true)); 860 while (p != nil and not isblock(p)) { 861 p = p->next_sym; 862 } 863 if (p != nil) { 864 iob = lookup(identname("_iob", true)); 865 if (iob != nil) { 866 pushenv(); 867 pc = codeloc(p); 868 savesp = sp; 869 push(long, address(iob, nil) + sizeof(struct _iobuf)); 870 setreg(STKP, reg(STKP) - sizeof(long)); 871 dwrite(savesp, reg(STKP), sizeof(long)); 872 sp = savesp; 873 beginproc(p, 1); 874 stepto(return_addr()); 875 popenv(); 876 } 877 } 878 } 879