1 /* unexec() support for Cygwin;
  2    complete rewrite of xemacs Cygwin unexec() code
  3 
  4    Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 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
 11 (at 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 <http://www.gnu.org/licenses/>.  */
 20 
 21 #include <config.h>
 22 #include <setjmp.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 extern int bss_sbrk_did_unexec;
 33 
 34 /* emacs symbols that indicate where bss and data end for emacs internals */
 35 extern char my_endbss[];
 36 extern char my_edata[];
 37 
 38 /*
 39 ** header for Windows executable files
 40 */
 41 typedef struct
 42 {
 43   FILHDR file_header;
 44   PEAOUTHDR file_optional_header;
 45   SCNHDR section_header[32];
 46 } exe_header_t;
 47 
 48 int debug_unexcw = 0;
 49 
 50 /*
 51 ** Read the header from the executable into memory so we can more easily access it.
 52 */
 53 static exe_header_t *
 54 read_exe_header (int fd, exe_header_t * exe_header_buffer)
 55 {
 56   int i;
 57   int ret;
 58 
 59   assert (fd >= 0);
 60   assert (exe_header_buffer != 0);
 61 
 62   ret = lseek (fd, 0L, SEEK_SET);
 63   assert (ret != -1);
 64 
 65   ret =
 66     read (fd, &exe_header_buffer->file_header,
 67           sizeof (exe_header_buffer->file_header));
 68   assert (ret == sizeof (exe_header_buffer->file_header));
 69 
 70   assert (exe_header_buffer->file_header.e_magic == 0x5a4d);
 71   assert (exe_header_buffer->file_header.nt_signature == 0x4550);
 72   assert (exe_header_buffer->file_header.f_magic == 0x014c);
 73   assert (exe_header_buffer->file_header.f_nscns > 0);
 74   assert (exe_header_buffer->file_header.f_nscns <=
 75           sizeof (exe_header_buffer->section_header) /
 76           sizeof (exe_header_buffer->section_header[0]));
 77   assert (exe_header_buffer->file_header.f_opthdr > 0);
 78 
 79   ret =
 80     read (fd, &exe_header_buffer->file_optional_header,
 81           sizeof (exe_header_buffer->file_optional_header));
 82   assert (ret == sizeof (exe_header_buffer->file_optional_header));
 83 
 84   assert (exe_header_buffer->file_optional_header.magic == 0x010b);
 85 
 86   for (i = 0; i < exe_header_buffer->file_header.f_nscns; ++i)
 87     {
 88       ret =
 89         read (fd, &exe_header_buffer->section_header[i],
 90               sizeof (exe_header_buffer->section_header[i]));
 91       assert (ret == sizeof (exe_header_buffer->section_header[i]));
 92     }
 93 
 94   return (exe_header_buffer);
 95 }
 96 
 97 /*
 98 ** Fix the dumped emacs executable:
 99 **
100 ** - copy .data section data of interest from running executable into
101 **   output .exe file
102 **
103 ** - convert .bss section into an initialized data section (like
104 **   .data) and copy .bss section data of interest from running
105 **   executable into output .exe file
106 */
107 static void
108 fixup_executable (int fd)
109 {
110   exe_header_t exe_header_buffer;
111   exe_header_t *exe_header;
112   int i;
113   int ret;
114   int found_data = 0;
115   int found_bss = 0;
116 
117   exe_header = read_exe_header (fd, &exe_header_buffer);
118   assert (exe_header != 0);
119 
120   assert (exe_header->file_header.f_nscns > 0);
121   for (i = 0; i < exe_header->file_header.f_nscns; ++i)
122     {
123       unsigned long start_address =
124         exe_header->section_header[i].s_vaddr +
125         exe_header->file_optional_header.ImageBase;
126       unsigned long end_address =
127         exe_header->section_header[i].s_vaddr +
128         exe_header->file_optional_header.ImageBase +
129         exe_header->section_header[i].s_paddr;
130       if (debug_unexcw)
131         printf ("%8s start 0x%08x end 0x%08x\n",
132                 exe_header->section_header[i].s_name,
133                 start_address, end_address);
134       if (my_edata >= (char *) start_address
135           && my_edata < (char *) end_address)
136         {
137           /* data section */
138           ret =
139             lseek (fd, (long) (exe_header->section_header[i].s_scnptr),
140                    SEEK_SET);
141           assert (ret != -1);
142           ret =
143             write (fd, (char *) start_address,
144                    my_edata - (char *) start_address);
145           assert (ret == my_edata - (char *) start_address);
146           ++found_data;
147           if (debug_unexcw)
148             printf ("         .data, mem start 0x%08x mem length %d\n",
149                     start_address, my_edata - (char *) start_address);
150           if (debug_unexcw)
151             printf ("         .data, file start %d file length %d\n",
152                     (int) exe_header->section_header[i].s_scnptr,
153                     (int) exe_header->section_header[i].s_paddr);
154         }
155       else if (my_endbss >= (char *) start_address
156                && my_endbss < (char *) end_address)
157         {
158           /* bss section */
159           ++found_bss;
160           if (exe_header->section_header[i].s_flags & 0x00000080)
161             {
162               /* convert uninitialized data section to initialized data section */
163               struct stat statbuf;
164               ret = fstat (fd, &statbuf);
165               assert (ret != -1);
166 
167               exe_header->section_header[i].s_flags &= ~0x00000080;
168               exe_header->section_header[i].s_flags |= 0x00000040;
169 
170               exe_header->section_header[i].s_scnptr =
171                 (statbuf.st_size +
172                  exe_header->file_optional_header.FileAlignment) /
173                 exe_header->file_optional_header.FileAlignment *
174                 exe_header->file_optional_header.FileAlignment;
175 
176               exe_header->section_header[i].s_size =
177                 (exe_header->section_header[i].s_paddr +
178                  exe_header->file_optional_header.FileAlignment) /
179                 exe_header->file_optional_header.FileAlignment *
180                 exe_header->file_optional_header.FileAlignment;
181 
182               ret =
183                 lseek (fd,
184                        (long) (exe_header->section_header[i].s_scnptr +
185                                exe_header->section_header[i].s_size - 1),
186                        SEEK_SET);
187               assert (ret != -1);
188               ret = write (fd, "", 1);
189               assert (ret == 1);
190 
191               ret =
192                 lseek (fd,
193                        (long) ((char *) &exe_header->section_header[i] -
194                                (char *) exe_header), SEEK_SET);
195               assert (ret != -1);
196               ret =
197                 write (fd, &exe_header->section_header[i],
198                        sizeof (exe_header->section_header[i]));
199               assert (ret == sizeof (exe_header->section_header[i]));
200               if (debug_unexcw)
201                 printf ("         seek to %ld, write %d\n",
202                         (long) ((char *) &exe_header->section_header[i] -
203                                 (char *) exe_header),
204                         sizeof (exe_header->section_header[i]));
205             }
206           /* write initialized data section */
207           ret =
208             lseek (fd, (long) (exe_header->section_header[i].s_scnptr),
209                    SEEK_SET);
210           assert (ret != -1);
211           ret =
212             write (fd, (char *) start_address,
213                    my_endbss - (char *) start_address);
214           assert (ret == (my_endbss - (char *) start_address));
215           if (debug_unexcw)
216             printf ("         .bss, mem start 0x%08x mem length %d\n",
217                     start_address, my_endbss - (char *) start_address);
218           if (debug_unexcw)
219             printf ("         .bss, file start %d file length %d\n",
220                     (int) exe_header->section_header[i].s_scnptr,
221                     (int) exe_header->section_header[i].s_paddr);
222         }
223     }
224   assert (found_bss == 1);
225   assert (found_data == 1);
226 }
227 
228 /*
229 ** Windows likes .exe suffixes on executables.
230 */
231 static char *
232 add_exe_suffix_if_necessary (const char *name, char *modified)
233 {
234   int i = strlen (name);
235   if (i <= (sizeof (DOTEXE) - 1))
236     {
237       sprintf (modified, "%s%s", name, DOTEXE);
238     }
239   else if (!strcasecmp (name + i - (sizeof (DOTEXE) - 1), DOTEXE))
240     {
241       strcpy (modified, name);
242     }
243   else
244     {
245       sprintf (modified, "%s%s", name, DOTEXE);
246     }
247   return (modified);
248 }
249 
250 int
251 unexec (char *outfile, char *infile, unsigned start_data, unsigned d1,
252         unsigned d2)
253 {
254   char infile_buffer[FILENAME_MAX];
255   char outfile_buffer[FILENAME_MAX];
256   int fd_in;
257   int fd_out;
258   int ret;
259   int ret2;
260 
261   if (bss_sbrk_did_unexec)
262     {
263       /* can only dump once */
264       printf ("You can only dump Emacs once on this platform.\n");
265       return (1);
266     }
267 
268   report_sheap_usage (1);
269 
270   infile = add_exe_suffix_if_necessary (infile, infile_buffer);
271   outfile = add_exe_suffix_if_necessary (outfile, outfile_buffer);
272 
273   fd_in = open (infile, O_RDONLY | O_BINARY);
274   assert (fd_in >= 0);
275   fd_out = open (outfile, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, 0755);
276   assert (fd_out >= 0);
277   for (;;)
278     {
279       char buffer[4096];
280       ret = read (fd_in, buffer, sizeof (buffer));
281       if (ret == 0)
282         {
283           /* eof */
284           break;
285         }
286       assert (ret > 0);
287       /* data */
288       ret2 = write (fd_out, buffer, ret);
289       assert (ret2 == ret);
290     }
291   ret = close (fd_in);
292   assert (ret == 0);
293 
294   bss_sbrk_did_unexec = 1;
295   fixup_executable (fd_out);
296   bss_sbrk_did_unexec = 0;
297 
298   ret = close (fd_out);
299   assert (ret == 0);
300 
301   return (0);
302 }
303 
304 /* arch-tag: fc44f6c3-ca0a-45e0-a5a2-58b6101b1e65
305    (do not change this comment) */