root/src/unexcw.c

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

DEFINITIONS

This source file includes following definitions.
  1. read_exe_header
  2. fixup_executable
  3. add_exe_suffix_if_necessary
  4. unexec

     1 /* unexec() support for Cygwin;
     2    complete rewrite of xemacs Cygwin unexec() code
     3 
     4    Copyright (C) 2004-2023 Free Software Foundation, Inc.
     5 
     6 This file is part of GNU Emacs.
     7 
     8 GNU Emacs is free software: you can redistribute it and/or modify
     9 it under the terms of the GNU General Public License as published by
    10 the Free Software Foundation, either version 3 of the License, or (at
    11 your option) any later version.
    12 
    13 GNU Emacs is distributed in the hope that it will be useful,
    14 but WITHOUT ANY WARRANTY; without even the implied warranty of
    15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16 GNU General Public License for more details.
    17 
    18 You should have received a copy of the GNU General Public License
    19 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    20 
    21 #include <config.h>
    22 #include "unexec.h"
    23 #include "lisp.h"
    24 #include <stdio.h>
    25 #include <fcntl.h>
    26 #include <a.out.h>
    27 #include <unistd.h>
    28 #include <assert.h>
    29 
    30 #define DOTEXE ".exe"
    31 
    32 /*
    33 ** header for Windows executable files
    34 */
    35 typedef struct
    36 {
    37   FILHDR file_header;
    38   PEAOUTHDR file_optional_header;
    39   SCNHDR section_header[32];
    40 } exe_header_t;
    41 
    42 int debug_unexcw = 0;
    43 
    44 /*
    45 ** Read the header from the executable into memory so we can more easily access it.
    46 */
    47 static exe_header_t *
    48 read_exe_header (int fd, exe_header_t * exe_header_buffer)
    49 {
    50   int i;
    51   MAYBE_UNUSED int ret;
    52 
    53   assert (fd >= 0);
    54   assert (exe_header_buffer != 0);
    55 
    56   ret = lseek (fd, 0L, SEEK_SET);
    57   assert (ret != -1);
    58 
    59   ret =
    60     read (fd, &exe_header_buffer->file_header,
    61           sizeof (exe_header_buffer->file_header));
    62   assert (ret == sizeof (exe_header_buffer->file_header));
    63 
    64   assert (exe_header_buffer->file_header.e_magic == 0x5a4d);
    65   assert (exe_header_buffer->file_header.nt_signature == 0x4550);
    66 #ifdef __x86_64__
    67   assert (exe_header_buffer->file_header.f_magic == 0x8664);
    68 #else
    69   assert (exe_header_buffer->file_header.f_magic == 0x014c);
    70 #endif
    71   assert (exe_header_buffer->file_header.f_nscns > 0);
    72   assert (exe_header_buffer->file_header.f_nscns <=
    73           ARRAYELTS (exe_header_buffer->section_header));
    74   assert (exe_header_buffer->file_header.f_opthdr > 0);
    75 
    76   ret =
    77     read (fd, &exe_header_buffer->file_optional_header,
    78           sizeof (exe_header_buffer->file_optional_header));
    79   assert (ret == sizeof (exe_header_buffer->file_optional_header));
    80 
    81 #ifdef __x86_64__
    82   assert (exe_header_buffer->file_optional_header.magic == 0x020b);
    83 #else
    84   assert (exe_header_buffer->file_optional_header.magic == 0x010b);
    85 #endif
    86 
    87   for (i = 0; i < exe_header_buffer->file_header.f_nscns; ++i)
    88     {
    89       ret =
    90         read (fd, &exe_header_buffer->section_header[i],
    91               sizeof (exe_header_buffer->section_header[i]));
    92       assert (ret == sizeof (exe_header_buffer->section_header[i]));
    93     }
    94 
    95   return (exe_header_buffer);
    96 }
    97 
    98 /*
    99 ** Fix the dumped emacs executable:
   100 **
   101 ** - copy .data section data of interest from running executable into
   102 **   output .exe file
   103 **
   104 ** - convert .bss section into an initialized data section (like
   105 **   .data) and copy .bss section data of interest from running
   106 **   executable into output .exe file
   107 */
   108 static void
   109 fixup_executable (int fd)
   110 {
   111   exe_header_t exe_header_buffer;
   112   exe_header_t *exe_header;
   113   int i;
   114   MAYBE_UNUSED int ret;
   115   int found_data = 0;
   116   int found_bss = 0;
   117 
   118   exe_header = read_exe_header (fd, &exe_header_buffer);
   119   assert (exe_header != 0);
   120 
   121   assert (exe_header->file_header.f_nscns > 0);
   122   for (i = 0; i < exe_header->file_header.f_nscns; ++i)
   123     {
   124       unsigned long start_address =
   125         exe_header->section_header[i].s_vaddr +
   126         exe_header->file_optional_header.ImageBase;
   127       unsigned long end_address =
   128         exe_header->section_header[i].s_vaddr +
   129         exe_header->file_optional_header.ImageBase +
   130         exe_header->section_header[i].s_paddr;
   131       if (debug_unexcw)
   132         printf ("%8s start %#lx end %#lx\n",
   133                 exe_header->section_header[i].s_name,
   134                 start_address, end_address);
   135       if (my_edata >= (char *) start_address
   136           && my_edata < (char *) end_address)
   137         {
   138           /* data section */
   139           ret =
   140             lseek (fd, (long) (exe_header->section_header[i].s_scnptr),
   141                    SEEK_SET);
   142           assert (ret != -1);
   143           ret =
   144             write (fd, (char *) start_address,
   145                    my_edata - (char *) start_address);
   146           assert (ret == my_edata - (char *) start_address);
   147           ++found_data;
   148           if (debug_unexcw)
   149             printf ("         .data, mem start %#lx mem length %td\n",
   150                     start_address, my_edata - (char *) start_address);
   151           if (debug_unexcw)
   152             printf ("         .data, file start %d file length %d\n",
   153                     (int) exe_header->section_header[i].s_scnptr,
   154                     (int) exe_header->section_header[i].s_paddr);
   155         }
   156       else if (my_endbss >= (char *) start_address
   157                && my_endbss < (char *) end_address)
   158         {
   159           /* bss section */
   160           ++found_bss;
   161           if (exe_header->section_header[i].s_flags & 0x00000080)
   162             {
   163               /* convert uninitialized data section to initialized data section */
   164               struct stat statbuf;
   165               ret = fstat (fd, &statbuf);
   166               assert (ret != -1);
   167 
   168               exe_header->section_header[i].s_flags &= ~0x00000080;
   169               exe_header->section_header[i].s_flags |= 0x00000040;
   170 
   171               exe_header->section_header[i].s_scnptr =
   172                 (statbuf.st_size +
   173                  exe_header->file_optional_header.FileAlignment) /
   174                 exe_header->file_optional_header.FileAlignment *
   175                 exe_header->file_optional_header.FileAlignment;
   176 
   177               exe_header->section_header[i].s_size =
   178                 (exe_header->section_header[i].s_paddr +
   179                  exe_header->file_optional_header.FileAlignment) /
   180                 exe_header->file_optional_header.FileAlignment *
   181                 exe_header->file_optional_header.FileAlignment;
   182 
   183               /* Make sure the generated bootstrap binary isn't
   184                * sparse.  NT doesn't use a file cache for sparse
   185                * executables, so if we bootstrap Emacs using a sparse
   186                * bootstrap-emacs.exe, bootstrap takes about twenty
   187                * times longer than it would otherwise.  */
   188 
   189               ret = posix_fallocate (fd,
   190                                      ( exe_header->section_header[i].s_scnptr +
   191                                        exe_header->section_header[i].s_size ),
   192                                      1);
   193 
   194               assert (ret != -1);
   195 
   196               ret =
   197                 lseek (fd,
   198                        (long) (exe_header->section_header[i].s_scnptr +
   199                                exe_header->section_header[i].s_size - 1),
   200                        SEEK_SET);
   201               assert (ret != -1);
   202               ret = write (fd, "", 1);
   203               assert (ret == 1);
   204 
   205               ret =
   206                 lseek (fd,
   207                        (long) ((char *) &exe_header->section_header[i] -
   208                                (char *) exe_header), SEEK_SET);
   209               assert (ret != -1);
   210               ret =
   211                 write (fd, &exe_header->section_header[i],
   212                        sizeof (exe_header->section_header[i]));
   213               assert (ret == sizeof (exe_header->section_header[i]));
   214               if (debug_unexcw)
   215                 printf ("         seek to %ld, write %zu\n",
   216                         (long) ((char *) &exe_header->section_header[i] -
   217                                 (char *) exe_header),
   218                         sizeof (exe_header->section_header[i]));
   219             }
   220           /* write initialized data section */
   221           ret =
   222             lseek (fd, (long) (exe_header->section_header[i].s_scnptr),
   223                    SEEK_SET);
   224           assert (ret != -1);
   225           ret =
   226             write (fd, (char *) start_address,
   227                    my_endbss - (char *) start_address);
   228           assert (ret == (my_endbss - (char *) start_address));
   229           if (debug_unexcw)
   230             printf ("         .bss, mem start %#lx mem length %td\n",
   231                     start_address, my_endbss - (char *) start_address);
   232           if (debug_unexcw)
   233             printf ("         .bss, file start %d file length %d\n",
   234                     (int) exe_header->section_header[i].s_scnptr,
   235                     (int) exe_header->section_header[i].s_paddr);
   236         }
   237     }
   238   assert (found_bss == 1);
   239   assert (found_data == 1);
   240 }
   241 
   242 /*
   243 ** Windows likes .exe suffixes on executables.
   244 */
   245 static char *
   246 add_exe_suffix_if_necessary (const char *name, char *modified)
   247 {
   248   int i = strlen (name);
   249   if (i <= (sizeof (DOTEXE) - 1))
   250     {
   251       sprintf (modified, "%s%s", name, DOTEXE);
   252     }
   253   else if (!strcasecmp (name + i - (sizeof (DOTEXE) - 1), DOTEXE))
   254     {
   255       strcpy (modified, name);
   256     }
   257   else
   258     {
   259       sprintf (modified, "%s%s", name, DOTEXE);
   260     }
   261   return (modified);
   262 }
   263 
   264 void
   265 unexec (const char *outfile, const char *infile)
   266 {
   267   char infile_buffer[FILENAME_MAX];
   268   char outfile_buffer[FILENAME_MAX];
   269   int fd_in;
   270   int fd_out;
   271   int ret;
   272   MAYBE_UNUSED int ret2;
   273 
   274   infile = add_exe_suffix_if_necessary (infile, infile_buffer);
   275   outfile = add_exe_suffix_if_necessary (outfile, outfile_buffer);
   276 
   277   fd_in = emacs_open (infile, O_RDONLY, 0);
   278   assert (fd_in >= 0);
   279   fd_out = emacs_open (outfile, O_RDWR | O_TRUNC | O_CREAT, 0755);
   280   assert (fd_out >= 0);
   281   for (;;)
   282     {
   283       char buffer[4096];
   284       ret = read (fd_in, buffer, sizeof (buffer));
   285       if (ret == 0)
   286         {
   287           /* eof */
   288           break;
   289         }
   290       assert (ret > 0);
   291       /* data */
   292       ret2 = write (fd_out, buffer, ret);
   293       assert (ret2 == ret);
   294     }
   295   ret = emacs_close (fd_in);
   296   assert (ret == 0);
   297 
   298   fixup_executable (fd_out);
   299 
   300   ret = emacs_close (fd_out);
   301   assert (ret == 0);
   302 }

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