/* Copyright (c) 1982 Regents of the University of California */
static char sccsid
[] = "@(#)runtime.c 1.13 (Berkeley) %G%";
static char rcsid
[] = "$Header: runtime.c,v 1.5 84/12/26 10:41:52 linton Exp $";
* 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)
#define isstackaddr(addr) \
(((addr) < 0x80000000) and ((addr) > 0x80000000 - 0x200 * UPAGES))
* Set a frame to the current activation record.
frp
->save_ap
= reg(ARGP
);
frp
->save_pc
= reg(PROGCTR
);
for (i
= 0; i
< NSAVEREG
; i
++) {
frp
->save_reg
[i
] = reg(i
);
* Get the saved registers from one frame to another
* given mask specifying which registers were actually saved.
#define bis(b, n) ((b & (1 << (n))) != 0)
private getsaveregs (newfrp
, frp
, mask
)
for (i
= 0; i
< NSAVEREG
; i
++) {
newfrp
->save_reg
[i
] = frp
->save_reg
[j
];
* 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.
private Frame
nextframe(frp
)
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
or frame
.save_pc
== (Address
) -1) {
} else if (isstackaddr(callpc
)) {
prev_frame
= frame
.save_fp
;
mask
= ((frame
.mask
>> 16) & 0x0fff);
getsaveregs(newfrp
, &frame
, mask
);
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
public 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
;
if (f
== curfunc
and curframe
!= nil
) {
p
= whatblock(frp
->save_pc
);
} else if (p
== program
) {
* Set the registers according to the given frame pointer.
dread(&frame
, addr
, sizeof(frame
));
setreg(ARGP
, frame
.save_ap
);
setreg(FRP
, frame
.save_fp
);
setreg(PROGCTR
, frame
.save_pc
);
mask
= ((frame
.mask
>> 16) & 0x0fff);
for (i
= 0; i
< NSAVEREG
; i
++) {
setreg(i
, frame
.save_reg
[j
]);
setcurfunc(whatblock(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
)) {
error("[internal error: bad size %d in pushretval]", len
);
* 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
));
* Print a list of currently active blocks starting with most recent.
* Print the variables in the given frame or the current one if nil.
* Walk the stack of active procedures printing information
* about each active procedure.
private walkstack(dumpvariables
)
if (notstarted(process
) or isfinished(process
)) {
error("program is not active");
showaggrs
= dumpvariables
;
if (frp
== nil
or f
== program
) {
printf("in \"%s\":\n", symname(program
));
* Print out the information about a call, i.e.,
* routine name, parameter values, and source location.
private printcallinfo (f
, frp
)
if (frp
->save_fp
!= reg(FRP
)) {
printf(", line %d", line
);
printf(" in \"%s\"\n", srcfilename(savepc
));
printf(" at 0x%x\n", savepc
);
* 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.
* Return the frame for the current function.
* The space for the frame is allocated statically.
public Frame
curfuncframe ()
static struct Frame frame
;
frp
= findframe(curfunc
);
* 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");
printcallinfo(curfunc
, curframe
);
} 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");
curfrp
= 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
++) {
printcallinfo(curfunc
, curframe
);
* 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(exprnode
, isfunc
)
procnode
= exprnode
->value
.arg
[0];
arglist
= exprnode
->value
.arg
[1];
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
));
endproc
.callnode
= exprnode
;
endproc
.cmdnode
= topnode
;
argc
= pushargs(proc
, arglist
);
build(O_EQ
, build(O_SYM
, pcsym
), build(O_SYM
, retaddrsym
)),
buildcmdlist(build(O_PROCRTN
, proc
))
* bpact() won't return true, it will call printstatus() and go back
* to command input if a breakpoint is found.
* 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
or actual
->nodetype
== t_addr
or
actual
->op
== O_TYPERENAME
and
actual
->value
.arg
[0]->op
== O_RVAL
or
actual
->value
.arg
[0]->nodetype
== t_addr
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
* Another special case raised by strings is the possibility that
* the actual parameter will be larger than the formal, even with
* appropriate type-checking. This occurs because we assume during
* evaluation that strings are null-terminated, whereas some languages,
* notably Pascal, do not work under that assumption.
private passparam (actual
, formal
)
integer actsize
, formsize
;
if (formal
!= nil
and isvarparam(formal
) and
(not isopenarray(formal
->type
))
addr
= lval(actual
->value
.arg
[0]);
} else if (passaddr(formal
, actual
->nodetype
)) {
reg(STKP
) - ((actsize
+ sizeof(Word
) - 1) & ~(sizeof(Word
) - 1))
dwrite(savesp
, reg(STKP
), actsize
);
push(Address
, reg(STKP
));
if (formal
!= nil
and isopenarray(formal
->type
)) {
push(integer
, actsize div
size(formal
->type
->type
));
} else if (formal
!= nil
) {
if (actsize
> formsize
) {
sp
-= (actsize
- formsize
);
* 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
));
retvalsize
= size(f
->type
);
if (retvalsize
> sizeof(long)) {
pushretval(retvalsize
, true);
copy
= newarr(char, retvalsize
);
tmp
= build(O_SCON
, copy
);
tmp
= build(O_LCON
, (long) (reg(0)));
*(endproc
.callnode
) = *(tmp
);
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.
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));