Use a newer version from GNU Emacs.
authorGuillermo J. Rozas <edu/mit/csail/zurich/gjr>
Sat, 11 Apr 1987 15:17:52 +0000 (15:17 +0000)
committerGuillermo J. Rozas <edu/mit/csail/zurich/gjr>
Sat, 11 Apr 1987 15:17:52 +0000 (15:17 +0000)
v7/src/microcode/unexec.c

index a5f04bab32d51754d45d1dbcd8079e60e40e9614..a677017b0c133ef6a932adf429c9e0a6000e483c 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1985 Free Software Foundation, Inc.
+/* Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -18,9 +18,9 @@ file named COPYING.  Among other things, the copyright notice
 and this notice must be preserved on all copies.  */
 
 
-/* 
+/*
  * unexec.c - Convert a running program into an a.out file.
- * 
+ *
  * Author:     Spencer W. Thomas
  *             Computer Science Dept.
  *             University of Utah
@@ -35,19 +35,23 @@ and this notice must be preserved on all copies.  */
  * 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 
+ * On some machines, an existing a_name file is required.
+ *
+ * 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.
- * 
+ * and unprotected.  Data_start gives the lowest unprotected address.
+ * The value you specify may be rounded down to a suitable boundary
+ * as required by the machine you are using.
+ *
+ * Specifying zero for data_start means the boundary between text and data
+ * should not be the same as when the program was loaded.
+ * If NO_REMAP is defined, the argument data_start is ignored and the
+ * segment boundaries are never changed.
+ *
  * 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
@@ -62,55 +66,153 @@ and this notice must be preserved on all copies.  */
  *
  */
 
-#ifdef emacs
-#include "config.h"
-#define has_error
-#endif
+/* There are several compilation parameters affecting unexec:
+
+* COFF
+
+Define this if your system uses COFF for executables.
+Otherwise we assume you use Berkeley format.
+
+* NO_REMAP
+
+Define this if you do not want to try to save Emacs's pure data areas
+as part of the text segment.
+
+Saving them as text is good because it allows users to share more.
+
+However, on machines that locate the text area far from the data area,
+the boundary cannot feasibly be moved.  Such machines require
+NO_REMAP.
+
+Also, remapping can cause trouble with the built-in startup routine
+/lib/crt0.o, which defines `environ' as an initialized variable.
+Dumping `environ' as pure does not work!  So, to use remapping,
+you must write a startup routine for your machine in Emacs's crt0.c.
+If NO_REMAP is defined, Emacs uses the system's crt0.o.
+
+* SECTION_ALIGNMENT
+
+Some machines that use COFF executables require that each section
+start on a certain boundary *in the COFF file*.  Such machines should
+define SECTION_ALIGNMENT to a mask of the low-order bits that must be
+zero on such a boundary.  This mask is used to control padding between
+segments in the COFF file.
+
+If SECTION_ALIGNMENT is not defined, the segments are written
+consecutively with no attempt at alignment.  This is right for
+unmodified system V.
+
+* SEGMENT_MASK
+
+Some machines require that the beginnings and ends of segments
+*in core* be on certain boundaries.  For most machines, a page
+boundary is sufficient.  That is the default.  When a larger
+boundary is needed, define SEGMENT_MASK to a mask of
+the bits that must be zero on such a boundary.
+
+* A_TEXT_OFFSET(HDR)
+
+Some machines count the a.out header as part of the size of the text
+segment (a_text); they may actually load the header into core as the
+first data in the text segment.  Some have additional padding between
+the header and the real text of the program that is counted in a_text.
+
+For these machines, define A_TEXT_OFFSET(HDR) to examine the header
+structure HDR and return the number of bytes to add to `a_text'
+before writing it (above and beyond the number of bytes of actual
+program text).  HDR's standard fields are already correct, except that
+this adjustment to the `a_text' field has not yet been made;
+thus, the amount of offset can depend on the data in the file.
+  
+* A_TEXT_SEEK(HDR)
+
+If defined, this macro specifies the number of bytes to seek into the
+a.out file before starting to write the text segment.a
+
+* EXEC_MAGIC
+
+For machines using COFF, this macro, if defined, is a value stored
+into the magic number field of the output file.
+
+* ADJUST_EXEC_HEADER
+
+This macro can be used to generate statements to adjust or
+initialize nonstandard fields in the file header
+
+* ADDR_CORRECT(ADDR)
+
+Macro to correct an int which is the bit pattern of a pointer to a byte
+into an int which is the number of a byte.
 
-#ifndef has_error
+This macro has a default definition which is usually right.
+This default definition is a no-op on most machines (where a
+pointer looks like an int) but not on all machines.
+
+*/
+
+#ifndef mips  /* mips machine requires completely separate code.  */
+
+#ifndef emacs
 #define PERROR(arg) perror (arg); return -1
 #else
+#include "config.h"
 #define PERROR(file) report_error (file, new)
 #endif
 
 #ifndef CANNOT_DUMP  /* all rest of file!  */
 
-#include <sys/param.h>
+#include <a.out.h>
+/* Define getpagesize () if the system does not.
+   Note that this may depend on symbols defined in a.out.h
+ */
+#include "getpagesize.h"
+
 #ifndef makedev                        /* Try to detect types.h already loaded */
 #include <sys/types.h>
 #endif
 #include <stdio.h>
 #include <sys/stat.h>
 #include <errno.h>
-  
+
 extern char *start_of_text ();         /* Start of text */
 extern char *start_of_data ();         /* Start of initialized data */
-  
+
 #ifdef COFF
+#ifndef USG
+#ifndef STRIDE
+#ifndef UMAX
+/* I have a suspicion that these are turned off on all systems
+   and can be deleted.  Try it in version 19.  */
 #include <filehdr.h>
 #include <aouthdr.h>
 #include <scnhdr.h>
+#include <syms.h>
+#endif /* not UMAX */
+#endif /* Not STRIDE */
+#endif /* not USG */
 static long block_copy_start;          /* Old executable start point */
 static struct filehdr f_hdr;           /* File header */
 static struct aouthdr f_ohdr;          /* Optional file header (a.out) */
+long bias;                     /* Bias to add for growth */
+long lnnoptr;                  /* Pointer to line-number info within file */
 #define SYMS_START block_copy_start
 
-static int text_scnptr;
+static long text_scnptr;
+static long data_scnptr;
 
 #else /* not COFF */
 
 extern char *sbrk ();
 
-#include <a.out.h>
 #define SYMS_START ((long) N_SYMOFF (ohdr))
 
 #ifdef HPUX
-#ifdef hp9000s200
+#ifdef HP9000S200_ID
 #define MY_ID HP9000S200_ID
 #else
 #include <model.h>
 #define MY_ID MYSYS
-#endif /* not hp9000s200 */
+#endif /* no HP9000S200_ID */
 static MAGIC OLDMAGIC = {MY_ID, SHARE_MAGIC};
 static MAGIC NEWMAGIC = {MY_ID, DEMAND_MAGIC};
 #define N_TXTOFF(x) TEXT_OFFSET(x)
@@ -139,34 +241,22 @@ static struct exec hdr, ohdr;
 #endif /* not USG */
 #endif /* not HPUX */
 
+static int unexec_text_start;
+static int unexec_data_start;
+
 #endif /* not COFF */
 
 static int pagemask;
 
-#if defined (BSD4_1) || defined (USG)
-#ifdef EXEC_PAGESIZE
-#define getpagesize() EXEC_PAGESIZE
-#else
-#ifdef NBPG
-#define getpagesize() NBPG * CLSIZE
-#ifndef CLSIZE
-#define CLSIZE 1
-#endif /* no CLSIZE */
-#else /* no NBPG */
-#define getpagesize() NBPC
-#endif /* no NBPG */
-#endif /* no EXEC_PAGESIZE */
-#endif /* BSD4_1 or USG */
-
 /* Correct an int which is the bit pattern of a pointer to a byte
    into an int which is the number of a byte.
    This is a no-op on ordinary machines, but not on all.  */
 
 #ifndef ADDR_CORRECT   /* Let m-*.h files override this definition */
-#define ADDR_CORRECT(x) (((char *)(x)) - ((char *) 0))
+#define ADDR_CORRECT(x) ((char *)(x) - (char*)0)
 #endif
 
-#ifdef has_error
+#ifdef emacs
 
 static
 report_error (file, fd)
@@ -177,7 +267,7 @@ report_error (file, fd)
     close (fd);
   error ("Failure operating on %s", file);
 }
-#endif /* has_error */
+#endif /* emacs */
 
 #define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
 #define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
@@ -190,7 +280,7 @@ report_error_1 (fd, msg, a1, a2)
      int a1, a2;
 {
   close (fd);
-#ifdef has_error
+#ifdef emacs
   error (msg, a1, a2);
 #else
   fprintf (stderr, msg, a1, a2);
@@ -217,11 +307,12 @@ unexec (new_name, a_name, data_start, bss_start, entry_address)
     {
       PERROR (new_name);
     }
+
   if (make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name) < 0
       || copy_text_and_data (new) < 0
       || copy_sym (new, a_out, a_name, new_name) < 0
 #ifdef COFF
-      || adjust_lnnoptrs (new) < 0
+      || adjust_lnnoptrs (new, a_out, new_name) < 0
 #endif
       )
     {
@@ -250,14 +341,55 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
      char *a_name;
      char *new_name;
 {
+  int tem;
 #ifdef COFF
   auto struct scnhdr f_thdr;           /* Text section header */
   auto struct scnhdr f_dhdr;           /* Data section header */
   auto struct scnhdr f_bhdr;           /* Bss section header */
   auto struct scnhdr scntemp;          /* Temporary section header */
   register int scns;
-  register long bias;                  /* Bias to add for growth */
+#endif /* COFF */
+  unsigned int bss_end;
+
+  pagemask = getpagesize () - 1;
+
+  /* Adjust text/data boundary. */
+#ifdef NO_REMAP
+  data_start = (int) start_of_data ();
+#else /* not NO_REMAP */
+  if (!data_start)
+    data_start = (int) start_of_data ();
+#endif /* not NO_REMAP */
+  data_start = ADDR_CORRECT (data_start);
+
+#ifdef SEGMENT_MASK
+  data_start = data_start & ~SEGMENT_MASK; /* (Down) to segment boundary. */
+#else
+  data_start = data_start & ~pagemask; /* (Down) to page boundary. */
+#endif
+
+  bss_end = (ADDR_CORRECT (sbrk (0)) + pagemask) & ~pagemask;
 
+  /* Adjust data/bss boundary. */
+  if (bss_start != 0)
+    {
+      bss_start = (ADDR_CORRECT (bss_start) + pagemask) & ~pagemask;         /* (Up) to page bdry. */
+      if (bss_start > bss_end)
+       {
+         ERROR1 ("unexec: Specified bss_start (%u) is past end of program",
+                 bss_start);
+       }
+    }
+  else
+    bss_start = bss_end;
+
+  if (data_start > bss_start)  /* Can't have negative data size. */
+    {
+      ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
+             data_start, bss_start);
+    }
+
+#ifdef COFF
   /* Salvage as much info from the existing file as possible */
   if (a_out >= 0)
     {
@@ -273,17 +405,17 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
              PERROR (a_name);
            }
          block_copy_start += sizeof (f_ohdr);
-       }           
+       }
       /* Loop through section headers, copying them in */
       for (scns = f_hdr.f_nscns; scns > 0; scns--) {
        if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp))
          {
            PERROR (a_name);
          }
-       block_copy_start += sizeof (scntemp);
        if (scntemp.s_scnptr > 0L)
          {
-           block_copy_start += scntemp.s_size;
+            if (block_copy_start < scntemp.s_scnptr + scntemp.s_size)
+             block_copy_start = scntemp.s_scnptr + scntemp.s_size;
          }
        if (strcmp (scntemp.s_name, ".text") == 0)
          {
@@ -297,39 +429,49 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
          {
            f_bhdr = scntemp;
          }
-      }            
+      }
     }
   else
     {
       ERROR0 ("can't build a COFF file from scratch yet");
     }
 
-  pagemask = getpagesize () - 1;
-
-  if (!data_start)
-    data_start = (int) start_of_data ();
-  data_start = ADDR_CORRECT (data_start);
-  data_start = data_start & ~pagemask; /* down to a page boundary */
+  /* Now we alter the contents of all the f_*hdr variables
+     to correspond to what we want to dump.  */
 
   f_hdr.f_flags |= (F_RELFLG | F_EXEC);
 #ifdef EXEC_MAGIC
   f_ohdr.magic = EXEC_MAGIC;
 #endif
+#ifndef NO_REMAP
   f_ohdr.text_start = (long) start_of_text ();
   f_ohdr.tsize = data_start - f_ohdr.text_start;
   f_ohdr.data_start = data_start;
-  f_ohdr.dsize = (long) sbrk (0) - f_ohdr.data_start;
-  f_ohdr.bsize = 0;
+#endif /* NO_REMAP */
+  f_ohdr.dsize = bss_start - f_ohdr.data_start;
+  f_ohdr.bsize = bss_end - bss_start;
   f_thdr.s_size = f_ohdr.tsize;
   f_thdr.s_scnptr = sizeof (f_hdr) + sizeof (f_ohdr);
   f_thdr.s_scnptr += (f_hdr.f_nscns) * (sizeof (f_thdr));
-  f_thdr.s_scnptr = (f_thdr.s_scnptr + pagemask) & ~pagemask;  /* round up */
+  lnnoptr = f_thdr.s_lnnoptr;
+#ifdef SECTION_ALIGNMENT
+  /* Some systems require special alignment
+     of the sections in the file itself.  */
+  f_thdr.s_scnptr
+    = (f_thdr.s_scnptr + SECTION_ALIGNMENT) & ~SECTION_ALIGNMENT;
+#endif /* SECTION_ALIGNMENT */
   text_scnptr = f_thdr.s_scnptr;
   f_dhdr.s_paddr = f_ohdr.data_start;
   f_dhdr.s_vaddr = f_ohdr.data_start;
   f_dhdr.s_size = f_ohdr.dsize;
   f_dhdr.s_scnptr = f_thdr.s_scnptr + f_thdr.s_size;
-  f_dhdr.s_scnptr &= ~pagemask; /* round down to page boundary */
+#ifdef SECTION_ALIGNMENT
+  /* Some systems require special alignment
+     of the sections in the file itself.  */
+  f_dhdr.s_scnptr
+    = (f_dhdr.s_scnptr + SECTION_ALIGNMENT) & ~SECTION_ALIGNMENT;
+#endif /* SECTION_ALIGNMENT */
+  data_scnptr = f_dhdr.s_scnptr;
   f_bhdr.s_paddr = f_ohdr.data_start + f_ohdr.dsize;
   f_bhdr.s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
   f_bhdr.s_size = f_ohdr.bsize;
@@ -341,11 +483,15 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
       f_hdr.f_symptr += bias;
     }
 
-  if (f_thdr.s_lnnoptr > 0L) 
+  if (f_thdr.s_lnnoptr > 0L)
     {
       f_thdr.s_lnnoptr += bias;
     }
 
+#ifdef ADJUST_EXEC_HEADER
+  ADJUST_EXEC_HEADER
+#endif /* ADJUST_EXEC_HEADER */
+
   if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
     {
       PERROR (new_name);
@@ -371,7 +517,7 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
       PERROR (new_name);
     }
   return (0);
-    
+
 #else /* if not COFF */
 
   /* Get symbol table info from header of a.out file if given one. */
@@ -386,98 +532,47 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
        {
          ERROR1 ("invalid magic number in %s", a_name);
        }
-#ifdef celerity
-      hdr.a_scovrfl = ohdr.a_scovrfl;
-#endif
-#ifdef HPUX
-      hdr.a_lesyms = ohdr.a_lesyms;
-      hdr.a_sltsize = ohdr.a_sltsize;
-      hdr.a_dnttsize = ohdr.a_dnttsize;
-      hdr.a_vtsize = ohdr.a_vtsize;
-#else /* not HPUX */
-      hdr.a_syms = ohdr.a_syms;
-#endif /* not HPUX */
+      hdr = ohdr;
     }
   else
     {
-#ifdef celerity
-      hdr.a_scovrfl = 0;
-#endif
-#ifdef HPUX
-      hdr.a_lesyms = 0;
-      hdr.a_sltsize = 0;
-      hdr.a_dnttsize = 0;
-      hdr.a_vtsize = 0;
-#else /* not HPUX */
-      hdr.a_syms = 0;                  /* No a.out, so no symbol info. */
-#endif /* not HPUX */
+      bzero (hdr, sizeof hdr);
     }
 
-  /* Construct header from user structure. */
-#ifdef HPUX
-  /* (((MAGIC) ohdr.a_magic) == ((MAGIC) OLDMAGIC)) This does not work */
-  hdr.a_magic = ((ohdr.a_magic.file_type == OLDMAGIC.file_type) ?
-                NEWMAGIC : ohdr.a_magic);
-#else /* not HPUX */
-/* hdr.a_magic = NEWMAGIC; */
-  hdr.a_magic = ohdr.a_magic;
-#endif /* not HPUX */
-#ifdef sun3
-  hdr.a_machtype = ohdr.a_machtype;
-#endif /* sun3 */
-  hdr.a_trsize = 0;
-  hdr.a_drsize = 0;
-  hdr.a_entry = entry_address;
+  unexec_text_start = (long) start_of_text ();
+  unexec_data_start = data_start;
 
-  pagemask = getpagesize () - 1;
+  /* Machine-dependent fixup for header, or maybe for unexec_text_start */
+#ifdef ADJUST_EXEC_HEADER
+  ADJUST_EXEC_HEADER;
+#endif /* ADJUST_EXEC_HEADER */
 
-  /* Adjust data/bss boundary. */
-  if (bss_start != 0)
-    {
-      bss_start = (ADDR_CORRECT (bss_start) + pagemask) & ~pagemask;         /* (Up) to page bdry. */
-      if (bss_start > ADDR_CORRECT (sbrk (0)))
-       {
-         ERROR1 ("unexec: Specified bss_start (%u) is past end of program",
-                 bss_start);
-       }
-    }
-  else
-    {
-      bss_start = ADDR_CORRECT (sbrk (0));
-      bss_start = (bss_start + pagemask) & ~pagemask;
-    }
+  hdr.a_trsize = 0;
+  hdr.a_drsize = 0;
+  if (entry_address != 0)
+    hdr.a_entry = entry_address;
 
-  /* Adjust text/data boundary. */
-  if (!data_start)
-    data_start = (int) start_of_data ();
+  hdr.a_bss = bss_end - bss_start;
+  hdr.a_data = bss_start - data_start;
+#ifdef NO_REMAP
+  hdr.a_text = ohdr.a_text;
+#else /* not NO_REMAP */
+  hdr.a_text = data_start - unexec_text_start;
+#endif /* not NO_REMAP */
 
-  data_start = ADDR_CORRECT (data_start);
-#ifdef sun
-  data_start = data_start & ~(SEGSIZ - 1); /* (Down) to segment boundary. */
-#else
-  data_start = data_start & ~pagemask; /* (Down) to page boundary. */
+#ifdef A_TEXT_OFFSET
+  hdr.a_text += A_TEXT_OFFSET (ohdr);
 #endif
 
-  if (data_start > bss_start)  /* Can't have negative data size. */
-    {
-      ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
-             data_start, bss_start);
-    }
-
-  hdr.a_bss = ADDR_CORRECT (sbrk (0)) - bss_start;
-  if (hdr.a_bss < 0)
-    hdr.a_bss = 0;
-  hdr.a_data = bss_start - data_start;
-#if defined(sequent)
-  hdr.a_text = data_start - (long) start_of_text () + sizeof(hdr) + N_ADDRADJ(ohdr);
-#else
-  hdr.a_text = data_start - (long) start_of_text ();
-#endif /* not sequent */
-
   if (write (new, &hdr, sizeof hdr) != sizeof hdr)
     {
       PERROR (new_name);
     }
+
+#ifdef A_TEXT_OFFSET
+  hdr.a_text -= A_TEXT_OFFSET (ohdr);
+#endif
+
   return 0;
 
 #endif /* not COFF */
@@ -492,60 +587,85 @@ static int
 copy_text_and_data (new)
      int new;
 {
-  register int nwrite, ret;
   register char *end;
-  int i;
   register char *ptr;
-  char buf[80];
-  extern int errno;
-  
+
 #ifdef COFF
   lseek (new, (long) text_scnptr, 0);
   ptr = (char *) f_ohdr.text_start;
-  end = ptr + f_ohdr.tsize + f_ohdr.dsize;
-  while (ptr < end)
-    {
-      nwrite = 128;
-      if (nwrite > end - ptr) nwrite = end - ptr;
-      ret = write (new, ptr, nwrite);
-      if (nwrite != ret)
-       {
-         sprintf (buf,
-                  "unexec write failure: addr 0x%x, fileno %d, size 0x%x, wrote 0x%x, errno %d",
-                  ptr, new, nwrite, ret, errno);
-         PERROR (buf);
-       }
-      ptr += nwrite;
-    }
-  return (0);
+  end = ptr + f_ohdr.tsize;
+  write_segment (new, ptr, end);
+
+  lseek (new, (long) data_scnptr, 0);
+  ptr = (char *) f_ohdr.data_start;
+  end = ptr + f_ohdr.dsize;
+  write_segment (new, ptr, end);
 
 #else /* if not COFF */
 
-#if defined(sun3) || defined(sequent)
-  lseek (new, (long) (N_TXTOFF (hdr) + sizeof (hdr)), 0);
+/* Some machines count the header as part of the text segment.
+   That is to say, the header appears in core
+   just before the address that start_of_text () returns.
+   For them, N_TXTOFF is the place where the header goes.
+   We must adjust the seek to the place after the header.
+   Note that at this point hdr.a_text does *not* count
+   the extra A_TEXT_OFFSET bytes, only the actual bytes of code.  */
+
+#ifdef A_TEXT_SEEK
+  lseek (new, (long) A_TEXT_SEEK (hdr), 0);
+#else
+#ifdef A_TEXT_OFFSET
+  /* Note that on the Sequent machine A_TEXT_OFFSET != sizeof (hdr)
+     and sizeof (hdr) is the correct amount to add here.  */
+  /* In version 19, eliminate this case and use A_TEXT_SEEK whenever
+     N_TXTOFF is not right.  */
+  lseek (new, (long) N_TXTOFF (hdr) + sizeof (hdr), 0);
 #else
   lseek (new, (long) N_TXTOFF (hdr), 0);
-#endif
+#endif /* no A_TEXT_OFFSET */
+#endif /* no A_TEXT_SEEK */
+
+  ptr = (char *) unexec_text_start;
+  end = ptr + hdr.a_text;
+  write_segment (new, ptr, end);
+
+  ptr = (char *) unexec_data_start;
+  end = ptr + hdr.a_data;
+/*  This lseek is certainly incorrect when A_TEXT_OFFSET
+    and I believe it is a no-op otherwise.
+    Let's see if its absence ever fails.  */
+/*  lseek (new, (long) N_TXTOFF (hdr) + hdr.a_text, 0); */
+  write_segment (new, ptr, end);
+
+#endif /* not COFF */
+
+  return 0;
+}
+
+write_segment (new, ptr, end)
+     int new;
+     register char *ptr, *end;
+{
+  register int i, nwrite, ret;
+  char buf[80];
+  extern int errno;
+  char zeros[128];
+
+  bzero (zeros, sizeof zeros);
 
-  ptr = start_of_text ();
-  end = ptr + hdr.a_text + hdr.a_data;
-#if defined(sequent)
-  end -= (sizeof(hdr) + N_ADDRADJ(hdr));
-#endif
   for (i = 0; ptr < end;)
     {
-      nwrite = 128;
+      /* distance to next multiple of 128.  */
+      nwrite = (((int) ptr + 128) & -128) - (int) ptr;
+      /* But not beyond specified end.  */
       if (nwrite > end - ptr) nwrite = end - ptr;
       ret = write (new, ptr, nwrite);
+      /* If write gets a page fault, it means we reached
+        a gap between the old text segment and the old data segment.
+        This gap has probably been remapped into part of the text segment.
+        So write zeros for it.  */
       if (ret == -1 && errno == EFAULT)
-       {
-          /* BZS - again, see above about N_TXTOFF on a SUN */
-#if defined(sun3) || defined(sequent)
-         lseek (new, (long) (N_TXTOFF (hdr) + i + nwrite + sizeof (hdr)), 0);
-#else
-         lseek (new, (long) (N_TXTOFF (hdr) + i + nwrite), 0);
-#endif
-       }
+       write (new, zeros, nwrite);
       else if (nwrite != ret)
        {
          sprintf (buf,
@@ -556,9 +676,6 @@ copy_text_and_data (new)
       i += nwrite;
       ptr += nwrite;
     }
-
-  return 0;
-#endif /* not COFF */
 }
 \f
 /* ****************************************************************
@@ -582,12 +699,13 @@ copy_sym (new, a_out, a_name, new_name)
     return 0;
 #endif  /* COFF */
 
-#ifdef sun3
-  /* BZS - I might be covering a sin with this */
-  lseek (new, N_SYMOFF (hdr), 0);
-#else
-  lseek (a_out, SYMS_START, 0);        /* Position a.out to symtab. */
-#endif
+#ifdef COFF
+  if (lnnoptr)                 /* if there is line number info */
+    lseek (a_out, lnnoptr, 0); /* start copying from there */
+  else
+#endif /* COFF */
+    lseek (a_out, SYMS_START, 0);      /* Position a.out to symtab. */
+
   while ((n = read (a_out, page, sizeof page)) > 0)
     {
       if (write (new, page, n) != n)
@@ -643,12 +761,292 @@ mark_x (name)
 
 #ifdef COFF
 
-adjust_lnnoptrs (new)
-     int new;
+/* This function is probably very slow.  Instead of reopening the new
+   file for input and output it should copy from the old to the new
+   using the two descriptors already open (WRITEDESC and READDESC).
+   Instead of reading one small structure at a time it should use
+   a reasonable size buffer.  But I don't have time to work on such
+   things, so I am installing it as submitted to me.  -- RMS.  */
+
+adjust_lnnoptrs (writedesc, readdesc, new_name)
+     int writedesc;
+     int readdesc;
+     char *new_name;
 {
-  return 0;
+  register int nsyms;
+  register int new;
+#ifdef amdahl_uts
+  SYMENT symentry;
+  AUXENT auxentry;
+#else
+  struct syment symentry;
+  struct auxent auxentry;
+#endif
+
+  if (!lnnoptr || !f_hdr.f_symptr)
+    return 0;
+
+  if ((new = open (new_name, 2)) < 0)
+    {
+      PERROR (new_name);
+      return -1;
+    }
+
+  lseek (new, f_hdr.f_symptr, 0);
+  for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
+    {
+      read (new, &symentry, SYMESZ);
+      if (symentry.n_numaux)
+       {
+         read (new, &auxentry, AUXESZ);
+         nsyms++;
+         if (ISFCN (symentry.n_type)) {
+           auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
+           lseek (new, -AUXESZ, 1);
+           write (new, &auxentry, AUXESZ);
+         }
+       }
+    }
+  close (new);
 }
 
 #endif /* COFF */
 
 #endif /* not CANNOT_DUMP */
+\f
+#else /* mips */
+
+/* Unexec for mips machines.
+   Note that I regard it as the responsibility of people at Mips
+   to tell me about any changes that need to be made in this code.
+   I won't take responsibility to think about it even if a change
+   I make elsewhere causes it to break.  -- RMS.  */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <varargs.h>
+#include <filehdr.h>
+#include <aouthdr.h>
+#include <scnhdr.h>
+#include <sym.h>
+
+#include "m-mips.h"
+
+#define private static
+\f
+extern int errno;
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define EEOF -1
+
+private void
+fatal(s, va_alist)
+    va_dcl
+{
+    va_list ap;
+    if (errno == EEOF) {
+       fputs("unexec: unexpected end of file, ", stderr);
+    }
+    else if (errno < sys_nerr) {
+       fprintf(stderr, "unexec: %s, ", sys_errlist[errno]);
+    }
+    else {
+       fprintf(stderr, "unexec: error code %d, ", errno);
+    }
+    va_start(ap);
+    _doprnt(s, ap, stderr);
+    fputs(".\n", stderr);
+    exit(1);
+}
+
+#define READ(_fd, _buffer, _size, _error_message, _error_arg) \
+       errno = EEOF; \
+       if (read(_fd, _buffer, _size) != _size) \
+         fatal(_error_message, _error_arg);
+
+#define WRITE(_fd, _buffer, _size, _error_message, _error_arg) \
+       if (write(_fd, _buffer, _size) != _size) \
+         fatal(_error_message, _error_arg);
+
+#define SEEK(_fd, _position, _error_message, _error_arg) \
+       errno = EEOF; \
+       if (lseek(_fd, _position, L_SET) != _position) \
+         fatal(_error_message, _error_arg);
+\f
+struct headers {
+    struct filehdr fhdr;
+    struct aouthdr aout;
+    struct scnhdr text_section;
+    struct scnhdr rdata_section;
+    struct scnhdr data_section;
+    struct scnhdr sdata_section;
+    struct scnhdr sbss_section;
+    struct scnhdr bss_section;
+};
+
+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, old;
+  int pagesize, brk;
+  int newsyms, symrel;
+  int nread;
+  struct headers hdr;
+#define BUFSIZE 8192
+  char buffer[BUFSIZE];
+
+  old = open (a_name, O_RDONLY, 0);
+  if (old < 0) fatal("openning %s", a_name);
+
+  new = creat (new_name, 0666);
+  if (new < 0) fatal("creating %s", new_name);
+
+  hdr = *((struct headers *)TEXT_START);
+  if (hdr.fhdr.f_magic != MIPSELMAGIC
+      && hdr.fhdr.f_magic != MIPSEBMAGIC) {
+      fprintf(stderr, "unexec: input file magic number is %x, not %x or %x.\n",
+             hdr.fhdr.f_magic, MIPSELMAGIC, MIPSEBMAGIC);
+      exit(1);
+  }
+  if (hdr.fhdr.f_opthdr != sizeof(hdr.aout)) {
+      fprintf(stderr, "unexec: input a.out header is %d bytes, not %d.\n",
+             hdr.fhdr.f_opthdr, sizeof(hdr.aout));
+      exit(1);
+  }
+#if 0
+  if (hdr.aout.magic != ZMAGIC
+      && hdr.aout.magic != NMAGIC
+      && hdr.aout.magic != OMAGIC) {
+      fprintf(stderr, "unexec: input file a.out magic number is %o, not %o, %o, or %o.\n",
+             hdr.aout.magic, ZMAGIC, NMAGIC, OMAGIC);
+      exit(1);
+  }
+#else
+  if (hdr.aout.magic != ZMAGIC) {
+      fprintf(stderr, "unexec: input file a.out magic number is %o, not %o.\n",
+             hdr.aout.magic, ZMAGIC);
+      exit(1);
+  }
+#endif
+  if (hdr.fhdr.f_nscns != 6) {
+      fprintf(stderr, "unexec: %d sections instead of 6.\n", hdr.fhdr.f_nscns);
+  }
+#define CHECK_SCNHDR(field, name, flags) \
+  if (strcmp(hdr.field.s_name, name) != 0) { \
+      fprintf(stderr, "unexec: %s section where %s expected.\n", \
+             hdr.field.s_name, name); \
+      exit(1); \
+  } \
+  else if (hdr.field.s_flags != flags) { \
+      fprintf(stderr, "unexec: %x flags where %x expected in %s section.\n", \
+             hdr.field.s_flags, flags, name); \
+  }
+  CHECK_SCNHDR(text_section,  _TEXT,  STYP_TEXT);
+  CHECK_SCNHDR(rdata_section, _RDATA, STYP_RDATA);
+  CHECK_SCNHDR(data_section,  _DATA,  STYP_DATA);
+  CHECK_SCNHDR(sdata_section, _SDATA, STYP_SDATA);
+  CHECK_SCNHDR(sbss_section,  _SBSS,  STYP_SBSS);
+  CHECK_SCNHDR(bss_section,   _BSS,   STYP_BSS);
+
+  pagesize = getpagesize();
+  brk = (sbrk(0) + pagesize - 1) & (-pagesize);
+  hdr.aout.dsize = brk - DATA_START;
+  hdr.aout.bsize = 0;
+  if (entry_address == 0) {
+    extern __start();
+    hdr.aout.entry = (unsigned)__start;
+  }
+  else {
+    hdr.aout.entry = entry_address;
+  }
+  hdr.aout.bss_start = hdr.aout.data_start + hdr.aout.dsize;
+  hdr.rdata_section.s_size = data_start - DATA_START;
+  hdr.data_section.s_vaddr = data_start;
+  hdr.data_section.s_paddr = data_start;
+  hdr.data_section.s_size = brk - DATA_START;
+  hdr.data_section.s_scnptr = hdr.rdata_section.s_scnptr
+                               + hdr.rdata_section.s_size;
+  hdr.sdata_section.s_vaddr = hdr.data_section.s_vaddr
+                               + hdr.data_section.s_size;
+  hdr.sdata_section.s_paddr = hdr.sdata_section.s_paddr;
+  hdr.sdata_section.s_size = 0;
+  hdr.sdata_section.s_scnptr = hdr.data_section.s_scnptr
+                               + hdr.data_section.s_size;
+  hdr.sbss_section.s_vaddr = hdr.sdata_section.s_vaddr
+                               + hdr.sdata_section.s_size;
+  hdr.sbss_section.s_paddr = hdr.sbss_section.s_vaddr;
+  hdr.sbss_section.s_size = 0;
+  hdr.sbss_section.s_scnptr = hdr.sdata_section.s_scnptr
+                               + hdr.sdata_section.s_size;
+  hdr.bss_section.s_vaddr = hdr.sbss_section.s_vaddr
+                               + hdr.sbss_section.s_size;
+  hdr.bss_section.s_paddr = hdr.bss_section.s_vaddr;
+  hdr.bss_section.s_size = 0;
+  hdr.bss_section.s_scnptr = hdr.sbss_section.s_scnptr
+                               + hdr.sbss_section.s_size;
+
+  WRITE(new, TEXT_START, hdr.aout.tsize,
+       "writing text section to %s", new_name);
+  WRITE(new, DATA_START, hdr.aout.dsize,
+       "writing text section to %s", new_name);
+
+  SEEK(old, hdr.fhdr.f_symptr, "seeking to start of symbols in %s", a_name);
+  errno = EEOF;
+  nread = read(old, buffer, BUFSIZE);
+  if (nread < sizeof(HDRR)) fatal("reading symbols from %s", a_name);
+#define symhdr ((pHDRR)buffer)
+  newsyms = hdr.aout.tsize + hdr.aout.dsize;
+  symrel = newsyms - hdr.fhdr.f_symptr;
+  hdr.fhdr.f_symptr = newsyms;
+  symhdr->cbLineOffset += symrel;
+  symhdr->cbDnOffset += symrel;
+  symhdr->cbPdOffset += symrel;
+  symhdr->cbSymOffset += symrel;
+  symhdr->cbOptOffset += symrel;
+  symhdr->cbAuxOffset += symrel;
+  symhdr->cbSsOffset += symrel;
+  symhdr->cbSsExtOffset += symrel;
+  symhdr->cbFdOffset += symrel;
+  symhdr->cbRfdOffset += symrel;
+  symhdr->cbExtOffset += symrel;
+#undef symhdr
+  do {
+      if (write(new, buffer, nread) != nread)
+       fatal("writing symbols to %s", new_name);
+      nread = read(old, buffer, BUFSIZE);
+      if (nread < 0) fatal("reading symbols from %s", a_name);
+#undef BUFSIZE
+  } while (nread != 0);
+
+  SEEK(new, 0, "seeking to start of header in %s", new_name);
+  WRITE(new, &hdr, sizeof(hdr),
+       "writing header of %s", new_name);
+
+  close(old);
+  close(new);
+  mark_x(new_name);
+}
+
+/*
+ * mark_x
+ *
+ * After succesfully building the new a.out, mark it executable
+ */
+static
+mark_x (name)
+     char *name;
+{
+  struct stat sbuf;
+  int um = umask (777);
+  umask (um);
+  if (stat(name, &sbuf) < 0)
+    fatal("getting protection on %s", name);
+  sbuf.st_mode |= 0111 & ~um;
+  if (chmod(name, sbuf.st_mode) < 0)
+    fatal("setting protection on %s", name);
+}
+
+#endif /* mips */