Commit | Line | Data |
---|---|---|
2a24676e DF |
1 | /* |
2 | * Copyright (c) 1983 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
52646e6f | 6 | |
2a24676e | 7 | #ifndef lint |
b9ae3d87 | 8 | static char sccsid[] = "@(#)runtime.c 5.3 (Berkeley) %G%"; |
2a24676e | 9 | #endif not lint |
0022c355 ML |
10 | |
11 | static char rcsid[] = "$Header: runtime.c,v 1.5 84/12/26 10:41:52 linton Exp $"; | |
52646e6f ML |
12 | |
13 | /* | |
14 | * Runtime organization dependent routines, mostly dealing with | |
15 | * activation records. | |
16 | */ | |
17 | ||
18 | #include "defs.h" | |
19 | #include "runtime.h" | |
20 | #include "process.h" | |
21 | #include "machine.h" | |
22 | #include "events.h" | |
23 | #include "mappings.h" | |
24 | #include "symbols.h" | |
25 | #include "tree.h" | |
26 | #include "eval.h" | |
27 | #include "operators.h" | |
28 | #include "object.h" | |
381c0f0e | 29 | #include <sys/param.h> |
52646e6f ML |
30 | |
31 | #ifndef public | |
32 | typedef struct Frame *Frame; | |
33 | ||
34 | #include "machine.h" | |
35 | #endif | |
36 | ||
37 | #define NSAVEREG 12 | |
38 | ||
39 | struct Frame { | |
0022c355 ML |
40 | integer condition_handler; |
41 | integer mask; | |
52646e6f ML |
42 | Address save_ap; /* argument pointer */ |
43 | Address save_fp; /* frame pointer */ | |
44 | Address save_pc; /* program counter */ | |
45 | Word save_reg[NSAVEREG]; /* not necessarily there */ | |
46 | }; | |
47 | ||
2fd0f574 SL |
48 | private Frame curframe = nil; |
49 | private struct Frame curframerec; | |
52646e6f ML |
50 | private Boolean walkingstack = false; |
51 | ||
2fd0f574 SL |
52 | #define frameeq(f1, f2) ((f1)->save_fp == (f2)->save_fp) |
53 | ||
0022c355 ML |
54 | #define isstackaddr(addr) \ |
55 | (((addr) < 0x80000000) and ((addr) > 0x80000000 - 0x200 * UPAGES)) | |
56 | ||
57 | typedef struct { | |
58 | Node callnode; | |
59 | Node cmdnode; | |
60 | boolean isfunc; | |
61 | } CallEnv; | |
62 | ||
63 | private CallEnv endproc; | |
64 | ||
52646e6f ML |
65 | /* |
66 | * Set a frame to the current activation record. | |
67 | */ | |
68 | ||
69 | private getcurframe(frp) | |
0022c355 | 70 | Frame frp; |
52646e6f ML |
71 | { |
72 | register int i; | |
73 | ||
74 | checkref(frp); | |
75 | frp->mask = reg(NREG); | |
76 | frp->save_ap = reg(ARGP); | |
77 | frp->save_fp = reg(FRP); | |
0022c355 | 78 | frp->save_pc = reg(PROGCTR); |
52646e6f ML |
79 | for (i = 0; i < NSAVEREG; i++) { |
80 | frp->save_reg[i] = reg(i); | |
81 | } | |
82 | } | |
83 | ||
0022c355 ML |
84 | /* |
85 | * Get the saved registers from one frame to another | |
86 | * given mask specifying which registers were actually saved. | |
87 | */ | |
88 | ||
89 | #define bis(b, n) ((b & (1 << (n))) != 0) | |
90 | ||
91 | private getsaveregs (newfrp, frp, mask) | |
92 | Frame newfrp, frp; | |
93 | integer mask; | |
94 | { | |
95 | integer i, j; | |
96 | ||
97 | j = 0; | |
98 | for (i = 0; i < NSAVEREG; i++) { | |
99 | if (bis(mask, i)) { | |
100 | newfrp->save_reg[i] = frp->save_reg[j]; | |
101 | ++j; | |
102 | } | |
103 | } | |
104 | } | |
105 | ||
52646e6f ML |
106 | /* |
107 | * Return a pointer to the next activation record up the stack. | |
108 | * Return nil if there is none. | |
109 | * Writes over space pointed to by given argument. | |
110 | */ | |
111 | ||
52646e6f ML |
112 | private Frame nextframe(frp) |
113 | Frame frp; | |
114 | { | |
0022c355 | 115 | Frame newfrp; |
52646e6f | 116 | struct Frame frame; |
0022c355 | 117 | integer mask; |
381c0f0e | 118 | Address prev_frame, callpc; |
0022c355 | 119 | static integer ntramp = 0; |
52646e6f ML |
120 | |
121 | newfrp = frp; | |
381c0f0e AF |
122 | prev_frame = frp->save_fp; |
123 | ||
316d2228 ML |
124 | /* |
125 | * The check for interrupt generated frames is taken from adb with only | |
126 | * partial understanding. If you're in "sub" and on a sigxxx "sigsub" | |
127 | * gets control, then the stack does NOT look like <main, sub, sigsub>. | |
381c0f0e AF |
128 | * |
129 | * As best I can make out it looks like: | |
316d2228 ML |
130 | * |
131 | * <main, (machine check exception block + sub), sysframe, sigsub>. | |
132 | * | |
133 | * When the signal occurs an exception block and a frame for the routine | |
134 | * in which it occured are pushed on the user stack. Then another frame | |
135 | * is pushed corresponding to a call from the kernel to sigsub. | |
381c0f0e AF |
136 | * |
137 | * The addr in sub at which the exception occured is not in sub.save_pc | |
316d2228 | 138 | * but in the machine check exception block. It is at the magic address |
8d44f3a5 | 139 | * fp + 84. |
381c0f0e AF |
140 | * |
141 | * The current approach ignores the sys_frame (what adb reports as sigtramp) | |
316d2228 ML |
142 | * and takes the pc for sub from the exception block. This allows the |
143 | * "where" command to report <main, sub, sigsub>, which seems reasonable. | |
381c0f0e | 144 | */ |
381c0f0e | 145 | |
316d2228 ML |
146 | nextf: |
147 | dread(&frame, prev_frame, sizeof(struct Frame)); | |
148 | if (ntramp == 1) { | |
8d44f3a5 | 149 | dread(&callpc, prev_frame + 84, sizeof(callpc)); |
316d2228 ML |
150 | } else { |
151 | callpc = frame.save_pc; | |
152 | } | |
0022c355 | 153 | if (frame.save_fp == nil or frame.save_pc == (Address) -1) { |
52646e6f | 154 | newfrp = nil; |
0022c355 ML |
155 | } else if (isstackaddr(callpc)) { |
156 | ntramp++; | |
157 | prev_frame = frame.save_fp; | |
158 | goto nextf; | |
316d2228 | 159 | } else { |
381c0f0e | 160 | frame.save_pc = callpc; |
316d2228 | 161 | ntramp = 0; |
52646e6f | 162 | mask = ((frame.mask >> 16) & 0x0fff); |
0022c355 | 163 | getsaveregs(newfrp, &frame, mask); |
52646e6f ML |
164 | newfrp->condition_handler = frame.condition_handler; |
165 | newfrp->mask = mask; | |
166 | newfrp->save_ap = frame.save_ap; | |
167 | newfrp->save_fp = frame.save_fp; | |
168 | newfrp->save_pc = frame.save_pc; | |
169 | } | |
170 | return newfrp; | |
171 | } | |
172 | ||
2fd0f574 SL |
173 | /* |
174 | * Get the current frame information in the given Frame and store the | |
175 | * associated function in the given value-result parameter. | |
176 | */ | |
177 | ||
178 | private getcurfunc (frp, fp) | |
179 | Frame frp; | |
180 | Symbol *fp; | |
181 | { | |
182 | getcurframe(frp); | |
183 | *fp = whatblock(frp->save_pc); | |
184 | } | |
185 | ||
186 | /* | |
187 | * Return the frame associated with the next function up the call stack, or | |
188 | * nil if there is none. The function is returned in a value-result parameter. | |
189 | * For "inline" functions the statically outer function and same frame | |
190 | * are returned. | |
191 | */ | |
192 | ||
0022c355 | 193 | public Frame nextfunc (frp, fp) |
2fd0f574 SL |
194 | Frame frp; |
195 | Symbol *fp; | |
196 | { | |
197 | Symbol t; | |
198 | Frame nfrp; | |
199 | ||
200 | t = *fp; | |
201 | checkref(t); | |
202 | if (isinline(t)) { | |
203 | t = container(t); | |
204 | nfrp = frp; | |
205 | } else { | |
206 | nfrp = nextframe(frp); | |
207 | if (nfrp == nil) { | |
208 | t = nil; | |
209 | } else { | |
210 | t = whatblock(nfrp->save_pc); | |
211 | } | |
212 | } | |
213 | *fp = t; | |
214 | return nfrp; | |
215 | } | |
216 | ||
52646e6f ML |
217 | /* |
218 | * Return the frame associated with the given function. | |
219 | * If the function is nil, return the most recently activated frame. | |
220 | * | |
221 | * Static allocation for the frame. | |
222 | */ | |
223 | ||
224 | public Frame findframe(f) | |
225 | Symbol f; | |
226 | { | |
0022c355 | 227 | Frame frp; |
52646e6f | 228 | static struct Frame frame; |
4fd956ac | 229 | Symbol p; |
0022c355 | 230 | Boolean done; |
52646e6f ML |
231 | |
232 | frp = &frame; | |
233 | getcurframe(frp); | |
0022c355 ML |
234 | if (f != nil) { |
235 | if (f == curfunc and curframe != nil) { | |
236 | *frp = *curframe; | |
237 | } else { | |
238 | done = false; | |
239 | p = whatblock(frp->save_pc); | |
240 | do { | |
241 | if (p == f) { | |
242 | done = true; | |
243 | } else if (p == program) { | |
244 | done = true; | |
245 | frp = nil; | |
246 | } else { | |
247 | frp = nextfunc(frp, &p); | |
248 | if (frp == nil) { | |
249 | done = true; | |
250 | } | |
251 | } | |
252 | } while (not done); | |
912533aa | 253 | } |
0022c355 ML |
254 | } |
255 | return frp; | |
256 | } | |
257 | ||
258 | /* | |
259 | * Set the registers according to the given frame pointer. | |
260 | */ | |
261 | ||
262 | public getnewregs (addr) | |
263 | Address addr; | |
264 | { | |
265 | struct Frame frame; | |
266 | integer i, j, mask; | |
267 | ||
268 | dread(&frame, addr, sizeof(frame)); | |
269 | setreg(ARGP, frame.save_ap); | |
270 | setreg(FRP, frame.save_fp); | |
271 | setreg(PROGCTR, frame.save_pc); | |
272 | mask = ((frame.mask >> 16) & 0x0fff); | |
273 | j = 0; | |
274 | for (i = 0; i < NSAVEREG; i++) { | |
275 | if (bis(mask, i)) { | |
276 | setreg(i, frame.save_reg[j]); | |
277 | ++j; | |
912533aa | 278 | } |
52646e6f | 279 | } |
0022c355 ML |
280 | pc = frame.save_pc; |
281 | setcurfunc(whatblock(pc)); | |
52646e6f ML |
282 | } |
283 | ||
284 | /* | |
285 | * Find the return address of the current procedure/function. | |
286 | */ | |
287 | ||
288 | public Address return_addr() | |
289 | { | |
290 | Frame frp; | |
291 | Address addr; | |
292 | struct Frame frame; | |
293 | ||
294 | frp = &frame; | |
295 | getcurframe(frp); | |
296 | frp = nextframe(frp); | |
297 | if (frp == nil) { | |
298 | addr = 0; | |
299 | } else { | |
300 | addr = frp->save_pc; | |
301 | } | |
302 | return addr; | |
303 | } | |
304 | ||
305 | /* | |
306 | * Push the value associated with the current function. | |
307 | */ | |
308 | ||
309 | public pushretval(len, isindirect) | |
0022c355 ML |
310 | integer len; |
311 | boolean isindirect; | |
52646e6f ML |
312 | { |
313 | Word r0; | |
314 | ||
315 | r0 = reg(0); | |
316 | if (isindirect) { | |
317 | rpush((Address) r0, len); | |
318 | } else { | |
319 | switch (len) { | |
320 | case sizeof(char): | |
321 | push(char, r0); | |
322 | break; | |
323 | ||
324 | case sizeof(short): | |
325 | push(short, r0); | |
326 | break; | |
327 | ||
328 | default: | |
329 | if (len == sizeof(Word)) { | |
330 | push(Word, r0); | |
331 | } else if (len == 2*sizeof(Word)) { | |
332 | push(Word, r0); | |
333 | push(Word, reg(1)); | |
334 | } else { | |
0022c355 | 335 | error("[internal error: bad size %d in pushretval]", len); |
52646e6f ML |
336 | } |
337 | break; | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
342 | /* | |
343 | * Return the base address for locals in the given frame. | |
344 | */ | |
345 | ||
346 | public Address locals_base(frp) | |
0022c355 | 347 | Frame frp; |
52646e6f ML |
348 | { |
349 | return (frp == nil) ? reg(FRP) : frp->save_fp; | |
350 | } | |
351 | ||
352 | /* | |
353 | * Return the base address for arguments in the given frame. | |
354 | */ | |
355 | ||
356 | public Address args_base(frp) | |
0022c355 | 357 | Frame frp; |
52646e6f ML |
358 | { |
359 | return (frp == nil) ? reg(ARGP) : frp->save_ap; | |
360 | } | |
361 | ||
362 | /* | |
363 | * Return saved register n from the given frame. | |
364 | */ | |
365 | ||
366 | public Word savereg(n, frp) | |
0022c355 ML |
367 | integer n; |
368 | Frame frp; | |
52646e6f | 369 | { |
0022c355 | 370 | Word w; |
52646e6f ML |
371 | |
372 | if (frp == nil) { | |
373 | w = reg(n); | |
374 | } else { | |
375 | switch (n) { | |
376 | case ARGP: | |
377 | w = frp->save_ap; | |
378 | break; | |
379 | ||
380 | case FRP: | |
381 | w = frp->save_fp; | |
382 | break; | |
383 | ||
384 | case STKP: | |
385 | w = reg(STKP); | |
386 | break; | |
387 | ||
388 | case PROGCTR: | |
389 | w = frp->save_pc; | |
390 | break; | |
391 | ||
392 | default: | |
393 | assert(n >= 0 and n < NSAVEREG); | |
394 | w = frp->save_reg[n]; | |
395 | break; | |
396 | } | |
397 | } | |
398 | return w; | |
399 | } | |
400 | ||
401 | /* | |
402 | * Return the nth argument to the current procedure. | |
403 | */ | |
404 | ||
405 | public Word argn(n, frp) | |
0022c355 | 406 | integer n; |
52646e6f ML |
407 | Frame frp; |
408 | { | |
409 | Word w; | |
410 | ||
411 | dread(&w, args_base(frp) + (n * sizeof(Word)), sizeof(w)); | |
412 | return w; | |
413 | } | |
414 | ||
415 | /* | |
0022c355 | 416 | * Print a list of currently active blocks starting with most recent. |
52646e6f ML |
417 | */ |
418 | ||
0022c355 | 419 | public wherecmd() |
52646e6f | 420 | { |
0022c355 | 421 | walkstack(false); |
52646e6f ML |
422 | } |
423 | ||
424 | /* | |
0022c355 | 425 | * Print the variables in the given frame or the current one if nil. |
52646e6f ML |
426 | */ |
427 | ||
0022c355 ML |
428 | public dump (func) |
429 | Symbol func; | |
52646e6f | 430 | { |
0022c355 ML |
431 | Symbol f; |
432 | Frame frp; | |
433 | ||
434 | if (func == nil) { | |
435 | f = curfunc; | |
436 | if (curframe != nil) { | |
437 | frp = curframe; | |
438 | } else { | |
439 | frp = findframe(f); | |
440 | } | |
441 | } else { | |
442 | f = func; | |
443 | frp = findframe(f); | |
444 | } | |
445 | showaggrs = true; | |
446 | printcallinfo(f, frp); | |
447 | dumpvars(f, frp); | |
52646e6f ML |
448 | } |
449 | ||
450 | /* | |
0022c355 | 451 | * Dump all values. |
52646e6f ML |
452 | */ |
453 | ||
0022c355 | 454 | public dumpall () |
52646e6f ML |
455 | { |
456 | walkstack(true); | |
457 | } | |
458 | ||
459 | /* | |
460 | * Walk the stack of active procedures printing information | |
461 | * about each active procedure. | |
462 | */ | |
463 | ||
52646e6f ML |
464 | private walkstack(dumpvariables) |
465 | Boolean dumpvariables; | |
466 | { | |
0022c355 ML |
467 | Frame frp; |
468 | boolean save; | |
2fd0f574 | 469 | Symbol f; |
52646e6f ML |
470 | struct Frame frame; |
471 | ||
0022c355 | 472 | if (notstarted(process) or isfinished(process)) { |
52646e6f ML |
473 | error("program is not active"); |
474 | } else { | |
475 | save = walkingstack; | |
476 | walkingstack = true; | |
0022c355 | 477 | showaggrs = dumpvariables; |
52646e6f | 478 | frp = &frame; |
2fd0f574 | 479 | getcurfunc(frp, &f); |
0022c355 ML |
480 | for (;;) { |
481 | printcallinfo(f, frp); | |
52646e6f ML |
482 | if (dumpvariables) { |
483 | dumpvars(f, frp); | |
484 | putchar('\n'); | |
485 | } | |
2fd0f574 | 486 | frp = nextfunc(frp, &f); |
0022c355 ML |
487 | if (frp == nil or f == program) { |
488 | break; | |
489 | } | |
490 | } | |
52646e6f ML |
491 | if (dumpvariables) { |
492 | printf("in \"%s\":\n", symname(program)); | |
493 | dumpvars(program, nil); | |
494 | putchar('\n'); | |
495 | } | |
496 | walkingstack = save; | |
497 | } | |
498 | } | |
499 | ||
0022c355 ML |
500 | /* |
501 | * Print out the information about a call, i.e., | |
502 | * routine name, parameter values, and source location. | |
503 | */ | |
504 | ||
505 | private printcallinfo (f, frp) | |
506 | Symbol f; | |
507 | Frame frp; | |
508 | { | |
509 | Lineno line; | |
510 | Address savepc; | |
511 | ||
512 | savepc = frp->save_pc; | |
513 | if (frp->save_fp != reg(FRP)) { | |
514 | savepc -= 1; | |
515 | } | |
516 | printname(stdout, f); | |
517 | if (not isinline(f)) { | |
518 | printparams(f, frp); | |
519 | } | |
520 | line = srcline(savepc); | |
521 | if (line != 0) { | |
522 | printf(", line %d", line); | |
523 | printf(" in \"%s\"\n", srcfilename(savepc)); | |
524 | } else { | |
525 | printf(" at 0x%x\n", savepc); | |
526 | } | |
527 | } | |
528 | ||
2fd0f574 SL |
529 | /* |
530 | * Set the current function to the given symbol. | |
531 | * We must adjust "curframe" so that subsequent operations are | |
532 | * not confused; for simplicity we simply clear it. | |
533 | */ | |
534 | ||
535 | public setcurfunc (f) | |
536 | Symbol f; | |
537 | { | |
538 | curfunc = f; | |
539 | curframe = nil; | |
540 | } | |
541 | ||
0022c355 ML |
542 | /* |
543 | * Return the frame for the current function. | |
544 | * The space for the frame is allocated statically. | |
545 | */ | |
546 | ||
547 | public Frame curfuncframe () | |
548 | { | |
549 | static struct Frame frame; | |
550 | Frame frp; | |
551 | ||
552 | if (curframe == nil) { | |
553 | frp = findframe(curfunc); | |
554 | curframe = &curframerec; | |
555 | *curframe = *frp; | |
556 | } else { | |
557 | frp = &frame; | |
558 | *frp = *curframe; | |
559 | } | |
560 | return frp; | |
561 | } | |
562 | ||
2fd0f574 SL |
563 | /* |
564 | * Set curfunc to be N up/down the stack from its current value. | |
565 | */ | |
566 | ||
567 | public up (n) | |
568 | integer n; | |
569 | { | |
570 | integer i; | |
571 | Symbol f; | |
572 | Frame frp; | |
573 | boolean done; | |
574 | ||
575 | if (not isactive(program)) { | |
576 | error("program is not active"); | |
577 | } else if (curfunc == nil) { | |
578 | error("no current function"); | |
579 | } else { | |
580 | i = 0; | |
581 | f = curfunc; | |
0022c355 | 582 | frp = curfuncframe(); |
2fd0f574 SL |
583 | done = false; |
584 | do { | |
585 | if (frp == nil) { | |
586 | done = true; | |
587 | error("not that many levels"); | |
588 | } else if (i >= n) { | |
589 | done = true; | |
590 | curfunc = f; | |
591 | curframe = &curframerec; | |
592 | *curframe = *frp; | |
0022c355 ML |
593 | showaggrs = false; |
594 | printcallinfo(curfunc, curframe); | |
2fd0f574 SL |
595 | } else if (f == program) { |
596 | done = true; | |
597 | error("not that many levels"); | |
598 | } else { | |
599 | frp = nextfunc(frp, &f); | |
600 | } | |
601 | ++i; | |
602 | } while (not done); | |
603 | } | |
604 | } | |
605 | ||
606 | public down (n) | |
607 | integer n; | |
608 | { | |
609 | integer i, depth; | |
0022c355 | 610 | Frame frp, curfrp; |
2fd0f574 SL |
611 | Symbol f; |
612 | struct Frame frame; | |
613 | ||
614 | if (not isactive(program)) { | |
615 | error("program is not active"); | |
616 | } else if (curfunc == nil) { | |
617 | error("no current function"); | |
618 | } else { | |
619 | depth = 0; | |
620 | frp = &frame; | |
621 | getcurfunc(frp, &f); | |
622 | if (curframe == nil) { | |
0022c355 | 623 | curfrp = findframe(curfunc); |
2fd0f574 | 624 | curframe = &curframerec; |
0022c355 | 625 | *curframe = *curfrp; |
2fd0f574 SL |
626 | } |
627 | while ((f != curfunc or !frameeq(frp, curframe)) and f != nil) { | |
628 | frp = nextfunc(frp, &f); | |
629 | ++depth; | |
630 | } | |
631 | if (f == nil or n > depth) { | |
632 | error("not that many levels"); | |
633 | } else { | |
634 | depth -= n; | |
635 | frp = &frame; | |
636 | getcurfunc(frp, &f); | |
637 | for (i = 0; i < depth; i++) { | |
638 | frp = nextfunc(frp, &f); | |
639 | assert(frp != nil); | |
640 | } | |
641 | curfunc = f; | |
642 | *curframe = *frp; | |
0022c355 ML |
643 | showaggrs = false; |
644 | printcallinfo(curfunc, curframe); | |
2fd0f574 SL |
645 | } |
646 | } | |
647 | } | |
648 | ||
52646e6f ML |
649 | /* |
650 | * Find the entry point of a procedure or function. | |
651 | */ | |
652 | ||
0022c355 | 653 | public findbeginning (f) |
52646e6f ML |
654 | Symbol f; |
655 | { | |
2fd0f574 SL |
656 | if (isinternal(f)) { |
657 | f->symvalue.funcv.beginaddr += 15; | |
658 | } else { | |
659 | f->symvalue.funcv.beginaddr += 2; | |
660 | } | |
52646e6f ML |
661 | } |
662 | ||
663 | /* | |
664 | * Return the address corresponding to the first line in a function. | |
665 | */ | |
666 | ||
667 | public Address firstline(f) | |
668 | Symbol f; | |
669 | { | |
670 | Address addr; | |
671 | ||
672 | addr = codeloc(f); | |
673 | while (linelookup(addr) == 0 and addr < objsize) { | |
674 | ++addr; | |
675 | } | |
676 | if (addr == objsize) { | |
677 | addr = -1; | |
678 | } | |
679 | return addr; | |
680 | } | |
681 | ||
682 | /* | |
683 | * Catcher drops strike three ... | |
684 | */ | |
685 | ||
686 | public runtofirst() | |
687 | { | |
688 | Address addr; | |
689 | ||
690 | addr = pc; | |
691 | while (linelookup(addr) == 0 and addr < objsize) { | |
692 | ++addr; | |
693 | } | |
694 | if (addr < objsize) { | |
695 | stepto(addr); | |
696 | } | |
697 | } | |
698 | ||
699 | /* | |
700 | * Return the address corresponding to the end of the program. | |
701 | * | |
702 | * We look for the entry to "exit". | |
703 | */ | |
704 | ||
705 | public Address lastaddr() | |
706 | { | |
0022c355 | 707 | Symbol s; |
52646e6f ML |
708 | |
709 | s = lookup(identname("exit", true)); | |
710 | if (s == nil) { | |
711 | panic("can't find exit"); | |
712 | } | |
713 | return codeloc(s); | |
714 | } | |
715 | ||
716 | /* | |
717 | * Decide if the given function is currently active. | |
718 | * | |
719 | * We avoid calls to "findframe" during a stack trace for efficiency. | |
720 | * Presumably information evaluated while walking the stack is active. | |
721 | */ | |
722 | ||
723 | public Boolean isactive(f) | |
724 | Symbol f; | |
725 | { | |
0022c355 | 726 | Boolean b; |
52646e6f ML |
727 | |
728 | if (isfinished(process)) { | |
729 | b = false; | |
730 | } else { | |
731 | if (walkingstack or f == program or | |
732 | (ismodule(f) and isactive(container(f)))) { | |
733 | b = true; | |
734 | } else { | |
735 | b = (Boolean) (findframe(f) != nil); | |
736 | } | |
737 | } | |
738 | return b; | |
739 | } | |
740 | ||
741 | /* | |
742 | * Evaluate a call to a procedure. | |
743 | */ | |
744 | ||
0022c355 ML |
745 | public callproc(exprnode, isfunc) |
746 | Node exprnode; | |
747 | boolean isfunc; | |
52646e6f | 748 | { |
0022c355 | 749 | Node procnode, arglist; |
52646e6f | 750 | Symbol proc; |
0022c355 | 751 | integer argc; |
52646e6f | 752 | |
0022c355 ML |
753 | procnode = exprnode->value.arg[0]; |
754 | arglist = exprnode->value.arg[1]; | |
52646e6f ML |
755 | if (procnode->op != O_SYM) { |
756 | beginerrmsg(); | |
757 | fprintf(stderr, "can't call \""); | |
758 | prtree(stderr, procnode); | |
759 | fprintf(stderr, "\""); | |
760 | enderrmsg(); | |
761 | } | |
762 | assert(procnode->op == O_SYM); | |
763 | proc = procnode->value.sym; | |
764 | if (not isblock(proc)) { | |
765 | error("\"%s\" is not a procedure or function", symname(proc)); | |
766 | } | |
0022c355 ML |
767 | endproc.isfunc = isfunc; |
768 | endproc.callnode = exprnode; | |
769 | endproc.cmdnode = topnode; | |
52646e6f ML |
770 | pushenv(); |
771 | pc = codeloc(proc); | |
772 | argc = pushargs(proc, arglist); | |
773 | beginproc(proc, argc); | |
0022c355 ML |
774 | event_once( |
775 | build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)), | |
776 | buildcmdlist(build(O_PROCRTN, proc)) | |
777 | ); | |
778 | isstopped = false; | |
779 | if (not bpact()) { | |
780 | isstopped = true; | |
781 | cont(0); | |
782 | } | |
783 | /* | |
784 | * bpact() won't return true, it will call printstatus() and go back | |
785 | * to command input if a breakpoint is found. | |
786 | */ | |
52646e6f ML |
787 | /* NOTREACHED */ |
788 | } | |
789 | ||
790 | /* | |
791 | * Push the arguments on the process' stack. We do this by first | |
792 | * evaluating them on the "eval" stack, then copying into the process' | |
793 | * space. | |
794 | */ | |
795 | ||
0022c355 | 796 | private integer pushargs(proc, arglist) |
52646e6f ML |
797 | Symbol proc; |
798 | Node arglist; | |
799 | { | |
800 | Stack *savesp; | |
801 | int argc, args_size; | |
802 | ||
803 | savesp = sp; | |
6167b701 DS |
804 | if (varIsSet("$unsafecall")) { |
805 | argc = unsafe_evalargs(proc, arglist); | |
806 | } else { | |
807 | argc = evalargs(proc, arglist); | |
808 | } | |
52646e6f ML |
809 | args_size = sp - savesp; |
810 | setreg(STKP, reg(STKP) - args_size); | |
811 | dwrite(savesp, reg(STKP), args_size); | |
812 | sp = savesp; | |
813 | return argc; | |
814 | } | |
815 | ||
816 | /* | |
2fd0f574 SL |
817 | * Check to see if an expression is correct for a given parameter. |
818 | * If the given parameter is false, don't worry about type inconsistencies. | |
819 | * | |
820 | * Return whether or not it is ok. | |
821 | */ | |
822 | ||
823 | private boolean chkparam (actual, formal, chk) | |
824 | Node actual; | |
825 | Symbol formal; | |
826 | boolean chk; | |
827 | { | |
828 | boolean b; | |
829 | ||
830 | b = true; | |
831 | if (chk) { | |
832 | if (formal == nil) { | |
833 | beginerrmsg(); | |
834 | fprintf(stderr, "too many parameters"); | |
835 | b = false; | |
836 | } else if (not compatible(formal->type, actual->nodetype)) { | |
837 | beginerrmsg(); | |
838 | fprintf(stderr, "type mismatch for %s", symname(formal)); | |
839 | b = false; | |
840 | } | |
841 | } | |
0022c355 ML |
842 | if (b and formal != nil and |
843 | isvarparam(formal) and not isopenarray(formal->type) and | |
844 | not ( | |
845 | actual->op == O_RVAL or actual->nodetype == t_addr or | |
846 | ( | |
847 | actual->op == O_TYPERENAME and | |
848 | ( | |
849 | actual->value.arg[0]->op == O_RVAL or | |
850 | actual->value.arg[0]->nodetype == t_addr | |
851 | ) | |
852 | ) | |
853 | ) | |
854 | ) { | |
2fd0f574 SL |
855 | beginerrmsg(); |
856 | fprintf(stderr, "expected variable, found \""); | |
857 | prtree(stderr, actual); | |
858 | fprintf(stderr, "\""); | |
859 | b = false; | |
860 | } | |
861 | return b; | |
862 | } | |
863 | ||
864 | /* | |
865 | * Pass an expression to a particular parameter. | |
866 | * | |
867 | * Normally we pass either the address or value, but in some cases | |
868 | * (such as C strings) we want to copy the value onto the stack and | |
869 | * pass its address. | |
0022c355 ML |
870 | * |
871 | * Another special case raised by strings is the possibility that | |
872 | * the actual parameter will be larger than the formal, even with | |
873 | * appropriate type-checking. This occurs because we assume during | |
874 | * evaluation that strings are null-terminated, whereas some languages, | |
875 | * notably Pascal, do not work under that assumption. | |
2fd0f574 SL |
876 | */ |
877 | ||
878 | private passparam (actual, formal) | |
879 | Node actual; | |
880 | Symbol formal; | |
881 | { | |
882 | boolean b; | |
883 | Address addr; | |
884 | Stack *savesp; | |
0022c355 | 885 | integer actsize, formsize; |
2fd0f574 | 886 | |
0022c355 ML |
887 | if (formal != nil and isvarparam(formal) and |
888 | (not isopenarray(formal->type)) | |
889 | ) { | |
2fd0f574 SL |
890 | addr = lval(actual->value.arg[0]); |
891 | push(Address, addr); | |
892 | } else if (passaddr(formal, actual->nodetype)) { | |
893 | savesp = sp; | |
894 | eval(actual); | |
0022c355 ML |
895 | actsize = sp - savesp; |
896 | setreg(STKP, | |
897 | reg(STKP) - ((actsize + sizeof(Word) - 1) & ~(sizeof(Word) - 1)) | |
898 | ); | |
899 | dwrite(savesp, reg(STKP), actsize); | |
2fd0f574 SL |
900 | sp = savesp; |
901 | push(Address, reg(STKP)); | |
902 | if (formal != nil and isopenarray(formal->type)) { | |
0022c355 ML |
903 | push(integer, actsize div size(formal->type->type)); |
904 | } | |
905 | } else if (formal != nil) { | |
906 | formsize = size(formal); | |
907 | savesp = sp; | |
908 | eval(actual); | |
909 | actsize = sp - savesp; | |
910 | if (actsize > formsize) { | |
911 | sp -= (actsize - formsize); | |
2fd0f574 SL |
912 | } |
913 | } else { | |
914 | eval(actual); | |
915 | } | |
916 | } | |
917 | ||
918 | /* | |
919 | * Evaluate an argument list left-to-right. | |
52646e6f ML |
920 | */ |
921 | ||
0022c355 | 922 | private integer evalargs(proc, arglist) |
52646e6f ML |
923 | Symbol proc; |
924 | Node arglist; | |
925 | { | |
2fd0f574 SL |
926 | Node p, actual; |
927 | Symbol formal; | |
52646e6f | 928 | Stack *savesp; |
0022c355 | 929 | integer count; |
2fd0f574 | 930 | boolean chk; |
52646e6f ML |
931 | |
932 | savesp = sp; | |
933 | count = 0; | |
2fd0f574 SL |
934 | formal = proc->chain; |
935 | chk = (boolean) (not nosource(proc)); | |
52646e6f | 936 | for (p = arglist; p != nil; p = p->value.arg[1]) { |
2fd0f574 SL |
937 | assert(p->op == O_COMMA); |
938 | actual = p->value.arg[0]; | |
939 | if (not chkparam(actual, formal, chk)) { | |
940 | fprintf(stderr, " in call to %s", symname(proc)); | |
52646e6f | 941 | sp = savesp; |
2fd0f574 | 942 | enderrmsg(); |
52646e6f | 943 | } |
2fd0f574 SL |
944 | passparam(actual, formal); |
945 | if (formal != nil) { | |
946 | formal = formal->chain; | |
52646e6f | 947 | } |
52646e6f ML |
948 | ++count; |
949 | } | |
2fd0f574 SL |
950 | if (chk) { |
951 | if (formal != nil) { | |
952 | sp = savesp; | |
953 | error("not enough parameters to %s", symname(proc)); | |
954 | } | |
52646e6f ML |
955 | } |
956 | return count; | |
6167b701 DS |
957 | } |
958 | ||
959 | /* | |
960 | * Evaluate an argument list without concern for matching the formal | |
961 | * parameters of a function in type or quantity. Useful for functions | |
962 | * like C's printf(). | |
963 | */ | |
964 | ||
965 | private integer unsafe_evalargs(proc, arglist) | |
966 | Symbol proc; | |
967 | Node arglist; | |
968 | { | |
969 | Node p; | |
970 | Integer count; | |
971 | ||
972 | count = 0; | |
973 | for (p = arglist; p != nil; p = p->value.arg[1]) { | |
974 | assert(p->op == O_COMMA); | |
975 | eval(p->value.arg[0]); | |
976 | ++count; | |
977 | } | |
978 | return count; | |
52646e6f ML |
979 | } |
980 | ||
981 | public procreturn(f) | |
982 | Symbol f; | |
983 | { | |
0022c355 ML |
984 | integer retvalsize; |
985 | Node tmp; | |
986 | char *copy; | |
987 | ||
52646e6f | 988 | flushoutput(); |
52646e6f | 989 | popenv(); |
0022c355 ML |
990 | if (endproc.isfunc) { |
991 | retvalsize = size(f->type); | |
992 | if (retvalsize > sizeof(long)) { | |
993 | pushretval(retvalsize, true); | |
994 | copy = newarr(char, retvalsize); | |
995 | popn(retvalsize, copy); | |
996 | tmp = build(O_SCON, copy); | |
997 | } else { | |
998 | tmp = build(O_LCON, (long) (reg(0))); | |
999 | } | |
1000 | tmp->nodetype = f->type; | |
1001 | tfree(endproc.callnode); | |
1002 | *(endproc.callnode) = *(tmp); | |
1003 | dispose(tmp); | |
1004 | eval(endproc.cmdnode); | |
1005 | } else { | |
1006 | putchar('\n'); | |
1007 | printname(stdout, f); | |
b9ae3d87 | 1008 | printf("%s returns successfully\n", symname(f)); |
0022c355 | 1009 | } |
52646e6f ML |
1010 | erecover(); |
1011 | } | |
1012 | ||
1013 | /* | |
1014 | * Push the current environment. | |
1015 | */ | |
1016 | ||
1017 | private pushenv() | |
1018 | { | |
1019 | push(Address, pc); | |
1020 | push(Lineno, curline); | |
1021 | push(String, cursource); | |
1022 | push(Boolean, isstopped); | |
1023 | push(Symbol, curfunc); | |
2fd0f574 SL |
1024 | push(Frame, curframe); |
1025 | push(struct Frame, curframerec); | |
0022c355 | 1026 | push(CallEnv, endproc); |
52646e6f ML |
1027 | push(Word, reg(PROGCTR)); |
1028 | push(Word, reg(STKP)); | |
1029 | } | |
1030 | ||
1031 | /* | |
1032 | * Pop back to the real world. | |
1033 | */ | |
1034 | ||
1035 | public popenv() | |
1036 | { | |
0022c355 | 1037 | String filename; |
52646e6f ML |
1038 | |
1039 | setreg(STKP, pop(Word)); | |
1040 | setreg(PROGCTR, pop(Word)); | |
0022c355 | 1041 | endproc = pop(CallEnv); |
2fd0f574 SL |
1042 | curframerec = pop(struct Frame); |
1043 | curframe = pop(Frame); | |
52646e6f ML |
1044 | curfunc = pop(Symbol); |
1045 | isstopped = pop(Boolean); | |
1046 | filename = pop(String); | |
1047 | curline = pop(Lineno); | |
1048 | pc = pop(Address); | |
1049 | setsource(filename); | |
1050 | } | |
1051 | ||
1052 | /* | |
1053 | * Flush the debuggee's standard output. | |
1054 | * | |
1055 | * This is VERY dependent on the use of stdio. | |
1056 | */ | |
1057 | ||
1058 | public flushoutput() | |
1059 | { | |
0022c355 ML |
1060 | Symbol p, iob; |
1061 | Stack *savesp; | |
52646e6f ML |
1062 | |
1063 | p = lookup(identname("fflush", true)); | |
1064 | while (p != nil and not isblock(p)) { | |
1065 | p = p->next_sym; | |
1066 | } | |
1067 | if (p != nil) { | |
1068 | iob = lookup(identname("_iob", true)); | |
1069 | if (iob != nil) { | |
1070 | pushenv(); | |
1071 | pc = codeloc(p); | |
1072 | savesp = sp; | |
1073 | push(long, address(iob, nil) + sizeof(struct _iobuf)); | |
1074 | setreg(STKP, reg(STKP) - sizeof(long)); | |
1075 | dwrite(savesp, reg(STKP), sizeof(long)); | |
1076 | sp = savesp; | |
1077 | beginproc(p, 1); | |
1078 | stepto(return_addr()); | |
1079 | popenv(); | |
1080 | } | |
1081 | } | |
1082 | } |