1/* unexec() support for Cygwin;
2   complete rewrite of xemacs Cygwin unexec() code
3
4   Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5
6This file is part of GNU Emacs.
7
8GNU Emacs is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Emacs; see the file COPYING.  If not, write to
20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21Boston, MA 02110-1301, USA.  */
22
23#include <config.h>
24#include <lisp.h>
25#include <stdio.h>
26#include <fcntl.h>
27#include <a.out.h>
28#include <unistd.h>
29#include <assert.h>
30
31#define DOTEXE ".exe"
32
33extern int bss_sbrk_did_unexec;
34
35/* emacs symbols that indicate where bss and data end for emacs internals */
36extern char my_endbss[];
37extern char my_edata[];
38
39/*
40** header for Windows executable files
41*/
42typedef struct
43{
44  FILHDR file_header;
45  PEAOUTHDR file_optional_header;
46  SCNHDR section_header[32];
47} exe_header_t;
48
49int debug_unexcw = 0;
50
51/*
52** Read the header from the executable into memory so we can more easily access it.
53*/
54static exe_header_t *
55read_exe_header (int fd, exe_header_t * exe_header_buffer)
56{
57  int i;
58  int ret;
59
60  assert (fd >= 0);
61  assert (exe_header_buffer != 0);
62
63  ret = lseek (fd, 0L, SEEK_SET);
64  assert (ret != -1);
65
66  ret =
67    read (fd, &exe_header_buffer->file_header,
68	  sizeof (exe_header_buffer->file_header));
69  assert (ret == sizeof (exe_header_buffer->file_header));
70
71  assert (exe_header_buffer->file_header.e_magic == 0x5a4d);
72  assert (exe_header_buffer->file_header.nt_signature == 0x4550);
73  assert (exe_header_buffer->file_header.f_magic == 0x014c);
74  assert (exe_header_buffer->file_header.f_nscns > 0);
75  assert (exe_header_buffer->file_header.f_nscns <=
76	  sizeof (exe_header_buffer->section_header) /
77	  sizeof (exe_header_buffer->section_header[0]));
78  assert (exe_header_buffer->file_header.f_opthdr > 0);
79
80  ret =
81    read (fd, &exe_header_buffer->file_optional_header,
82	  sizeof (exe_header_buffer->file_optional_header));
83  assert (ret == sizeof (exe_header_buffer->file_optional_header));
84
85  assert (exe_header_buffer->file_optional_header.magic == 0x010b);
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*/
108static void
109fixup_executable (int fd)
110{
111  exe_header_t exe_header_buffer;
112  exe_header_t *exe_header;
113  int i;
114  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 0x%08x end 0x%08x\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 0x%08x mem length %d\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	      ret =
184		lseek (fd,
185		       (long) (exe_header->section_header[i].s_scnptr +
186			       exe_header->section_header[i].s_size - 1),
187		       SEEK_SET);
188	      assert (ret != -1);
189	      ret = write (fd, "", 1);
190	      assert (ret == 1);
191
192	      ret =
193		lseek (fd,
194		       (long) ((char *) &exe_header->section_header[i] -
195			       (char *) exe_header), SEEK_SET);
196	      assert (ret != -1);
197	      ret =
198		write (fd, &exe_header->section_header[i],
199		       sizeof (exe_header->section_header[i]));
200	      assert (ret == sizeof (exe_header->section_header[i]));
201	      if (debug_unexcw)
202		printf ("         seek to %ld, write %d\n",
203			(long) ((char *) &exe_header->section_header[i] -
204				(char *) exe_header),
205			sizeof (exe_header->section_header[i]));
206	    }
207	  /* write initialized data section */
208	  ret =
209	    lseek (fd, (long) (exe_header->section_header[i].s_scnptr),
210		   SEEK_SET);
211	  assert (ret != -1);
212	  ret =
213	    write (fd, (char *) start_address,
214		   my_endbss - (char *) start_address);
215	  assert (ret == (my_endbss - (char *) start_address));
216	  if (debug_unexcw)
217	    printf ("         .bss, mem start 0x%08x mem length %d\n",
218		    start_address, my_endbss - (char *) start_address);
219	  if (debug_unexcw)
220	    printf ("         .bss, file start %d file length %d\n",
221		    (int) exe_header->section_header[i].s_scnptr,
222		    (int) exe_header->section_header[i].s_paddr);
223	}
224    }
225  assert (found_bss == 1);
226  assert (found_data == 1);
227}
228
229/*
230** Windows likes .exe suffixes on executables.
231*/
232static char *
233add_exe_suffix_if_necessary (const char *name, char *modified)
234{
235  int i = strlen (name);
236  if (i <= (sizeof (DOTEXE) - 1))
237    {
238      sprintf (modified, "%s%s", name, DOTEXE);
239    }
240  else if (!strcasecmp (name + i - (sizeof (DOTEXE) - 1), DOTEXE))
241    {
242      strcpy (modified, name);
243    }
244  else
245    {
246      sprintf (modified, "%s%s", name, DOTEXE);
247    }
248  return (modified);
249}
250
251int
252unexec (char *outfile, char *infile, unsigned start_data, unsigned d1,
253	unsigned d2)
254{
255  char infile_buffer[FILENAME_MAX];
256  char outfile_buffer[FILENAME_MAX];
257  int fd_in;
258  int fd_out;
259  int ret;
260  int ret2;
261
262  if (bss_sbrk_did_unexec)
263    {
264      /* can only dump once */
265      printf ("You can only dump Emacs once on this platform.\n");
266      return (1);
267    }
268
269  report_sheap_usage (1);
270
271  infile = add_exe_suffix_if_necessary (infile, infile_buffer);
272  outfile = add_exe_suffix_if_necessary (outfile, outfile_buffer);
273
274  fd_in = open (infile, O_RDONLY | O_BINARY);
275  assert (fd_in >= 0);
276  fd_out = open (outfile, O_RDWR | O_TRUNC | O_CREAT | O_BINARY, 0755);
277  assert (fd_out >= 0);
278  for (;;)
279    {
280      char buffer[4096];
281      ret = read (fd_in, buffer, sizeof (buffer));
282      if (ret == 0)
283	{
284	  /* eof */
285	  break;
286	}
287      assert (ret > 0);
288      /* data */
289      ret2 = write (fd_out, buffer, ret);
290      assert (ret2 == ret);
291    }
292  ret = close (fd_in);
293  assert (ret == 0);
294
295  bss_sbrk_did_unexec = 1;
296  fixup_executable (fd_out);
297  bss_sbrk_did_unexec = 0;
298
299  ret = close (fd_out);
300  assert (ret == 0);
301
302  return (0);
303}
304
305/* arch-tag: fc44f6c3-ca0a-45e0-a5a2-58b6101b1e65
306   (do not change this comment) */
307