*** empty log message ***
[bpt/emacs.git] / src / unexec.c
index bd6985a..fb00c27 100644 (file)
@@ -1,10 +1,10 @@
-/* Copyright (C) 1985, 1986, 1987, 1988, 1992 Free Software Foundation, Inc.
+/* Copyright (C) 1985,86,87,88,92,93,94 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 1, or (at your option)
+the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -14,7 +14,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 /*
 
 
 /*
@@ -137,7 +138,7 @@ 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_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
+a.out file before starting to write the text segment.
 
 * EXEC_MAGIC
 
 
 * EXEC_MAGIC
 
@@ -164,24 +165,49 @@ pointer looks like an int) but not on all machines.
 #define PERROR(arg) perror (arg); return -1
 #else
 #define IN_UNEXEC
 #define PERROR(arg) perror (arg); return -1
 #else
 #define IN_UNEXEC
-#include "config.h"
+#include <config.h>
 #define PERROR(file) report_error (file, new)
 #endif
 
 #ifndef CANNOT_DUMP  /* all rest of file!  */
 
 #define PERROR(file) report_error (file, new)
 #endif
 
 #ifndef CANNOT_DUMP  /* all rest of file!  */
 
-#ifndef CANNOT_UNEXEC /* most of rest of file */
-
+#ifdef COFF
+#include <coff.h>
+#ifdef MSDOS
+#if __DJGPP__ > 1
+#include <fcntl.h>  /* for O_RDONLY, O_RDWR */
+#include <crt0.h>   /* for _crt0_startup_flags and its bits */
+static int save_djgpp_startup_flags;
+#endif /* __DJGPP__ > 1 */
+#define filehdr external_filehdr
+#define scnhdr external_scnhdr
+#define syment external_syment
+#define auxent external_auxent
+#define n_numaux e_numaux
+#define n_type e_type
+struct aouthdr
+{
+  unsigned short       magic;  /* type of file                         */
+  unsigned short       vstamp; /* version stamp                        */
+  unsigned long                tsize;  /* text size in bytes, padded to FW bdry*/
+  unsigned long                dsize;  /* initialized data "  "                */
+  unsigned long                bsize;  /* uninitialized data "   "             */
+  unsigned long                entry;  /* entry pt.                            */
+  unsigned long                text_start;/* base of text used for this file */
+  unsigned long                data_start;/* base of data used for this file */
+};
+#endif /* not MSDOS */
+#else  /* not COFF */
 #ifdef COFF_ENCAPSULATE
 int need_coff_header = 1;
 #include <coff-encap/a.out.encap.h> /* The location might be a poor assumption */
 #ifdef COFF_ENCAPSULATE
 int need_coff_header = 1;
 #include <coff-encap/a.out.encap.h> /* The location might be a poor assumption */
-#else
+#else  /* not COFF_ENCAPSULATE */
 #include <a.out.h>
 #include <a.out.h>
-#endif
+#endif /* not COFF_ENCAPSULATE */
+#endif /* not COFF */
 
 
-/* Define getpagesize () if the system does not.
-   Note that this may depend on symbols defined in 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 "getpagesize.h"
 
 #ifndef makedev                        /* Try to detect types.h already loaded */
@@ -191,6 +217,20 @@ int need_coff_header = 1;
 #include <sys/stat.h>
 #include <errno.h>
 
 #include <sys/stat.h>
 #include <errno.h>
 
+#include <sys/file.h>  /* Must be after sys/types.h for USG and BSD4_1*/
+
+#ifdef USG5
+#include <fcntl.h>
+#endif
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+#ifndef O_RDWR
+#define O_RDWR 2
+#endif
+
+
 extern char *start_of_text ();         /* Start of text */
 extern char *start_of_data ();         /* Start of initialized data */
 
 extern char *start_of_text ();         /* Start of text */
 extern char *start_of_data ();         /* Start of initialized data */
 
@@ -205,13 +245,26 @@ long lnnoptr;                     /* Pointer to line-number info within file */
 static long text_scnptr;
 static long data_scnptr;
 
 static long text_scnptr;
 static long data_scnptr;
 
+static long coff_offset;
+
 #else /* not COFF */
 
 #else /* not COFF */
 
+#ifdef HPUX
+extern void *sbrk ();
+#else
+#if 0
+/* Some systems with __STDC__ compilers still declare this `char *' in some
+   header file, and our declaration conflicts.  The return value is always
+   cast, so it should be harmless to leave it undefined.  Hopefully
+   machines with different size pointers and ints declare sbrk in a header
+   file.  */
 #ifdef __STDC__
 extern void *sbrk ();
 #else
 extern char *sbrk ();
 #ifdef __STDC__
 extern void *sbrk ();
 #else
 extern char *sbrk ();
+#endif /* __STDC__ */
 #endif
 #endif
+#endif /* HPUX */
 
 #define SYMS_START ((long) N_SYMOFF (ohdr))
 
 
 #define SYMS_START ((long) N_SYMOFF (ohdr))
 
@@ -235,7 +288,7 @@ static EXEC_HDR_TYPE hdr, ohdr;
 
 #else /* not HPUX */
 
 
 #else /* not HPUX */
 
-#if defined (USG) && !defined (IBMAIX) && !defined (IRIS) && !defined (COFF_ENCAPSULATE)
+#if defined (USG) && !defined (IBMAIX) && !defined (IRIS) && !defined (COFF_ENCAPSULATE) && !defined (LINUX)
 static struct bhdr hdr, ohdr;
 #define a_magic fmagic
 #define a_text tsize
 static struct bhdr hdr, ohdr;
 #define a_magic fmagic
 #define a_text tsize
@@ -277,6 +330,8 @@ static int pagemask;
 
 #ifdef emacs
 
 
 #ifdef emacs
 
+#include "lisp.h"
+
 static
 report_error (file, fd)
      char *file;
 static
 report_error (file, fd)
      char *file;
@@ -284,7 +339,7 @@ report_error (file, fd)
 {
   if (fd)
     close (fd);
 {
   if (fd)
     close (fd);
-  error ("Failure operating on %s\n", file);
+  report_file_error ("Cannot unexec", Fcons (build_string (file), Qnil));
 }
 #endif /* emacs */
 
 }
 #endif /* emacs */
 
@@ -323,7 +378,7 @@ unexec (new_name, a_name, data_start, bss_start, entry_address)
 {
   int new, a_out = -1;
 
 {
   int new, a_out = -1;
 
-  if (a_name && (a_out = open (a_name, 0)) < 0)
+  if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
     {
       PERROR (a_name);
     }
     {
       PERROR (a_name);
     }
@@ -423,9 +478,32 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
     }
 
 #ifdef COFF
     }
 
 #ifdef COFF
+  coff_offset = 0L;            /* stays zero, except in DJGPP */
+
   /* Salvage as much info from the existing file as possible */
   if (a_out >= 0)
     {
   /* Salvage as much info from the existing file as possible */
   if (a_out >= 0)
     {
+#ifdef MSDOS
+#if __DJGPP__ > 1
+      /* Support the coff-go32-exe format with a prepended stub, since
+        this is what GCC 2.8.0 and later generates by default in DJGPP.  */
+      unsigned short mz_header[3];
+
+      if (read (a_out, &mz_header, sizeof (mz_header)) != sizeof (mz_header))
+       {
+         PERROR (a_name);
+       }
+      if (mz_header[0] == 0x5a4d || mz_header[0] == 0x4d5a) /* "MZ" or "ZM" */
+       {
+         coff_offset = (long)mz_header[2] * 512L;
+         if (mz_header[1])
+           coff_offset += (long)mz_header[1] - 512L;
+         lseek (a_out, coff_offset, 0);
+       }
+      else
+       lseek (a_out, 0L, 0);
+#endif /* __DJGPP__ > 1 */
+#endif /* MSDOS */
       if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
        {
          PERROR (a_name);
       if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
        {
          PERROR (a_name);
@@ -440,6 +518,7 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
          block_copy_start += sizeof (f_ohdr);
        }
       /* Loop through section headers, copying them in */
          block_copy_start += sizeof (f_ohdr);
        }
       /* Loop through section headers, copying them in */
+      lseek (a_out, coff_offset + sizeof (f_hdr) + f_hdr.f_opthdr, 0);
       for (scns = f_hdr.f_nscns; scns > 0; scns--) {
        if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp))
          {
       for (scns = f_hdr.f_nscns; scns > 0; scns--) {
        if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp))
          {
@@ -603,11 +682,13 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
    * .lib), adjust the address of where the section data is in the
    * file, and write out the header.
    *
    * .lib), adjust the address of where the section data is in the
    * file, and write out the header.
    *
-   * If any section preceeds .text or .data in the file, this code
+   * If any section precedes .text or .data in the file, this code
    * will not adjust the file pointer for that section correctly.
    */
 
    * will not adjust the file pointer for that section correctly.
    */
 
-  lseek (a_out, sizeof (f_hdr) + sizeof (f_ohdr), 0);
+  /* This used to use sizeof (f_ohdr) instead of .f_opthdr.
+     .f_opthdr is said to be right when there is no optional header.  */
+  lseek (a_out, sizeof (f_hdr) + f_hdr.f_opthdr, 0);
 
   for (scns = f_hdr.f_nscns; scns > 0; scns--)
     {
 
   for (scns = f_hdr.f_nscns; scns > 0; scns--)
     {
@@ -675,7 +756,11 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
        */
       ERROR0 ("can't build a COFF file from scratch yet");
 #else
        */
       ERROR0 ("can't build a COFF file from scratch yet");
 #else
-      bzero (hdr, sizeof hdr);
+#ifdef MSDOS   /* Demacs 1.1.1 91/10/16 HIRANO Satoshi */
+      bzero ((void *)&hdr, sizeof hdr);
+#else
+      bzero (&hdr, sizeof hdr);
+#endif
 #endif
     }
 
 #endif
     }
 
@@ -736,6 +821,11 @@ make_hdr (new, a_out, data_start, bss_start, entry_address, a_name, new_name)
       PERROR (new_name);
     }
 
       PERROR (new_name);
     }
 
+#if 0 /* This #ifndef caused a bug on Linux when using QMAGIC.  */
+  /* This adjustment was done above only #ifndef NO_REMAP,
+     so only undo it now #ifndef NO_REMAP.  */
+  /* #ifndef NO_REMAP  */
+#endif
 #ifdef A_TEXT_OFFSET
   hdr.a_text -= A_TEXT_OFFSET (ohdr);
 #endif
 #ifdef A_TEXT_OFFSET
   hdr.a_text -= A_TEXT_OFFSET (ohdr);
 #endif
@@ -808,7 +898,7 @@ copy_text_and_data (new, a_out)
            {
              n = size > sizeof (page) ? sizeof (page) : size;
              if (read (a_out, page, n) != n || write (new, page, n) != n)
            {
              n = size > sizeof (page) ? sizeof (page) : size;
              if (read (a_out, page, n) != n || write (new, page, n) != n)
-               PERROR ("xemacs");
+               PERROR ("emacs");
            }
          lseek (a_out, old_a_out_ptr, 0);
        }
            }
          lseek (a_out, old_a_out_ptr, 0);
        }
@@ -816,6 +906,19 @@ copy_text_and_data (new, a_out)
 
 #else /* COFF, but not USG_SHARED_LIBRARIES */
 
 
 #else /* COFF, but not USG_SHARED_LIBRARIES */
 
+#ifdef MSDOS
+#if __DJGPP__ >= 2
+  /* Dump the original table of exception handlers, not the one
+     where our exception hooks are registered.  */
+  __djgpp_exception_toggle ();
+
+  /* Switch off startup flags that might have been set at runtime
+     and which might change the way that dumped Emacs works.  */
+  save_djgpp_startup_flags = _crt0_startup_flags;
+  _crt0_startup_flags &= ~(_CRT0_FLAG_NO_LFN | _CRT0_FLAG_NEARPTR);
+#endif
+#endif
+
   lseek (new, (long) text_scnptr, 0);
   ptr = (char *) f_ohdr.text_start;
 #ifdef HEADER_INCL_IN_TEXT
   lseek (new, (long) text_scnptr, 0);
   ptr = (char *) f_ohdr.text_start;
 #ifdef HEADER_INCL_IN_TEXT
@@ -830,13 +933,23 @@ copy_text_and_data (new, a_out)
   end = ptr + f_ohdr.dsize;
   write_segment (new, ptr, end);
 
   end = ptr + f_ohdr.dsize;
   write_segment (new, ptr, end);
 
+#ifdef MSDOS
+#if __DJGPP__ >= 2
+  /* Restore our exception hooks.  */
+  __djgpp_exception_toggle ();
+
+  /* Restore the startup flags.  */
+  _crt0_startup_flags = save_djgpp_startup_flags;
+#endif
+#endif
+
 #endif /* USG_SHARED_LIBRARIES */
 
 #else /* if not COFF */
 
 /* Some machines count the header as part of the text segment.
    That is to say, the header appears in core
 #endif /* USG_SHARED_LIBRARIES */
 
 #else /* if not COFF */
 
 /* 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.
+   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
    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
@@ -848,9 +961,92 @@ copy_text_and_data (new, a_out)
   lseek (new, (long) N_TXTOFF (hdr), 0);
 #endif /* no A_TEXT_SEEK */
 
   lseek (new, (long) N_TXTOFF (hdr), 0);
 #endif /* no A_TEXT_SEEK */
 
+#ifdef RISCiX
+
+  /* Acorn's RISC-iX has a wacky way of initialising the position of the heap.
+   * There is a little table in crt0.o that is filled at link time with
+   * the min and current brk positions, among other things.  When start
+   * runs, it copies the table to where these parameters live during
+   * execution.  This data is in text space, so it cannot be modified here
+   * before saving the executable, so the data is written manually.  In
+   * addition, the table does not have a label, and the nearest accessible
+   * label (mcount) is not prefixed with a '_', thus making it inaccessible
+   * from within C programs.  To overcome this, emacs's executable is passed
+   * through the command 'nm %s | fgrep mcount' into a pipe, and the
+   * resultant output is then used to find the address of 'mcount'.  As far as
+   * is possible to determine, in RISC-iX releases prior to 1.2, the negative
+   * offset of the table from mcount is 0x2c, whereas from 1.2 onwards it is
+   * 0x30.  bss_end has been rounded up to page boundary.  This solution is
+   * based on suggestions made by Kevin Welton and Steve Hunt of Acorn, and
+   * avoids the need for a custom version of crt0.o for emacs which has its
+   * table in data space.
+   */
+
+  {
+    char command[1024];
+    char errbuf[1024];
+    char address_text[32];
+    int  proforma[4];
+    FILE *pfile;
+    char *temp_ptr;
+    char c;
+    int mcount_address, mcount_offset, count;
+    extern char *_execname;
+   
+
+    /* The use of _execname is incompatible with RISCiX 1.1 */
+    sprintf (command, "nm %s | fgrep mcount", _execname);
+
+    if ( (pfile = popen(command, "r")) == NULL)
+    {
+      sprintf (errbuf, "Could not open pipe");
+      PERROR (errbuf);
+    }
+
+    count=0;
+    while ( ((c=getc(pfile)) != EOF) && (c != ' ') && (count < 31))
+      address_text[count++]=c;
+    address_text[count]=0;
+
+    if ((count == 0) || pclose(pfile) != NULL)
+    {
+      sprintf (errbuf, "Failed to execute the command '%s'\n", command);
+      PERROR (errbuf);
+    }  
+
+    sscanf(address_text, "%x", &mcount_address);
+    ptr = (char *) unexec_text_start;
+    mcount_offset = (char *)mcount_address - ptr;
+
+#ifdef RISCiX_1_1
+#define EDATA_OFFSET 0x2c
+#else
+#define EDATA_OFFSET 0x30
+#endif
+
+    end = ptr + mcount_offset - EDATA_OFFSET;
+
+    write_segment (new, ptr, end);
+
+    proforma[0] = bss_end;     /* becomes _edata */
+    proforma[1] = bss_end;     /* becomes _end */
+    proforma[2] = bss_end;     /* becomes _minbrk */
+    proforma[3] = bss_end;     /* becomes _curbrk */
+
+    write (new, proforma, 16);
+
+    temp_ptr = ptr;
+    ptr = end + 16;
+    end = temp_ptr + hdr.a_text;
+
+    write_segment (new, ptr, end);
+  }
+
+#else /* !RISCiX */
   ptr = (char *) unexec_text_start;
   end = ptr + hdr.a_text;
   write_segment (new, ptr, end);
   ptr = (char *) unexec_text_start;
   end = ptr + hdr.a_text;
   write_segment (new, ptr, end);
+#endif /* RISCiX */
 
   ptr = (char *) unexec_data_start;
   end = ptr + hdr.a_data;
 
   ptr = (char *) unexec_data_start;
   end = ptr + hdr.a_data;
@@ -871,15 +1067,21 @@ write_segment (new, ptr, end)
 {
   register int i, nwrite, ret;
   char buf[80];
 {
   register int i, nwrite, ret;
   char buf[80];
+#ifndef USE_CRT_DLL
   extern int errno;
   extern int errno;
-  char zeros[128];
+#endif
+  /* This is the normal amount to write at once.
+     It is the size of block that NFS uses.  */
+  int writesize = 1 << 13;
+  int pagesize = getpagesize ();
+  char zeros[1 << 13];
 
 
-  bzero (zeros, sizeof zeros);
+  bzero (zeros, sizeof (zeros));
 
   for (i = 0; ptr < end;)
     {
 
   for (i = 0; ptr < end;)
     {
-      /* distance to next multiple of 128.  */
-      nwrite = (((int) ptr + 128) & -128) - (int) ptr;
+      /* Distance to next multiple of writesize.  */
+      nwrite = (((int) ptr + writesize) & -writesize) - (int) ptr;
       /* But not beyond specified end.  */
       if (nwrite > end - ptr) nwrite = end - ptr;
       ret = write (new, ptr, nwrite);
       /* But not beyond specified end.  */
       if (nwrite > end - ptr) nwrite = end - ptr;
       ret = write (new, ptr, nwrite);
@@ -887,8 +1089,21 @@ write_segment (new, ptr, end)
         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.  */
         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)
-       write (new, zeros, nwrite);
+      if (ret == -1
+#ifdef EFAULT
+         && errno == EFAULT
+#endif
+         )
+       {
+         /* Write only a page of zeros at once,
+            so that we we don't overshoot the start
+            of the valid memory in the old data segment.  */
+         if (nwrite > pagesize)
+           nwrite = pagesize;
+         write (new, zeros, nwrite);
+       }
+#if 0 /* Now that we have can ask `write' to write more than a page,
+        it is legit for write do less than the whole amount specified.  */
       else if (nwrite != ret)
        {
          sprintf (buf,
       else if (nwrite != ret)
        {
          sprintf (buf,
@@ -896,6 +1111,7 @@ write_segment (new, ptr, end)
                   ptr, new, nwrite, ret, errno);
          PERROR (buf);
        }
                   ptr, new, nwrite, ret, errno);
          PERROR (buf);
        }
+#endif
       i += nwrite;
       ptr += nwrite;
     }
       i += nwrite;
       ptr += nwrite;
     }
@@ -924,10 +1140,12 @@ copy_sym (new, a_out, a_name, new_name)
 
 #ifdef COFF
   if (lnnoptr)                 /* if there is line number info */
 
 #ifdef COFF
   if (lnnoptr)                 /* if there is line number info */
-    lseek (a_out, lnnoptr, 0); /* start copying from there */
+    lseek (a_out, coff_offset + lnnoptr, 0);   /* start copying from there */
   else
   else
-#endif /* COFF */
-    lseek (a_out, SYMS_START, 0);      /* Position a.out to symtab. */
+    lseek (a_out, coff_offset + SYMS_START, 0);        /* Position a.out to symtab. */
+#else  /* not COFF */
+  lseek (a_out, SYMS_START, 0);        /* Position a.out to symtab. */
+#endif /* not COFF */
 
   while ((n = read (a_out, page, sizeof page)) > 0)
     {
 
   while ((n = read (a_out, page, sizeof page)) > 0)
     {
@@ -946,7 +1164,7 @@ copy_sym (new, a_out, a_name, new_name)
 /* ****************************************************************
  * mark_x
  *
 /* ****************************************************************
  * mark_x
  *
- * After succesfully building the new a.out, mark it executable
+ * After successfully building the new a.out, mark it executable
  */
 static void
 mark_x (name)
  */
 static void
 mark_x (name)
@@ -1010,7 +1228,11 @@ adjust_lnnoptrs (writedesc, readdesc, new_name)
   if (!lnnoptr || !f_hdr.f_symptr)
     return 0;
 
   if (!lnnoptr || !f_hdr.f_symptr)
     return 0;
 
-  if ((new = open (new_name, 2)) < 0)
+#ifdef MSDOS
+  if ((new = writedesc) < 0)
+#else
+  if ((new = open (new_name, O_RDWR)) < 0)
+#endif
     {
       PERROR (new_name);
       return -1;
     {
       PERROR (new_name);
       return -1;
@@ -1032,13 +1254,14 @@ adjust_lnnoptrs (writedesc, readdesc, new_name)
            }
        }
     }
            }
        }
     }
+#ifndef MSDOS
   close (new);
   close (new);
+#endif
+  return 0;
 }
 
 #endif /* COFF_BSD_SYMBOLS */
 
 #endif /* COFF */
 
 }
 
 #endif /* COFF_BSD_SYMBOLS */
 
 #endif /* COFF */
 
-#endif /* not CANNOT_UNEXEC */
-
 #endif /* not CANNOT_DUMP */
 #endif /* not CANNOT_DUMP */