root/src/unexcoff.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. report_error
  2. report_error_1
  3. make_hdr
  4. write_segment
  5. copy_text_and_data
  6. copy_sym
  7. adjust_lnnoptrs
  8. unexec

     1 /* Copyright (C) 1985-1988, 1992-1994, 2001-2023 Free Software
     2  * Foundation, Inc.
     3 
     4 This file is part of GNU Emacs.
     5 
     6 GNU Emacs is free software: you can redistribute it and/or modify
     7 it under the terms of the GNU General Public License as published by
     8 the Free Software Foundation, either version 3 of the License, or (at
     9 your option) any later version.
    10 
    11 GNU Emacs is distributed in the hope that it will be useful,
    12 but WITHOUT ANY WARRANTY; without even the implied warranty of
    13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14 GNU General Public License for more details.
    15 
    16 You should have received a copy of the GNU General Public License
    17 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    18 
    19 
    20 /*
    21  * unexcoff.c - Convert a running program into an a.out or COFF file.
    22  *
    23  * ==================================================================
    24  * Note: This file is currently used only by the MSDOS (a.k.a. DJGPP)
    25  * build of Emacs.  If you are not interested in the MSDOS build, you
    26  * are looking at the wrong version of unexec!
    27  * ==================================================================
    28  *
    29  * Author:      Spencer W. Thomas
    30  *              Computer Science Dept.
    31  *              University of Utah
    32  * Date:        Tue Mar  2 1982
    33  * Originally under the name unexec.c.
    34  * Modified heavily since then.
    35  *
    36  * Synopsis:
    37  *      unexec (const char *new_name, const char *old_name);
    38  *
    39  * Takes a snapshot of the program and makes an a.out format file in the
    40  * file named by the string argument new_name.
    41  * If a_name is non-NULL, the symbol table will be taken from the given file.
    42  * On some machines, an existing a_name file is required.
    43  *
    44  * If you make improvements I'd like to get them too.
    45  * harpo!utah-cs!thomas, thomas@Utah-20
    46  *
    47  */
    48 
    49 /* Modified to support SysVr3 shared libraries by James Van Artsdalen
    50  * of Dell Computer Corporation.  james@bigtex.cactus.org.
    51  */
    52 
    53 #include <config.h>
    54 #include "unexec.h"
    55 #include "lisp.h"
    56 
    57 #define PERROR(file) report_error (file, new)
    58 
    59 #ifdef HAVE_UNEXEC  /* all rest of file!  */
    60 
    61 #ifdef HAVE_COFF_H
    62 #include <coff.h>
    63 #ifdef MSDOS
    64 #include <fcntl.h>  /* for O_RDONLY, O_RDWR */
    65 #include <crt0.h>   /* for _crt0_startup_flags and its bits */
    66 #include <sys/exceptn.h>
    67 static int save_djgpp_startup_flags;
    68 #include <libc/atexit.h>
    69 static struct __atexit *save_atexit_ptr;
    70 #define filehdr external_filehdr
    71 #define scnhdr external_scnhdr
    72 #define syment external_syment
    73 #define auxent external_auxent
    74 #define n_numaux e_numaux
    75 #define n_type e_type
    76 struct aouthdr
    77 {
    78   unsigned short        magic;  /* type of file                         */
    79   unsigned short        vstamp; /* version stamp                        */
    80   unsigned long         tsize;  /* text size in bytes, padded to FW bdry*/
    81   unsigned long         dsize;  /* initialized data "  "                */
    82   unsigned long         bsize;  /* uninitialized data "   "             */
    83   unsigned long         entry;  /* entry pt.                            */
    84   unsigned long         text_start;/* base of text used for this file */
    85   unsigned long         data_start;/* base of data used for this file */
    86 };
    87 #endif /* MSDOS */
    88 #else  /* not HAVE_COFF_H */
    89 #include <a.out.h>
    90 #endif /* not HAVE_COFF_H */
    91 
    92 /* Define getpagesize if the system does not.
    93    Note that this may depend on symbols defined in a.out.h.  */
    94 #include "getpagesize.h"
    95 
    96 #ifndef makedev                 /* Try to detect types.h already loaded */
    97 #include <sys/types.h>
    98 #endif /* makedev */
    99 #include <errno.h>
   100 
   101 #include <sys/file.h>
   102 
   103 extern int etext;
   104 
   105 static long block_copy_start;           /* Old executable start point */
   106 static struct filehdr f_hdr;            /* File header */
   107 static struct aouthdr f_ohdr;           /* Optional file header (a.out) */
   108 long bias;                      /* Bias to add for growth */
   109 long lnnoptr;                   /* Pointer to line-number info within file */
   110 #define SYMS_START block_copy_start
   111 
   112 static long text_scnptr;
   113 static long data_scnptr;
   114 
   115 static long coff_offset;
   116 
   117 static int pagemask;
   118 
   119 /* Correct an int which is the bit pattern of a pointer to a byte
   120    into an int which is the number of a byte.
   121    This is a no-op on ordinary machines, but not on all.  */
   122 
   123 #define ADDR_CORRECT(x) ((char *) (x) - (char *) 0)
   124 
   125 #include "lisp.h"
   126 
   127 static void
   128 report_error (const char *file, int fd)
   129 {
   130   int err = errno;
   131   if (fd)
   132     emacs_close (fd);
   133   report_file_errno ("Cannot unexec", build_string (file), err);
   134 }
   135 
   136 #define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
   137 #define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
   138 #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1
   139 
   140 static void
   141 report_error_1 (int fd, const char *msg, int a1, int a2)
   142 {
   143   emacs_close (fd);
   144   error (msg, a1, a2);
   145 }
   146 
   147 static int make_hdr (int, int, const char *, const char *);
   148 static int copy_text_and_data (int, int);
   149 static int copy_sym (int, int, const char *, const char *);
   150 static void mark_x (const char *);
   151 
   152 /* ****************************************************************
   153  * make_hdr
   154  *
   155  * Make the header in the new a.out from the header in core.
   156  * Modify the text and data sizes.
   157  */
   158 static int
   159 make_hdr (int new, int a_out,
   160           const char *a_name, const char *new_name)
   161 {
   162   auto struct scnhdr f_thdr;            /* Text section header */
   163   auto struct scnhdr f_dhdr;            /* Data section header */
   164   auto struct scnhdr f_bhdr;            /* Bss section header */
   165   auto struct scnhdr scntemp;           /* Temporary section header */
   166   register int scns;
   167   unsigned int bss_start;
   168   unsigned int data_start;
   169 
   170   pagemask = getpagesize () - 1;
   171 
   172   /* Adjust text/data boundary. */
   173   data_start = (int) DATA_START;
   174   data_start = ADDR_CORRECT (data_start);
   175   data_start = data_start & ~pagemask; /* (Down) to page boundary. */
   176 
   177   bss_start = ADDR_CORRECT (sbrk (0)) + pagemask;
   178   bss_start &= ~ pagemask;
   179 
   180   if (data_start > bss_start)   /* Can't have negative data size. */
   181     {
   182       ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
   183               data_start, bss_start);
   184     }
   185 
   186   coff_offset = 0L;             /* stays zero, except in DJGPP */
   187 
   188   /* Salvage as much info from the existing file as possible */
   189   if (a_out >= 0)
   190     {
   191 #ifdef MSDOS
   192       /* Support the coff-go32-exe format with a prepended stub, since
   193          this is what GCC 2.8.0 and later generates by default in DJGPP.  */
   194       unsigned short mz_header[3];
   195 
   196       if (read (a_out, &mz_header, sizeof (mz_header)) != sizeof (mz_header))
   197         {
   198           PERROR (a_name);
   199         }
   200       if (mz_header[0] == 0x5a4d || mz_header[0] == 0x4d5a) /* "MZ" or "ZM" */
   201         {
   202           coff_offset = (long)mz_header[2] * 512L;
   203           if (mz_header[1])
   204             coff_offset += (long)mz_header[1] - 512L;
   205           lseek (a_out, coff_offset, 0);
   206         }
   207       else
   208         lseek (a_out, 0L, 0);
   209 #endif /* MSDOS */
   210       if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
   211         {
   212           PERROR (a_name);
   213         }
   214       block_copy_start += sizeof (f_hdr);
   215       if (f_hdr.f_opthdr > 0)
   216         {
   217           if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
   218             {
   219               PERROR (a_name);
   220             }
   221           block_copy_start += sizeof (f_ohdr);
   222         }
   223       /* Loop through section headers, copying them in */
   224       lseek (a_out, coff_offset + sizeof (f_hdr) + f_hdr.f_opthdr, 0);
   225       for (scns = f_hdr.f_nscns; scns > 0; scns--) {
   226         if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp))
   227           {
   228             PERROR (a_name);
   229           }
   230         if (scntemp.s_scnptr > 0L)
   231           {
   232             if (block_copy_start < scntemp.s_scnptr + scntemp.s_size)
   233               block_copy_start = scntemp.s_scnptr + scntemp.s_size;
   234           }
   235         if (strcmp (scntemp.s_name, ".text") == 0)
   236           {
   237             f_thdr = scntemp;
   238           }
   239         else if (strcmp (scntemp.s_name, ".data") == 0)
   240           {
   241             f_dhdr = scntemp;
   242           }
   243         else if (strcmp (scntemp.s_name, ".bss") == 0)
   244           {
   245             f_bhdr = scntemp;
   246           }
   247       }
   248     }
   249   else
   250     {
   251       ERROR0 ("can't build a COFF file from scratch yet");
   252     }
   253 
   254   /* Now we alter the contents of all the f_*hdr variables
   255      to correspond to what we want to dump.  */
   256 
   257   f_hdr.f_flags |= (F_RELFLG | F_EXEC);
   258   f_ohdr.dsize = bss_start - f_ohdr.data_start;
   259   f_ohdr.bsize = 0;
   260   f_thdr.s_size = f_ohdr.tsize;
   261   f_thdr.s_scnptr = sizeof (f_hdr) + sizeof (f_ohdr);
   262   f_thdr.s_scnptr += (f_hdr.f_nscns) * (sizeof (f_thdr));
   263   lnnoptr = f_thdr.s_lnnoptr;
   264   text_scnptr = f_thdr.s_scnptr;
   265   f_dhdr.s_paddr = f_ohdr.data_start;
   266   f_dhdr.s_vaddr = f_ohdr.data_start;
   267   f_dhdr.s_size = f_ohdr.dsize;
   268   f_dhdr.s_scnptr = f_thdr.s_scnptr + f_thdr.s_size;
   269   data_scnptr = f_dhdr.s_scnptr;
   270   f_bhdr.s_paddr = f_ohdr.data_start + f_ohdr.dsize;
   271   f_bhdr.s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
   272   f_bhdr.s_size = f_ohdr.bsize;
   273   f_bhdr.s_scnptr = 0L;
   274   bias = f_dhdr.s_scnptr + f_dhdr.s_size - block_copy_start;
   275 
   276   if (f_hdr.f_symptr > 0L)
   277     {
   278       f_hdr.f_symptr += bias;
   279     }
   280 
   281   if (f_thdr.s_lnnoptr > 0L)
   282     {
   283       f_thdr.s_lnnoptr += bias;
   284     }
   285 
   286   if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
   287     {
   288       PERROR (new_name);
   289     }
   290 
   291   if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
   292     {
   293       PERROR (new_name);
   294     }
   295 
   296   if (write (new, &f_thdr, sizeof (f_thdr)) != sizeof (f_thdr))
   297     {
   298       PERROR (new_name);
   299     }
   300 
   301   if (write (new, &f_dhdr, sizeof (f_dhdr)) != sizeof (f_dhdr))
   302     {
   303       PERROR (new_name);
   304     }
   305 
   306   if (write (new, &f_bhdr, sizeof (f_bhdr)) != sizeof (f_bhdr))
   307     {
   308       PERROR (new_name);
   309     }
   310 
   311   return (0);
   312 
   313 }
   314 
   315 void
   316 write_segment (int new, const char *ptr, const char *end)
   317 {
   318   register int i, nwrite, ret;
   319   /* This is the normal amount to write at once.
   320      It is the size of block that NFS uses.  */
   321   int writesize = 1 << 13;
   322   int pagesize = getpagesize ();
   323   char zeros[1 << 13];
   324 
   325   memset (zeros, 0, sizeof (zeros));
   326 
   327   for (i = 0; ptr < end;)
   328     {
   329       /* Distance to next multiple of writesize.  */
   330       nwrite = (((int) ptr + writesize) & -writesize) - (int) ptr;
   331       /* But not beyond specified end.  */
   332       if (nwrite > end - ptr) nwrite = end - ptr;
   333       ret = write (new, ptr, nwrite);
   334       /* If write gets a page fault, it means we reached
   335          a gap between the old text segment and the old data segment.
   336          This gap has probably been remapped into part of the text segment.
   337          So write zeros for it.  */
   338       if (ret == -1 && errno == EFAULT)
   339         {
   340           /* Write only a page of zeros at once,
   341              so that we don't overshoot the start
   342              of the valid memory in the old data segment.  */
   343           if (nwrite > pagesize)
   344             nwrite = pagesize;
   345           write (new, zeros, nwrite);
   346         }
   347       i += nwrite;
   348       ptr += nwrite;
   349     }
   350 }
   351 /* ****************************************************************
   352  * copy_text_and_data
   353  *
   354  * Copy the text and data segments from memory to the new a.out
   355  */
   356 static int
   357 copy_text_and_data (int new, int a_out)
   358 {
   359   register char *end;
   360   register char *ptr;
   361 
   362 #ifdef MSDOS
   363   /* Dump the original table of exception handlers, not the one
   364      where our exception hooks are registered.  */
   365   __djgpp_exception_toggle ();
   366 
   367   /* Switch off startup flags that might have been set at runtime
   368      and which might change the way that dumped Emacs works.  */
   369   save_djgpp_startup_flags = _crt0_startup_flags;
   370   _crt0_startup_flags &= ~(_CRT0_FLAG_NO_LFN | _CRT0_FLAG_NEARPTR);
   371 
   372   /* Zero out the 'atexit' chain in the dumped executable, to avoid
   373      calling the atexit functions twice.  (emacs.c:main installs an
   374      atexit function.)  */
   375   save_atexit_ptr = __atexit_ptr;
   376   __atexit_ptr = NULL;
   377 #endif
   378 
   379   lseek (new, (long) text_scnptr, 0);
   380   ptr = (char *) f_ohdr.text_start;
   381   end = ptr + f_ohdr.tsize;
   382   write_segment (new, ptr, end);
   383 
   384   lseek (new, (long) data_scnptr, 0);
   385   ptr = (char *) f_ohdr.data_start;
   386   end = ptr + f_ohdr.dsize;
   387   write_segment (new, ptr, end);
   388 
   389 #ifdef MSDOS
   390   /* Restore our exception hooks.  */
   391   __djgpp_exception_toggle ();
   392 
   393   /* Restore the startup flags.  */
   394   _crt0_startup_flags = save_djgpp_startup_flags;
   395 
   396   /* Restore the atexit chain.  */
   397   __atexit_ptr = save_atexit_ptr;
   398 #endif
   399 
   400 
   401   return 0;
   402 }
   403 
   404 /* ****************************************************************
   405  * copy_sym
   406  *
   407  * Copy the relocation information and symbol table from the a.out to the new
   408  */
   409 static int
   410 copy_sym (int new, int a_out, const char *a_name, const char *new_name)
   411 {
   412   char page[1024];
   413   int n;
   414 
   415   if (a_out < 0)
   416     return 0;
   417 
   418   if (SYMS_START == 0L)
   419     return 0;
   420 
   421   if (lnnoptr)                  /* if there is line number info */
   422     lseek (a_out, coff_offset + lnnoptr, 0);    /* start copying from there */
   423   else
   424     lseek (a_out, coff_offset + SYMS_START, 0); /* Position a.out to symtab. */
   425 
   426   while ((n = read (a_out, page, sizeof page)) > 0)
   427     {
   428       if (write (new, page, n) != n)
   429         {
   430           PERROR (new_name);
   431         }
   432     }
   433   if (n < 0)
   434     {
   435       PERROR (a_name);
   436     }
   437   return 0;
   438 }
   439 
   440 
   441 /*
   442  *      If the COFF file contains a symbol table and a line number section,
   443  *      then any auxiliary entries that have values for x_lnnoptr must
   444  *      be adjusted by the amount that the line number section has moved
   445  *      in the file (bias computed in make_hdr).  The #@$%&* designers of
   446  *      the auxiliary entry structures used the absolute file offsets for
   447  *      the line number entry rather than an offset from the start of the
   448  *      line number section!
   449  *
   450  *      When I figure out how to scan through the symbol table and pick out
   451  *      the auxiliary entries that need adjustment, this routine will
   452  *      be fixed.  As it is now, all such entries are wrong and sdb
   453  *      will complain.   Fred Fish, UniSoft Systems Inc.
   454  */
   455 
   456 /* This function is probably very slow.  Instead of reopening the new
   457    file for input and output it should copy from the old to the new
   458    using the two descriptors already open (WRITEDESC and READDESC).
   459    Instead of reading one small structure at a time it should use
   460    a reasonable size buffer.  But I don't have time to work on such
   461    things, so I am installing it as submitted to me.  -- RMS.  */
   462 
   463 int
   464 adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name)
   465 {
   466   register int nsyms;
   467   register int new;
   468   struct syment symentry;
   469   union auxent auxentry;
   470 
   471   if (!lnnoptr || !f_hdr.f_symptr)
   472     return 0;
   473 
   474 #ifdef MSDOS
   475   if ((new = writedesc) < 0)
   476 #else
   477   if ((new = emacs_open (new_name, O_RDWR, 0)) < 0)
   478 #endif
   479     {
   480       PERROR (new_name);
   481       return -1;
   482     }
   483 
   484   lseek (new, f_hdr.f_symptr, 0);
   485   for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
   486     {
   487       read (new, &symentry, SYMESZ);
   488       if (symentry.n_numaux)
   489         {
   490           read (new, &auxentry, AUXESZ);
   491           nsyms++;
   492           if (ISFCN (symentry.n_type) || symentry.n_type == 0x2400)
   493             {
   494               auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
   495               lseek (new, -AUXESZ, 1);
   496               write (new, &auxentry, AUXESZ);
   497             }
   498         }
   499     }
   500 #ifndef MSDOS
   501   emacs_close (new);
   502 #endif
   503   return 0;
   504 }
   505 
   506 /* ****************************************************************
   507  * unexec
   508  *
   509  * driving logic.
   510  */
   511 void
   512 unexec (const char *new_name, const char *a_name)
   513 {
   514   int new = -1, a_out = -1;
   515 
   516   if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0)
   517     {
   518       PERROR (a_name);
   519     }
   520   if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0777)) < 0)
   521     {
   522       PERROR (new_name);
   523     }
   524 
   525   if (make_hdr (new, a_out, a_name, new_name) < 0
   526       || copy_text_and_data (new, a_out) < 0
   527       || copy_sym (new, a_out, a_name, new_name) < 0
   528       || adjust_lnnoptrs (new, a_out, new_name) < 0
   529       )
   530     {
   531       emacs_close (new);
   532       return;
   533     }
   534 
   535   emacs_close (new);
   536   if (a_out >= 0)
   537     emacs_close (a_out);
   538 }
   539 
   540 #endif /* HAVE_UNEXEC */

/* [<][>][^][v][top][bottom][index][help] */