[comp.unix.questions] Core files ... it works

warner@scubed.UUCP (Ken Warner) (06/30/88)

In article <537@philmds.UUCP> leo@philmds.UUCP (L.J.M. de Wit) writes:
>In article <796@scubed.UUCP> warner@scubed.UUCP (Ken Warner) writes:
>|In article <11954@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>||In article <790@scubed.UUCP> warner@scubed.UUCP (Ken Warner) writes:
>|||Is there a way to run a core file?
>||It cannot be done portably, but with certain restrictions, it can
>||almost always be done.
>|[stuff deleted]
>||Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris
>|Well, I've looked at unexec() from emacs and then tried to write my own 
[stuff deleted]
>You do not copy the BSS space nor any extension of the data space

Yup! That was the problem. I've appended a copy of the current state of
my effort below.  It works.  

>I'm still wondering why you use 1 char writes? Pretty expensive I think.

Yup again! It was only a prototype.  Not production quality. 

>And I also would like to know what use this scheme has? Is it for
[stuff deleted]
>    Leo.

Well, if one had a custom shell for doing a particular job that took a long
time, days perhaps, it would be nice to be able to stop and go home knowing
one could restart again where one had left off.  In many implementations, one 
can save a Lisp, Smalltalk or Emacs in a particular state.  I thought it would
be interesting to be able to save a C program, perhaps a custom shell, in 
a particular state.
 
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?

Below is a test program that shows my progress so far.  It is Sun 3, OS 3.4,5
specific.  I've no interest in making it portable right now.

It first copies the text from the object file, its own name is argv[0] in the 
test program.  Then it copies the data and bss segments from memory, sets the
size of the data segment (hdr.a_data) to reflect the increase in size of the 
data segment, then sets the bss size (hdr.a_bss) to 0.  

Executing the test program and then the resulting executable will give a small 
demonstration of saving the values of static and dynamic variables.  There is 
probably a bug lurking about somewhere; this isn't production quality code, 
but it is a start.

The next thing I want to do is to save the stack and registers.  They can also
be saved in the bss segment and restored when the program is restarted.

This should be some fun, eh?

--------------------------------------------------------------------------------

#include <stdio.h>
#include <a.out.h>
#include <stab.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

extern int end;
extern int edata;
extern int etext;
extern int first();
extern char *sbrk ();
static struct exec hdr;


first()
{
	printf("first\n");
	second();
	fflush(stdout);
}
second()
{
	printf("second\n");
	third();
	fflush(stdout);
}
third()
{
	printf("third\n");
	forth();
	fflush(stdout);
}
forth()
{
	printf("forth\n");
	fflush(stdout);
}

int foo;
char *string;
main(argc,argv)
int argc;
char *argv[];
{
	struct exec hdr;
	int a_out;
	if ((a_out = open (argv[0], O_RDONLY)) < 0)
	{
	    perror(argv[0]);
	}
	if (read (a_out, &hdr, sizeof(hdr)) != sizeof(hdr))
	{
	  perror(argv[0]);
	}
	close(a_out);
#ifdef DEBUG
	if(foo == 9)
		printf("foo was initialized to %d\n",foo);
	else
		printf("foo was initialized to %d\n",foo);

	printf("N_TXTADDR(hdr) = %x\n",N_TXTADDR(hdr));
	printf("N_DATADDR(hdr) = %x\n",N_DATADDR(hdr));
	printf("N_BSSADDR(hdr) = %x\n",N_BSSADDR(hdr));
	printf("sizeof(hdr) = %x\n",sizeof(hdr));
	printf("a_machtype = %d\n",hdr.a_machtype);
	printf("a_magic = %o\n",hdr.a_magic);
	printf("a_text = %x\n",hdr.a_text);
	printf("a_data = %x\n",hdr.a_data);
	printf("a_bss = %x\n",hdr.a_bss);
	printf("a_syms = %x\n",hdr.a_syms);
	printf("a_entry = %x\n",hdr.a_entry);
	printf("a_trsize = %x\n",hdr.a_trsize);
	printf("a_drsize = %x\n",hdr.a_drsize);
	printf("sbrk(0) = %x\n",sbrk(0));
#endif
	if(foo == 9)
		printf("foo = %d and string = %s\n",foo,string);
	else
	{
		if((string = (char *)malloc(80 * sizeof(char))) == NULL)
		{	
			perror("malloc");
			exit(-1);
		}
		strcpy(string,"this is a test");

	}
	foo = 9;
 	unexec(argv[1],argv[0]); 
	
	first();
	second();
	third();
	forth();
	exit(1);
}


/* ****************************************************************
 * unexec(new_name,a_name) where new_name is the name of the new executable
 * and a_name is the name of the file containing the currently executing
 * program
 *
 */
unexec (new_name, a_name)
char *new_name, *a_name;
{

    int new, a_out = -1;
    if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
    {
	perror (a_name);
	exit(-1);
    }
    if ((new = open (new_name, O_WRONLY|O_CREAT|O_TRUNC,0755)) < 0)
    {
	perror (new_name);
	exit(-1);
    }

    if (make_a_out(new, a_out,new_name,a_name) < 0)
    {
	close (new);
	unlink (new_name);	    	/* Failed, unlink new a.out */
	return -1;	
    }

	close (new);
	close (a_out);
	return 0;
}

/* ****************************************************************
 * make_a_out
 */
static int make_a_out (new, a_out,new_name,a_name)
int new, a_out;
char *new_name,*a_name;
{
	char buff[PAGSIZ];
	unsigned int i,numrd,pos,bss_end;

    bzero(buff,PAGSIZ);

    if (read (a_out, &hdr, sizeof(hdr)) != sizeof hdr)
    {
      perror (a_out);
      return(-1);
    }
    if (N_BADMAG (hdr))
    {
      printf("invalid magic number in %s", a_name);
      fflush(stdout);
      return(-1);
    }

/* rewind a.out */
    if(lseek (a_out,0,L_SET) == -1)
    {
	perror("lseek #1");
	return(-1);
    }

/*snarf and plop text from old a.out to new */
    for(i=0;i<hdr.a_text;i+=PAGSIZ)
    {
	bzero(buff,PAGSIZ);
	if (read (a_out,buff,PAGSIZ) != PAGSIZ)
	{
	  perror ("read #1");
	  return(-1);
	}
	if(write(new,buff,PAGSIZ) != PAGSIZ)
	{
		perror("write #1");
		return(-1);
	}
    }

/* now read data through bss from a.out into new data segment */
/* first make sure bss will be in the same place in new */
/* round bss up to next page boundry */

    if((bss_end = (unsigned)sbrk(0)) == -1)
    {
	perror("sbrk #1");
	return(-1);
    }

    if(brk(PAGSIZ + (bss_end & ~(PAGSIZ - 1))) == -1)
    {
	perror("brk");
	return(-1);
    }

    if((bss_end = (unsigned)sbrk(0)) == -1)
    {
	perror("sbrk #2");
	return(-1);
    }

    for(i=(int)(N_DATADDR(hdr));i<(int)bss_end;i+=PAGSIZ)
    {
	if((write(new,(char *)i,PAGSIZ)) != PAGSIZ)
	{
	    perror("write #2");
	    printf("i = %d\n",i);
	    fflush(stdout);
	    return(-1);
	}
    }

/* jump over data in a_out and align to page boundry in new */
    if(lseek(a_out,N_SYMOFF(hdr),L_SET) == -1)
    {
	perror("lseek #2");
	return(-1);
    }

/* read symbol and text string space */ 
    while(1)
    {
	bzero(buff,PAGSIZ);
	if ((numrd = read (a_out,buff,PAGSIZ)) != PAGSIZ)
	{
	    printf("Last page read. \n");
	    if((numrd > 0) && (write(new,buff,numrd) == -1))
	    {
		perror("write #3");
		return(-1);
	    }
	    break;
	}
	if(write(new,buff,PAGSIZ) != PAGSIZ)
	{
	    perror("write #4");
	    return(-1);
	}
    }

/*rewind to begining of file */
    if(lseek (new,0,L_SET) == -1)
    {
	perror("lseek #3");
	return(-1);
    }

/* make size of data in header to include the bss space */
    hdr.a_data = (int)bss_end - (int)N_DATADDR(hdr);
    hdr.a_bss = 0;

/*write out the header if you changed anything */
    if (write (new, &hdr, sizeof hdr) != sizeof hdr)
    {
	perror ("write #5");
	return(-1);
    }

    return(0);

}

lm@arizona.edu (Larry McVoy) (06/30/88)

In article <797@scubed.UUCP> warner@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@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@sun.com	1-800-LAI-UNIX x286

libes@cme-durer.ARPA (Don Libes) (07/04/88)

In article <6053@megaron.arizona.edu> laidbak!lm@sun.com writes:
>In article <797@scubed.UUCP> warner@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.  
>>
>>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...

Here's how to restore all the registers on a Sun.  This is extracted
from code I wrote to get XINU running on top of UNIX.  This was
written up as part of a larger article on XINU in the July/August '87
;login:.  The code to save the registers is a little easier.  If you want
the whole XINU package, you can get anonymous ftp it from cme-durer.arpa.
It's called xinu.shar.Z.  Source is only 16Kb (compressed).

You can think of XINU as a thread library if you like.

static int *new_sp;		/* new stack pointer register to be loaded */
static int (*new_pc)();		/* new program counter register to be loaded */
static int new_signal_mask;	/* new signal mask to be loaded */

int restore()
{
	register struct pentry *nptr;	/* pointer to new process entry */

	.
	.  a lot of stuff omitted here 
	.

	/* at this point, nptr->pregs = a4@ */
	/* you may have to modify the following assembler if this is */
	/* not the case */

	/* restart the new process */

	/* prepare pc, sp and interrupt mask for rte() to use */
	new_sp = nptr->sp;	/* movl a4@(60),_new_sp */
	new_pc = nptr->pc;	/* movl a4@(64),_new_pc */
	new_signal_mask = nptr->signal_mask;
	/* load rest of registers directly except for a7 (sp) */
	asm("moveml	a4@,#0xfff");		/* d0-d7,a0-a3 */
	asm("movl	a4@(56),a6");		/* restore a6 */
	asm("movl	a4@(52),a5");		/* restore a5 */
	asm("movl	a4@(48),a4");		/* restore a4 */

	kill(getpid(),RTE);	/* this is caught by rte() below */
}

/* This routine is necessary to load the signal mask at the same time as */
/* we load the new pc and sp. */
/* ARGSUSED */
static void rte(sig,code,scp)
int sig;
int code;
struct sigcontext *scp;
{
	scp->sc_sp = (int)new_sp;
	scp->sc_pc = (int)new_pc;
	scp->sc_mask = new_signal_mask;

	/* No need to reload ps, as no one looks at it anyway, upon return. */
}

Don Libes          cme-durer.arpa      ...!uunet!cme-durer!libes

leo@philmds.UUCP (Leo de Wit) (07/04/88)

How about using setjmp() and longjmp()? Isn't that a - fairly - portable way
to get/restores the registers?

Something like:

     if (setjmp(&jpbf)) {
         /* save jmpbuf struct into data space of new executable */
     } else { /* */
     }

When running the executable, do a longjmp using the saved struct.
This can of course cause problems (longjmp complaining about an incorrect
stack) on systems where longjmp does such checkings (did Chris not mention
something like this in a recent posting?).

Anyway, it was just an idea...

   Leo.

chris@mimsy.UUCP (Chris Torek) (07/04/88)

In article <551@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>How about using setjmp() and longjmp()? Isn't that a - fairly - portable way
>to get/restores the registers?

Alas, no.  It may not save and restore *any* registers, especially
given the statement in the dpANS that local variables must be declared
`volatile' to get any guarantee from longjmp().
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris