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