Core files ... it works

Ken Warner warner at scubed.UUCP
Thu Jun 30 03:55:59 AEST 1988


In article <537 at philmds.UUCP> leo at philmds.UUCP (L.J.M. de Wit) writes:
>In article <796 at scubed.UUCP> warner at scubed.UUCP (Ken Warner) writes:
>|In article <11954 at mimsy.UUCP> chris at mimsy.UUCP (Chris Torek) writes:
>||In article <790 at scubed.UUCP> warner at 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 at 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);

}



More information about the Comp.unix.questions mailing list