[net.sources] GNU Emacs unexec

bruce@stride.UUCP (Bruce Robertson) (10/23/85)

This should run under any System-V - just defined USG to get the System-V
code.  Let me know if you have any problems, please.


	Bruce Robertson
	UUCP: cbosgd!utah-cs!utah-gr!stride!bruce


--------- cut here -------------
/* 
 * unexec.c - Convert a running program into an a.out file.
 * 
 * Author:	Spencer W. Thomas
 * 		Computer Science Dept.
 * 		University of Utah
 * Date:	Tue Mar  2 1982
 * Copyright (c) 1982 Spencer W. Thomas
 *
 * Synopsis:
 *	unexec (new_name, a_name, data_start, bss_start, entry_address)
 *	char *new_name, *a_name;
 *	unsigned data_start, bss_start, entry_address;
 *
 * Takes a snapshot of the program and makes an a.out format file in the
 * file named by the string argument new_name.
 * If a_name is non-NULL, the symbol table will be taken from the given file.
 * 
 * The boundaries within the a.out file may be adjusted with the data_start 
 * and bss_start arguments.  Either or both may be given as 0 for defaults.
 * 
 * Data_start gives the boundary between the text segment and the data
 * segment of the program.  The text segment can contain shared, read-only
 * program code and literal data, while the data segment is always unshared
 * and unprotected.  Data_start gives the lowest unprotected address.  Since
 * the granularity of write-protection is on 1k page boundaries on the VAX, a
 * given data_start value which is not on a page boundary is rounded down to
 * the beginning of the page it is on.  The default when 0 is given leaves the
 * number of protected pages the same as it was before.
 * 
 * Bss_start indicates how much of the data segment is to be saved in the
 * a.out file and restored when the program is executed.  It gives the lowest
 * unsaved address, and is rounded up to a page boundary.  The default when 0
 * is given assumes that the entire data segment is to be stored, including
 * the previous data and bss as well as any additional storage allocated with
 * break (2).
 *
 * The new file is set up to start at entry_address.
 *
 * If you make improvements I'd like to get them too.
 * harpo!utah-cs!thomas, thomas@Utah-20
 *
 */

#ifndef emacs
#define PERROR perror
#define ERROR fprintf
#define ERRORF stderr,
#else
#include "config.h"
#define PERROR(file) error ("Failure operating on %s", file)
#define ERROR error
#define ERRORF
#endif

#if defined(USG) || defined(STRIDE)
#include <sys/types.h>
#endif
#include <sys/param.h>
#include <stdio.h>
/* #include <sys/dir.h> */
#include <sys/stat.h>
#include <a.out.h>
#include <errno.h>

#if defined(USG) || defined(STRIDE)

static struct filehdr filehdr;
static struct filehdr old;
static struct aouthdr aouthdr;
static struct scnhdr text, data, bss;

static int pagemask;
extern char etext[];


/* ****************************************************************
 * unexec
 *
 * driving logic.
 */
unexec (new_name, a_name, data_start, bss_start, entry_address)
     char *new_name, *a_name;
     unsigned data_start, bss_start, entry_address;
{
  int new, a_out = -1;

  if (a_name && (a_out = open( a_name, 0 )) < 0)
    {
      PERROR (a_name);
      return -1;
    }
  if ((new = creat (new_name, 0666)) < 0)
    {
      PERROR( new_name );
      return -1;
    }

  pagemask = MMU_PAGESIZE - 1;

  filehdr.f_magic = MC68MAGIC;
  filehdr.f_nscns = 3;		/* text, data and bss */
  time(&filehdr.f_timdat);
  filehdr.f_opthdr = sizeof aouthdr;
  filehdr.f_flags = F_RELFLG | F_EXEC | F_LNNO | F_AR32WR;

  if (make_hdr( new, a_out, data_start, bss_start, entry_address) < 0 ||
      copy_text_and_data( new ) < 0 ||
      copy_sym( new, a_out ) < 0)
    {
      close (new);
      /* unlink( new_name );	    	/* Failed, unlink new a.out */
      return -1;	
    }

  close (new);
  if (a_out >= 0)
    close (a_out);
  mark_x (new_name);
  return 0;
}

/* ****************************************************************
 * make_hdr
 *
 * Make the header in the new a.out from the header in core.
 * Modify the text and data sizes.
 */
static int
make_hdr( new, a_out, data_start, bss_start, entry_address)
int new, a_out;
unsigned data_start, bss_start, entry_address;
{
    /* Adjust data/bss boundary. */
    if ( bss_start != 0 )
    {
/*	bss_start = (bss_start + pagemask) & ~pagemask;	      /* (Up) to page bdry. */
	if ( bss_start > sbrk (0))
	{
	    ERROR( ERRORF
		"unexec: Specified bss_start( %u ) is past end of program.\n",
		bss_start );
	    return -1;
	}
    }
    else
      bss_start = sbrk (0);

    /* Adjust text/data boundary. */
    if (!data_start)
      data_start = (int) etext;
    data_start = data_start & ~pagemask; /* (Down) to page boundary. */

    if ( data_start > bss_start )	/* Can't have negative data size. */
    {
	ERROR( ERRORF
	    "unexec: data_start(%u) can't be greater than bss_start( %u ).\n",
	    data_start, bss_start );
	return -1;
    }

    aouthdr.magic = 0410;
    aouthdr.vstamp = 0;
    aouthdr.text_start = TEXT_START;
    aouthdr.data_start = data_start;
    aouthdr.entry = entry_address;
    aouthdr.bsize = sbrk (0) - bss_start;
    aouthdr.dsize = bss_start - data_start;
    aouthdr.tsize = data_start - TEXT_START;

    strcpy(text.s_name, _TEXT);
    text.s_paddr = text.s_vaddr = aouthdr.text_start;
    text.s_size = aouthdr.tsize;
    text.s_scnptr = sizeof filehdr + sizeof aouthdr + 3 * SCNHSZ;
    text.s_relptr = text.s_lnnoptr = text.s_nreloc = text.s_nlnno = 0;
    text.s_flags = STYP_REG | STYP_TEXT;

    strcpy(data.s_name, _DATA);
    data.s_paddr = data.s_vaddr = aouthdr.data_start;
    data.s_size = aouthdr.dsize;
    data.s_scnptr = text.s_scnptr + text.s_size;
    data.s_relptr = data.s_lnnoptr = data.s_nreloc = data.s_nlnno = 0;
    data.s_flags = STYP_REG | STYP_DATA;

    strcpy(bss.s_name, _BSS);
    bss.s_paddr = bss.s_vaddr = data.s_paddr + data.s_size;
    bss.s_size = aouthdr.bsize;
    bss.s_scnptr = 0;
    bss.s_relptr = bss.s_lnnoptr = bss.s_nreloc = bss.s_nlnno = 0;
    bss.s_flags = STYP_REG | STYP_BSS;

    /* Get symbol table info from header of a.out file if given one. */
    if ( a_out >= 0 )
    {
	if ( read( a_out, &old, sizeof old ) != sizeof old )
	{
	    PERROR( "Couldn't read header from a.out file" );
	    return -1;
	}

	if (old.f_magic != MC68MAGIC)
	{
	    ERROR( ERRORF "a.out file doesn't have legal magic number\n" );
	    return -1;
	}
	filehdr.f_nsyms = old.f_nsyms;
	filehdr.f_symptr = data.s_scnptr + data.s_size;
    }
    else
    {
	filehdr.f_nsyms = 0;		/* No a.out, so no symbol info. */
	filehdr.f_symptr = 0;
    }

    if (write(new, (char *) &filehdr, sizeof filehdr) != sizeof filehdr
     || write(new, (char *) &aouthdr, sizeof aouthdr) != sizeof aouthdr
     || write(new, (char *) &text, sizeof text) != sizeof text
     || write(new, (char *) &data, sizeof data) != sizeof data
     || write(new, (char *) &bss,  sizeof bss)  != sizeof bss)
    {
	PERROR( "Couldn't write header to new a.out file" );
	return -1;
    }

    return 0;
}

/* ****************************************************************
 * copy_text_and_data
 *
 * Copy the text and data segments from memory to the new a.out
 */
static int
copy_text_and_data( new )
int new;
{
    int nwrite, ret;
    int end;
    int i;
    int ptr;
    char buf[80];
    extern int errno;

    end = aouthdr.tsize + aouthdr.dsize;
    for (i = 0, ptr = TEXT_START; i < end;)
      {
	nwrite = 128;
	if (nwrite > end - i) nwrite = end - i;
	ret = write (new, ptr, nwrite);
	if (ret == -1 && errno == EFAULT)
	  {
	    lseek (new, text.s_scnptr + i + nwrite, 0);
	  }
	else if (nwrite != ret)
	  {
	    sprintf(buf, "Write failure in unexec: ptr 0x%x size 0x%x nwrite 0x%x errno %d",
			 ptr, nwrite, ret, errno);
	    PERROR(buf);
	    return -1;
	  }
	i += nwrite;
	ptr += nwrite;
      }

    return 0;
}

/* ****************************************************************
 * copy_sym
 *
 * Copy the relocation information and symbol table from the a.out to the new
 */
static int
copy_sym( new, a_out )
int new, a_out;
{
    char page[1024];
    int n;

    if ( a_out < 0 )
	return 0;

    lseek(a_out, old.f_symptr, 0);
    while ( (n = read( a_out, page, sizeof page )) > 0 )
    {
	if ( write( new, page, n ) != n )
	{
	    PERROR( "Error writing symbol table to new a.out" );
	    ERROR( ERRORF "new a.out should be ok otherwise\n" );
	    return 0;
	}
    }
    if ( n < 0 )
    {
	PERROR( "Error reading symbol table from a.out,\n" );
	ERROR( ERRORF "new a.out should be ok otherwise\n" );
    }
    return 0;
}

/* ****************************************************************
 * mark_x
 *
 * After succesfully building the new a.out, mark it executable
 */
static
mark_x( name )
char *name;
{
    struct stat sbuf;
    int um;

    um = umask( 777 );
    umask( um );
    if ( stat( name, &sbuf ) == -1 )
    {
	PERROR ( "Can't stat new a.out" );
	ERROR( ERRORF "Setting protection to %o\n", 0777 & ~um );
	sbuf.st_mode = 0777;
    }
    sbuf.st_mode |= 0111 & ~um;
    if ( chmod( name, sbuf.st_mode ) == -1 )
	PERROR( "Couldn't change mode of new a.out to executable" );

}

#else	/* USG || STRIDE */

extern char etext;

static struct exec hdr, ohdr;
static int pagemask;

/* ****************************************************************
 * unexec
 *
 * driving logic.
 */
unexec (new_name, a_name, data_start, bss_start, entry_address)
     char *new_name, *a_name;
     unsigned data_start, bss_start, entry_address;
{
  int new, a_out = -1;

  if (a_name && (a_out = open( a_name, 0 )) < 0)
    {
      PERROR (a_name);
      return -1;
    }
  if ((new = creat (new_name, 0666)) < 0)
    {
      PERROR( new_name );
      return -1;
    }

  pagemask = getpagesize () - 1;

  if (make_hdr( new, a_out, data_start, bss_start, entry_address) < 0 ||
      copy_text_and_data( new ) < 0 ||
      copy_sym( new, a_out ) < 0)
    {
      close (new);
      /* unlink( new_name );	    	/* Failed, unlink new a.out */
      return -1;	
    }

  close (new);
  if (a_out >= 0)
    close (a_out);
  mark_x (new_name);
  return 0;
}

/* ****************************************************************
 * make_hdr
 *
 * Make the header in the new a.out from the header in core.
 * Modify the text and data sizes.
 */
static int
make_hdr( new, a_out, data_start, bss_start, entry_address)
int new, a_out;
unsigned data_start, bss_start, entry_address;
{
    /* Get symbol table info from header of a.out file if given one. */
    if ( a_out >= 0 )
    {
	if ( read( a_out, &ohdr, sizeof hdr ) != sizeof hdr )
	{
	    PERROR( "Couldn't read header from a.out file" );
	    return -1;
	}

	if N_BADMAG( ohdr )
	{
	    ERROR( ERRORF "a.out file doesn't have legal magic number\n" );
	    return -1;
	}
	hdr.a_syms = ohdr.a_syms;
    }
    else
	hdr.a_syms = 0;			/* No a.out, so no symbol info. */

    /* Construct header from user structure. */
    hdr.a_magic = ZMAGIC;
    hdr.a_trsize = 0;
    hdr.a_drsize = 0;
    hdr.a_entry = entry_address;

    /* Adjust data/bss boundary. */
    if ( bss_start != 0 )
    {
	bss_start = (bss_start + pagemask) & ~pagemask;	      /* (Up) to page bdry. */
	if ( bss_start > sbrk (0))
	{
	    ERROR( ERRORF
		"unexec: Specified bss_start( %u ) is past end of program.\n",
		bss_start );
	    return -1;
	}
    }
    else
      bss_start = (sbrk (0) + pagemask) & ~pagemask;

    /* Adjust text/data boundary. */
    if (!data_start)
      data_start = (int) &etext;
#ifdef SUN
    data_start = data_start & ~(SEGSIZ - 1); /* (Down) to segment boundary. */
#else
    data_start = data_start & ~pagemask; /* (Down) to page boundary. */
#endif

    if ( data_start > bss_start )	/* Can't have negative data size. */
    {
	ERROR( ERRORF
	    "unexec: data_start(%u) can't be greater than bss_start( %u ).\n",
	    data_start, bss_start );
	return -1;
    }

    hdr.a_bss = sbrk (0) - bss_start;
    hdr.a_data = bss_start - data_start;
    hdr.a_text = data_start - TEXT_START;

    if ( write( new, &hdr, sizeof hdr ) != sizeof hdr )
    {
	PERROR( "Couldn't write header to new a.out file" );
	return -1;
    }
    return 0;
}

/* ****************************************************************
 * copy_text_and_data
 *
 * Copy the text and data segments from memory to the new a.out
 */
static int
copy_text_and_data( new )
int new;
{
    int nwrite, ret;
    int end;
    int i;
    int ptr;
    char buf[80];
    extern int errno;

    lseek (new, (long) N_TXTOFF (hdr), 0);

    end = hdr.a_text + hdr.a_data;
    for (i = 0, ptr = TEXT_START; i < end;)
      {
	nwrite = 128;
	if (nwrite > end - i) nwrite = end - i;
	ret = write (new, ptr, nwrite);
	if (ret == -1 && errno == EFAULT)
	  {
	    lseek (new, (long) (N_TXTOFF (hdr) + i + nwrite), 0);
	  }
	else if (nwrite != ret)
	  {
	    sprintf(buf, "Write failure in unexec: ptr 0x%x size 0x%x nwrite 0x%x errno %d",
			 ptr, nwrite, ret, errno);
	    PERROR(buf);
	    return -1;
	  }
	i += nwrite;
	ptr += nwrite;
      }

    return 0;
}

/* ****************************************************************
 * copy_sym
 *
 * Copy the relocation information and symbol table from the a.out to the new
 */
static int
copy_sym( new, a_out )
int new, a_out;
{
    char page[1024];
    int n;

    if ( a_out < 0 )
	return 0;

    lseek( a_out, (long)N_SYMOFF(ohdr), 0 );	/* Position a.out to symtab.*/
    while ( (n = read( a_out, page, sizeof page )) > 0 )
    {
	if ( write( new, page, n ) != n )
	{
	    PERROR( "Error writing symbol table to new a.out" );
	    ERROR( ERRORF "new a.out should be ok otherwise\n" );
	    return 0;
	}
    }
    if ( n < 0 )
    {
	PERROR( "Error reading symbol table from a.out,\n" );
	ERROR( ERRORF "new a.out should be ok otherwise\n" );
    }
    return 0;
}

/* ****************************************************************
 * mark_x
 *
 * After succesfully building the new a.out, mark it executable
 */
static
mark_x( name )
char *name;
{
    struct stat sbuf;
    int um;

    um = umask( 777 );
    umask( um );
    if ( stat( name, &sbuf ) == -1 )
    {
	PERROR ( "Can't stat new a.out" );
	ERROR( ERRORF "Setting protection to %o\n", 0777 & ~um );
	sbuf.st_mode = 0777;
    }
    sbuf.st_mode |= 0111 & ~um;
    if ( chmod( name, sbuf.st_mode ) == -1 )
	PERROR( "Couldn't change mode of new a.out to executable" );

}
#endif /* USG || STRIDE */
-- 

	Bruce Robertson
	UUCP: cbosgd!utah-cs!utah-gr!stride!bruce