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