Core files ... it works
Larry McVoy
lm at arizona.edu
Thu Jun 30 08:28:00 AEST 1988
In article <797 at scubed.UUCP> warner at scubed.UUCP (Ken Warner) writes:
>The next thing I want to do is save the stack and registers at a particular,
>known state and restore the stack and registers on start-up. I know this will
>take some start-up code.
>
>My questions:
>
>Where is the top of the stack, I know it starts at some high-memory location
>and grows down. How and where can a program find out the bounds of the stack.
>
>How can one get the contents of the registers, including the pc, fp and sp
>registers?
>
>How can these be restored?
I had to do this for a thread library that I wrote for the Vax (Mt Xinu, 4.3).
I've attached the code below. You should be able to see how to do what you want
from this code (on a vax). If you do get a Sun version working I'd like a
copy...
Basically, you call the startup routine from main, Tfork() duplicates the
current stack (all the way back to the top return address which is exit(), I
believe), and Tswitch() saves state (regs), and starts you up on the new
stack. This library has been used a little, I'd be happy to give the whole
thing to anyone that wants it (contact me at laidbak!lm at sun.com - this
account will be dead soon).
# This is a shell archive. Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file". (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# README Tasm.S Tfork.c Tstack.c Tstartup.c Tswitch.c
echo x - README
cat > "README" << '//E*O*F README//'
TODO:
The memory management is screwed. I can't use malloc because malloc
imbeds info into free blocks and I can overrun a stack into the free
stuff (causes confusing core dumps in malloc). I'm currently statically
allocating but that is gross because it uses tons of mem. I really need
kernel supported stacks (plural).
Rework the run q? The doubly linked list shit is error prone.
Preemptive scheduling? Could use itimer but user level shit uses that.
FILES
-----
tests.d
a bunch of sample tests live here
T.h
definitions of proc structs and globals
Tasm.S
startup, switch, and fork live here
Texit.c
code for doing an exit. Surprisingly complex; I needed to be able
to tell which process runs next, hence Trunnext. I needed to have
a place to store the previous info (for switch) hence junk (I think).
In short, exit has its grubby paws all over the place.
Tf77.c
Stubs to make fortran work. Not well tested.
Tfork.c
Actually Tprefork. This is the C code called from the asm version
of fork. It does all the non-assembler stuff, like mucking about
with proc structs, etc.
Tgetpid.c
thread library process id.
Tpanic.c
error message and quit.
Tps.c
debugging. Defines Tps() and Trunq().
Tspace.c
definitions for globals.
Tstack.c
rethreading of the copied stack. called on each fork. gross code,
but it works.
Tstartup.c
actually Tstartup2. called from asm version to initialize the first
proc structure.
Tswitch.c
actually Tpreswitch. called from asm version for C stuff.
Twait.c
an attempt (pathetic one, I admit) at wait(2). I need this since I can't
let processes die until someone out there wants them to. I really out
to have a sigchild.
//E*O*F README//
echo x - Tasm.S
cat > "Tasm.S" << '//E*O*F Tasm.S//'
/*
* copyright (c) 1988 by Larry McVoy
*/
/*
* Tstartup
* find the bottom of the stack, save it's location;
* the best way to find the location of the beginning of
* the stack is to
* get main's ap
* dereference to get # of args
* add that # + 1 to get top
* must be called from **main** before any Tfork calls.
* Calls Tinit() for process table setup, etc.
*/
.data
.align 1
.comm _Tchildid,4
.text
.globl _Tstartup
.globl _Tnext
.globl _tnext_ /* for f77 */
.globl _Tfork
.globl _tfork_ /* for f77 */
.globl _Tswitch
.globl _tswitch_ /* for f77 */
.align 1
_Tstartup:
.word 0
moval _Tproc,r0 /* initialize state for proc 0 */
movl fp,(r0)+ /* save fp in Tproc.p_sp */
movl $0,(r0) /* true switch, not a fork */
movl 8(fp),r0 /* get main's ap */
movl $1,r1 /* 1 word for the N value */
addl2 (r0),r1 /* get offset past args in r1 */
mull2 $4,r1 /* get byte offset */
addl2 r0,r1 /* r1 has address of beginning */
pushl 12(fp) /* save old fp */
pushl r1 /* and stack bottom */
calls $2,_Tinit /* call Tinit(stkbottom, firstframe) */
ret
/*
* Tfork
* Save state in the parent's proc struct,
* pass info to Tstack so it can do its' thing,
*/
.align 1
_tfork_: /* for f77 */
_Tfork:
.word 0xfc0
/*
* set things up for the fork. Tprefork will point Tprevproc at child.
* Note that Tprefork is called from here because we don't want
* two call frames on the stack (which would happen if the fork
* called us instead of this way).
*/
calls $0,_Tprefork
movl r0,_Tchildid
/*
* allocate and rethread new stack
*/
pushl fp
calls $1,_Tstack
/*
* save state for the child
*/
movl _Tprevproc,r1
movl r0,(r1)+ /* store fp returned by Tstack */
movl $0,(r1) /* child is not forking */
movl _Tchildid,r0 /* child's Tid for the parent */
ret
/*
* Tswitch - the assembler stuff to make the switch work.
*
* save state and switch stacks.
*/
.align 1
_Tnext:
_tnext_: /* for f77 */
.word 0xfc0
pushl $-1 /* T_RNDROBIN */
jbr callpre
_tswitch_: /* for f77 */
.word 0xfc0
pushl *4(ap)
jbr callpre
_Tswitch:
.word 0xfc0
/*
* set things up for the switch.
* As above, we do most of the work in C, relying on Tpreswitch to
* give us the current process in Tprevproc and the next one in
* Tcurproc. Scheduling and all associated muck is done in Tpreswitch.
*/
pushl 4(ap)
callpre:
calls $1,_Tpreswitch
/*
* save state; note that the .word 0xfc0 saved the registers used by C.
*/
movl _Tprevproc,r0
movl fp,(r0)+
movl $0,(r0) /* true switch, not a fork */
/*
* restore state.
*/
movl _Tcurproc,r0
movl (r0)+,fp /* set up stack */
movl (r0),r0 /* return value; might be a pid */
ret
//E*O*F Tasm.S//
echo x - Tfork.c
cat > "Tfork.c" << '//E*O*F Tfork.c//'
static char* copyright = "copyright (c) 1988 by Larry McVoy";
# include "T.h"
/*
* Tfork() - create a new thread of control
*
* part of the job is to add the new thread to the end of the runq.
* the current method leaves the parent at the front; unfair.
*/
Tprefork()
{
register i;
static last = 0;
register proc* p;
register proc* lastp;
/*
* search the whole process table for a slot.
*/
for (i=0; i<T_MAXPROC; ++i) {
last = (last + 1) % T_MAXPROC;
if (Tproc[last].p_pid == T_UNUSED) {
p = &Tproc[last];
goto gotone;
}
}
Tpanic("T: can't fork, out of process table space.");
gotone:
/*
* patch up the runq:
* before: 1->2->3->(back to 1)
* after: 1->2->3->4->(back to 1)
* (imagine the backpointers, it's too hard to draw)
*/
if (Trunning == 1) {
Tcurproc->p_next = Tcurproc->p_prev = p;
p->p_next = p->p_prev = Tcurproc;
}
else {
p->p_next = Tcurproc;
p->p_prev = lastp = Tcurproc->p_prev;
lastp->p_next = Tcurproc->p_prev = p;
}
Tprevproc = p;
p->p_pid = last;
Trunning++;
debug((stderr, "Tfork() threads=%u prev=%u cur=%u\n",
Trunning, Tprevproc->p_pid, Tcurproc->p_pid));
debugTps();
return last;
}
//E*O*F Tfork.c//
echo x - Tstack.c
cat > "Tstack.c" << '//E*O*F Tstack.c//'
static char* copyright = "copyright (c) 1988 by Larry McVoy";
# include "T.h"
/*
* Tstack - set up the new stack
*
* check for enough space after copying the current stack,
* copy the stack,
* rethread the frame and arg pointers,
* return the location of the frame pointer to the new stack.
*
* Note: this code assumes that stacks grow down (also vaxdeps?)
*
* This is worth a little doc. The stack frame looks like
*
* main [ arg n ]
* [ .... ]
* ap-> [ # of args ]
* [ regs ]
* [ .... ]
* [ old pc ]
* [ old fp ]
* [ old ap ]
* [ junk ]
* fp-> [ 0 ]
* sub1 [ arg n ]
* [ .... ]
*
* I have the fp/ap pointing at main's; that's not true. What I really
* have is p_stkbgn, which points just above main, and p_1stfrm, which
* points where I have fp drawn.
*
* "mysize" is represents the stack size from "here" to the start of
* the stack. The arg "fp" is the *current* frame pointer, so I can
* get mysize from p_stkbgn and fp.
*
* Setting p_1stfrm is a little weird (it's done by hand for the first
* stack in Tinit()). What I do is use the offset from the current
* versions to calculate the new versions (since p_stkbgn is easy).
*
* In rethread, I work back up the stack until I hit p_1stfrm, which is
* the only frame I *don't* have to rethread (it points to exit()).
*/
int*
Tstack(fp)
register char* fp;
{
char* malloc();
register mysize;
register char* newstk;
register char* newfp;
register proc* kid = Tprevproc;
register proc* cur = Tcurproc;
mysize = (char*)cur->p_stkbgn - fp;
newstk = Tstacks[kid->p_pid];
newfp = newstk + T_STK_SIZE - mysize;
debug((stderr, "Tstack mysize=%u fp=%#x newstk=%#x\n",
mysize, fp, newstk));
kid->p_stack = newstk;
kid->p_stkbgn = (int*)(newstk + T_STK_SIZE);
kid->p_1stfrm = kid->p_stkbgn - (cur->p_stkbgn - cur->p_1stfrm);
bcopy(T_SENTINEL, newstk, sizeof(T_SENTINEL));
bcopy(fp, newfp, mysize);
rethread(fp, newfp, cur->p_1stfrm);
return (int*)newfp;
}
static
rethread(fp, newfp, firstframe)
register int* fp;
register int* newfp;
register int* firstframe;
{
register offset;
debug((stderr, "rethread: fp=%#x nfp=%#x quit=%#x\n",
fp, newfp, firstframe));
while (fp < firstframe) {
fp += 3;
newfp += 3;
offset = (int*)*fp - fp;
debug((stderr,
"rethread: fp=%#x oap=%#x shit=%#x ofp=%#x offset=%#x words\n",
fp, *(newfp-1), *(newfp-2), *newfp, offset));
*newfp = (int)(newfp + offset);
--fp, --newfp;
offset = (int*)*fp - fp;
*newfp = (int)(newfp + offset);
fp = (int*)*(fp+1);
newfp = (int*)*(newfp+1);
}
}
//E*O*F Tstack.c//
echo x - Tstartup.c
cat > "Tstartup.c" << '//E*O*F Tstartup.c//'
static char* copyright = "copyright (c) 1988 by Larry McVoy";
# include "T.h"
/*
* Tinit - fill out the first process table entry,
* general initialization,
* called from Tstartup().
*/
Tinit(stkbottom, firstframe)
int* stkbottom;
int* firstframe;
{
register i;
register proc* p = Tproc;
struct rlimit rl;
debug((stderr, "Tinit()\n"));
# ifdef T_DEBUG
setbuf(stdout, 0);
# endif
for (i=1; i<T_MAXPROC; ++i)
Tproc[i].p_pid = T_UNUSED;
p->p_pid = 0;
p->p_stack = 0; /* special thread, needs no stack check */
p->p_next = p->p_prev = p;
p->p_ran = 1;
p->p_stkbgn = stkbottom;
p->p_1stfrm = firstframe;
Trunning = 1;
Tmax = T_MAXPROC;
Tcurproc = p;
debugTps();
}
//E*O*F Tstartup.c//
echo x - Tswitch.c
cat > "Tswitch.c" << '//E*O*F Tswitch.c//'
static char* copyright = "copyright (c) 1988 by Larry McVoy";
# include "T.h"
/*
* Tpreswitch - release control
*
* if entered with a valid thread id then run that thread
*/
Tpreswitch(Tid)
{
register proc* p;
static proc junk;
debug((stderr, "Tswitch(%d)\n", Tid));
/*
* check the stack sentinal.
* The first process uses the unix stack; needs no check.
*/
if (Tcurproc->p_stack) {
if (bcmp(Tcurproc->p_stack, T_SENTINEL, sizeof(T_SENTINEL)))
Tpanic("stack sentinal overwritten");
}
/*
* Grab the previous proc while we know where it is.
* This is a bit strange because we can be switching or
* returning control from an exiting thread. If it's an
* exit then we don't want to save state. Tpid tells us which.
*/
if (Tpid == Tcurproc->p_pid) /* true switch */
Tprevproc = Tcurproc;
else
Tprevproc = &junk; /* switching from exit() */
/*
* figure out who's next.
*/
Trunnext = -1;
if (Tid == T_RNDROBIN)
rndrobin:
Tcurproc = p = Tcurproc->p_next;
else {
if ((p = &Tproc[Tid])->p_pid == Tid)
Tcurproc = p;
else {
char buf[100];
sprintf(buf, "Tswitch(%d): thread not on run q", Tid);
Terror(buf);
goto rndrobin;
}
}
# ifdef T_DEBUG
debug((stderr, "Tpreswitch: pid=%u next=%u fp=%#x prev=%u cur=%u\n",
Tpid, p->p_pid, p->p_sp, Tprevproc->p_pid, Tcurproc->p_pid));
{ register int* fp = p->p_sp;
fprintf(stderr, "frame=%#x %#x %#x %#x %#x\n",
*fp, *(fp+1), *(fp+2), *(fp+3), *(fp+4));
}
# endif
Tpid = p->p_pid;
p->p_ran++;
return;
}
//E*O*F Tswitch.c//
echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
51 277 1787 README
112 497 2695 Tasm.S
51 188 1213 Tfork.c
95 434 2654 Tstack.c
33 104 739 Tstartup.c
60 230 1576 Tswitch.c
402 1730 10664 total
!!!
wc README Tasm.S Tfork.c Tstack.c Tstartup.c Tswitch.c | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
--
Larry McVoy laidbak!lm at sun.com 1-800-LAI-UNIX x286
More information about the Comp.unix.questions
mailing list