/* Copyright (c) 1982 Regents of the University of California */
static char sccsid
[] = "@(#)runtime.c 1.11 (Berkeley) %G%";
* Runtime organization dependent routines, mostly dealing with
typedef struct Frame
*Frame
;
Integer condition_handler
;
Address save_ap
; /* argument pointer */
Address save_fp
; /* frame pointer */
Address save_pc
; /* program counter */
Word save_reg
[NSAVEREG
]; /* not necessarily there */
private Frame curframe
= nil
;
private struct Frame curframerec
;
private Boolean walkingstack
= false;
#define frameeq(f1, f2) ((f1)->save_fp == (f2)->save_fp)
* Set a frame to the current activation record.
frp
->save_ap
= reg(ARGP
);
frp
->save_pc
= reg(PROGCTR
) + 1;
for (i
= 0; i
< NSAVEREG
; i
++) {
frp
->save_reg
[i
] = reg(i
);
* Return a pointer to the next activation record up the stack.
* Return nil if there is none.
* Writes over space pointed to by given argument.
#define bis(b, n) ((b & (1 << (n))) != 0)
private Frame
nextframe(frp
)
register Integer i
, j
, mask
;
Address prev_frame
, callpc
;
static Integer ntramp
= 0;
prev_frame
= frp
->save_fp
;
* The check for interrupt generated frames is taken from adb with only
* partial understanding. If you're in "sub" and on a sigxxx "sigsub"
* gets control, then the stack does NOT look like <main, sub, sigsub>.
* As best I can make out it looks like:
* <main, (machine check exception block + sub), sysframe, sigsub>.
* When the signal occurs an exception block and a frame for the routine
* in which it occured are pushed on the user stack. Then another frame
* is pushed corresponding to a call from the kernel to sigsub.
* The addr in sub at which the exception occured is not in sub.save_pc
* but in the machine check exception block. It is at the magic address
* The current approach ignores the sys_frame (what adb reports as sigtramp)
* and takes the pc for sub from the exception block. This allows the
* "where" command to report <main, sub, sigsub>, which seems reasonable.
dread(&frame
, prev_frame
, sizeof(struct Frame
));
dread(&callpc
, prev_frame
+ 84, sizeof(callpc
));
if (frame
.save_fp
== nil
) {
} else if (callpc
> 0x80000000 - 0x200 * UPAGES
) {
prev_frame
= frame
.save_fp
;
mask
= ((frame
.mask
>> 16) & 0x0fff);
for (i
= 0; i
< NSAVEREG
; i
++) {
newfrp
->save_reg
[i
] = frame
.save_reg
[j
];
newfrp
->condition_handler
= frame
.condition_handler
;
newfrp
->save_ap
= frame
.save_ap
;
newfrp
->save_fp
= frame
.save_fp
;
newfrp
->save_pc
= frame
.save_pc
;
* Get the current frame information in the given Frame and store the
* associated function in the given value-result parameter.
private getcurfunc (frp
, fp
)
*fp
= whatblock(frp
->save_pc
);
* Return the frame associated with the next function up the call stack, or
* nil if there is none. The function is returned in a value-result parameter.
* For "inline" functions the statically outer function and same frame
private Frame
nextfunc (frp
, fp
)
t
= whatblock(nfrp
->save_pc
);
* Return the frame associated with the given function.
* If the function is nil, return the most recently activated frame.
* Static allocation for the frame.
public Frame
findframe(f
)
static struct Frame frame
;
* Starting at the current stack frame,
* walk backwards looking for a symbol
* match. Beware of local blocks which
* have a back pointer but no stack frame.
p
= whatblock(frp
->save_pc
);
p
= whatblock(frp
->save_pc
);
* Find the return address of the current procedure/function.
public Address
return_addr()
* Push the value associated with the current function.
public pushretval(len
, isindirect
)
rpush((Address
) r0
, len
);
if (len
== sizeof(Word
)) {
} else if (len
== 2*sizeof(Word
)) {
panic("not indirect in pushretval?");
* Return the base address for locals in the given frame.
public Address
locals_base(frp
)
return (frp
== nil
) ? reg(FRP
) : frp
->save_fp
;
* Return the base address for arguments in the given frame.
public Address
args_base(frp
)
return (frp
== nil
) ? reg(ARGP
) : frp
->save_ap
;
* Return saved register n from the given frame.
public Word
savereg(n
, frp
)
assert(n
>= 0 and n
< NSAVEREG
);
* Return the nth argument to the current procedure.
dread(&w
, args_base(frp
) + (n
* sizeof(Word
)), sizeof(w
));
* Calculate the entry address for a procedure or function parameter,
* given the address of the descriptor.
public Address
fparamaddr(a
)
* Print a list of currently active blocks starting with most recent.
* Dump the world to the given file.
* Like "where", but variables are dumped also.
* Walk the stack of active procedures printing information
* about each active procedure.
private walkstack(dumpvariables
)
if (notstarted(process
)) {
error("program is not active");
printf("%s", symname(f
));
line
= srcline(frp
->save_pc
- 1);
printf(", line %d", line
);
printf(" in \"%s\"\n", srcfilename(frp
->save_pc
- 1));
printf(" at 0x%x\n", frp
->save_pc
);
} while (frp
!= nil
and f
!= program
);
printf("in \"%s\":\n", symname(program
));
* Set the current function to the given symbol.
* We must adjust "curframe" so that subsequent operations are
* not confused; for simplicity we simply clear it.
* Set curfunc to be N up/down the stack from its current value.
if (not isactive(program
)) {
error("program is not active");
} else if (curfunc
== nil
) {
error("no current function");
error("not that many levels");
} else if (f
== program
) {
error("not that many levels");
if (not isactive(program
)) {
error("program is not active");
} else if (curfunc
== nil
) {
error("no current function");
*curframe
= *(findframe(curfunc
));
while ((f
!= curfunc
or !frameeq(frp
, curframe
)) and f
!= nil
) {
if (f
== nil
or n
> depth
) {
error("not that many levels");
for (i
= 0; i
< depth
; i
++) {
* Find the entry point of a procedure or function.
f
->symvalue
.funcv
.beginaddr
+= 15;
f
->symvalue
.funcv
.beginaddr
+= 2;
* Return the address corresponding to the first line in a function.
public Address
firstline(f
)
while (linelookup(addr
) == 0 and addr
< objsize
) {
* Catcher drops strike three ...
while (linelookup(addr
) == 0 and addr
< objsize
) {
* Return the address corresponding to the end of the program.
* We look for the entry to "exit".
public Address
lastaddr()
s
= lookup(identname("exit", true));
panic("can't find exit");
* Decide if the given function is currently active.
* We avoid calls to "findframe" during a stack trace for efficiency.
* Presumably information evaluated while walking the stack is active.
public Boolean
isactive(f
)
if (isfinished(process
)) {
if (walkingstack
or f
== program
or
(ismodule(f
) and isactive(container(f
)))) {
b
= (Boolean
) (findframe(f
) != nil
);
* Evaluate a call to a procedure.
public callproc(procnode
, arglist
)
if (procnode
->op
!= O_SYM
) {
fprintf(stderr
, "can't call \"");
prtree(stderr
, procnode
);
assert(procnode
->op
== O_SYM
);
proc
= procnode
->value
.sym
;
error("\"%s\" is not a procedure or function", symname(proc
));
argc
= pushargs(proc
, arglist
);
event_once(build(O_EQ
, build(O_SYM
, pcsym
), build(O_SYM
, retaddrsym
)),
buildcmdlist(build(O_PROCRTN
, proc
)));
* Push the arguments on the process' stack. We do this by first
* evaluating them on the "eval" stack, then copying into the process'
private Integer
pushargs(proc
, arglist
)
argc
= evalargs(proc
, arglist
);
setreg(STKP
, reg(STKP
) - args_size
);
dwrite(savesp
, reg(STKP
), args_size
);
* Check to see if an expression is correct for a given parameter.
* If the given parameter is false, don't worry about type inconsistencies.
* Return whether or not it is ok.
private boolean
chkparam (actual
, formal
, chk
)
fprintf(stderr
, "too many parameters");
} else if (not compatible(formal
->type
, actual
->nodetype
)) {
fprintf(stderr
, "type mismatch for %s", symname(formal
));
if (b
and formal
!= nil
and isvarparam(formal
) and
not isopenarray(formal
->type
) and actual
->op
!= O_RVAL
)
fprintf(stderr
, "expected variable, found \"");
* Pass an expression to a particular parameter.
* Normally we pass either the address or value, but in some cases
* (such as C strings) we want to copy the value onto the stack and
private passparam (actual
, formal
)
if (isvarparam(formal
) and not isopenarray(formal
->type
)) {
addr
= lval(actual
->value
.arg
[0]);
} else if (passaddr(formal
, actual
->nodetype
)) {
setreg(STKP
, reg(STKP
) - paramsize
);
dwrite(savesp
, reg(STKP
), paramsize
);
push(Address
, reg(STKP
));
if (formal
!= nil
and isopenarray(formal
->type
)) {
push(integer
, paramsize div
size(formal
->type
->type
));
* Evaluate an argument list left-to-right.
private Integer
evalargs(proc
, arglist
)
chk
= (boolean
) (not nosource(proc
));
for (p
= arglist
; p
!= nil
; p
= p
->value
.arg
[1]) {
assert(p
->op
== O_COMMA
);
actual
= p
->value
.arg
[0];
if (not chkparam(actual
, formal
, chk
)) {
fprintf(stderr
, " in call to %s", symname(proc
));
passparam(actual
, formal
);
error("not enough parameters to %s", symname(proc
));
printf(" returns successfully\n", symname(f
));
* Push the current environment.
push(Boolean
, isstopped
);
push(struct Frame
, curframerec
);
push(Word
, reg(PROGCTR
));
* Pop back to the real world.
register String filename
;
setreg(PROGCTR
, pop(Word
));
curframerec
= pop(struct Frame
);
isstopped
= pop(Boolean
);
* Flush the debuggee's standard output.
* This is VERY dependent on the use of stdio.
p
= lookup(identname("fflush", true));
while (p
!= nil
and not isblock(p
)) {
iob
= lookup(identname("_iob", true));
push(long, address(iob
, nil
) + sizeof(struct _iobuf
));
setreg(STKP
, reg(STKP
) - sizeof(long));
dwrite(savesp
, reg(STKP
), sizeof(long));