import -ko -b 1.1.3 XEmacs XEmacs-21_2 r21-2-35
[chise/xemacs-chise.git.1] / src / unexnt.c
1 /* unexec for GNU Emacs on Windows NT.
2    Copyright (C) 1994 Free Software Foundation, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21    Geoff Voelker (voelker@cs.washington.edu) 8-12-94 */
22
23 /* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */
24
25 /* The linkers that come with MSVC >= 4.0 merge .bss into .data and reorder
26  * uninitialised data so that the .data section looks like:
27  *
28  *      crt0 initialised data
29  *      emacs initialised data
30  *              <my_edata>
31  *      library initialised data
32  *              <start of bss part of .data>
33  *      emacs static uninitialised data
34  *      library static uninitialised data
35  *      emacs global uninitialised data
36  *              <my_ebss>
37  *      library global uninitialised data
38  *
39  * This means that we can't use the normal my_ebss in lastfile.c trick to
40  * differentiate between unitialised data that belongs to emacs and
41  * uninitialised data that belongs to system libraries. This is bad because
42  * we do want to initialise the emacs data, but we don't want to initialise
43  * the system library data.
44  *
45  * To solve this problem using MSVC >= 5.0 we use a pragma directive to tell
46  * the compiler to put emacs's data (both initialised and uninitialised) in
47  * a separate section in the executable, and we only dump that section. This
48  * means that all files that define initialized data must include config.h
49  * to pick up the pragma. We don't try to make any part of that section
50  * read-only.
51  *
52  * This pragma directive isn't supported by the MSVC 4.x compiler. Instead,
53  * we dump crt0 initialised data and library static uninitialised data in
54  * addition to the emacs data. This is wrong, but we appear to be able to
55  * get away with it. A proper fix might involve the introduction of a static
56  * version of my_ebss in lastfile.c and a new firstfile.c file.  jhar */
57
58 #include <config.h>
59 #include <stdlib.h>     /* _fmode */
60 #include <stdio.h>
61 #include <fcntl.h>
62 #include <windows.h>
63
64 #include "nt.h"
65 #include "ntheap.h"
66
67 /* From IMAGEHLP.H which is not installed by default by MSVC < 5 */
68 /* The IMAGEHLP.DLL library is not distributed by default with Windows95 */
69 typedef PIMAGE_NT_HEADERS
70 (__stdcall * pfnCheckSumMappedFile_t) (LPVOID BaseAddress, DWORD FileLength,
71                                        LPDWORD HeaderSum, LPDWORD CheckSum);
72
73
74 #if 0
75 extern BOOL ctrl_c_handler (unsigned long type);
76 #endif
77
78 /* Sync with FSF Emacs 19.34.6
79    note: struct file_data is now defined in nt.h */
80
81 enum {
82   HEAP_UNINITIALIZED = 1,
83   HEAP_UNLOADED,
84   HEAP_LOADED
85 };
86
87 /* Basically, our "initialized" flag.  */
88 int heap_state = HEAP_UNINITIALIZED;
89
90 /* So we can find our heap in the file to recreate it.  */
91 unsigned long heap_index_in_executable = UNINIT_LONG;
92
93 void get_section_info (file_data *p_file);
94 void copy_executable_and_dump_data_section (file_data *, file_data *);
95 void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
96
97 /* Cached info about the .data section in the executable.  */
98 PUCHAR data_start_va = UNINIT_PTR;
99 DWORD  data_start_file = UNINIT_LONG;
100 DWORD  data_size = UNINIT_LONG;
101
102 /* Cached info about the .bss section in the executable.  */
103 PUCHAR bss_start = UNINIT_PTR;
104 DWORD  bss_size = UNINIT_LONG;
105
106 /* Startup code for running on NT.  When we are running as the dumped
107    version, we need to bootstrap our heap and .bss section into our
108    address space before we can actually hand off control to the startup
109    code supplied by NT (primarily because that code relies upon malloc ()).  */
110
111 /* **********************
112    Hackers please remember, this _start() thingy is *not* called neither
113    when dumping portably, nor when running from temacs! Do not put
114    significant XEmacs initialization here!
115    ********************** */
116
117 void
118 _start (void)
119 {
120   extern void mainCRTStartup (void);
121
122   /* Cache system info, e.g., the NT page size.  */
123   cache_system_info ();
124
125   /* If we're a dumped version of emacs then we need to recreate
126      our heap and play tricks with our .bss section.  Do this before
127      start up.  (WARNING:  Do not put any code before this section
128      that relies upon malloc () and runs in the dumped version.  It
129      won't work.)  */
130   if (heap_state == HEAP_UNLOADED) 
131     {
132       char executable_path[MAX_PATH];
133
134       if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0) 
135         {
136           exit (1);
137         }
138
139       /* #### This is super-bogus. When I rename xemacs.exe,
140          the renamed file still loads its heap from xemacs.exe --kkm */
141 #if 0
142       {
143         /* To allow profiling, make sure executable_path names the .exe
144            file, not the file created by the profiler */
145         char *p = strrchr (executable_path, '\\');
146         strcpy (p+1, PATH_PROGNAME ".exe");
147       }
148 #endif
149
150       recreate_heap (executable_path);
151       heap_state = HEAP_LOADED;
152     }
153
154   /* #### This is bogus, too. _fmode is set to different values
155      when we run `xemacs' and `temacs run-emacs'. The sooner we
156      hit and fix all the weirdities this causes us, the better --kkm */
157 #if 0
158   /* The default behavior is to treat files as binary and patch up
159      text files appropriately.  */
160   _fmode = O_BINARY;
161 #endif
162
163 #if 0
164   /* This prevents ctrl-c's in shells running while we're suspended from
165      having us exit.  */
166   SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
167 #endif
168
169   mainCRTStartup ();
170 }
171
172 /* Dump out .data and .bss sections into a new executable.  */
173 void
174 unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
175         void *entry_address)
176 {
177   file_data in_file, out_file;
178   char out_filename[MAX_PATH], in_filename[MAX_PATH];
179   unsigned long size;
180   char *ptr;
181   HINSTANCE hImagehelp;
182   
183   /* Make sure that the input and output filenames have the
184      ".exe" extension...patch them up if they don't.  */
185   strcpy (in_filename, old_name);
186   ptr = in_filename + strlen (in_filename) - 4;
187   if (strcmp (ptr, ".exe"))
188     strcat (in_filename, ".exe");
189
190   strcpy (out_filename, new_name);
191   ptr = out_filename + strlen (out_filename) - 4;
192   if (strcmp (ptr, ".exe"))
193     strcat (out_filename, ".exe");
194
195   printf ("Dumping from %s\n", in_filename);
196   printf ("          to %s\n", out_filename);
197
198   /* We need to round off our heap to NT's allocation unit (64KB).  */
199   round_heap (get_allocation_unit ());
200
201   /* Open the undumped executable file.  */
202   if (!open_input_file (&in_file, in_filename))
203     {
204       printf ("Failed to open %s (%d)...bailing.\n", 
205               in_filename, GetLastError ());
206       exit (1);
207     }
208
209   /* Get the interesting section info, like start and size of .bss...  */
210   get_section_info (&in_file);
211
212   /* The size of the dumped executable is the size of the original
213      executable plus the size of the heap and the size of the .bss section.  */
214   heap_index_in_executable = (unsigned long)
215     round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
216   size = heap_index_in_executable + get_committed_heap_size () + bss_size;
217   if (!open_output_file (&out_file, out_filename, size))
218     {
219       printf ("Failed to open %s (%d)...bailing.\n", 
220               out_filename, GetLastError ());
221       exit (1);
222     }
223
224   /* Set the flag (before dumping).  */
225   heap_state = HEAP_UNLOADED;
226
227   copy_executable_and_dump_data_section (&in_file, &out_file);
228   dump_bss_and_heap (&in_file, &out_file);
229
230   /* Patch up header fields; profiler is picky about this. */
231   hImagehelp = LoadLibrary ("imagehlp.dll");
232   if (hImagehelp)
233   {
234     PIMAGE_DOS_HEADER dos_header;
235     PIMAGE_NT_HEADERS nt_header;
236
237     DWORD  headersum;
238     DWORD  checksum;
239     pfnCheckSumMappedFile_t pfnCheckSumMappedFile;
240
241     dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
242     nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
243
244     nt_header->OptionalHeader.CheckSum = 0;
245 #if 0
246     nt_header->FileHeader.TimeDateStamp = time (NULL);
247     dos_header->e_cp = size / 512;
248     nt_header->OptionalHeader.SizeOfImage = size;
249 #endif
250
251     pfnCheckSumMappedFile =
252       (pfnCheckSumMappedFile_t) GetProcAddress (hImagehelp,
253                                                 "CheckSumMappedFile");
254     if (pfnCheckSumMappedFile)
255       {
256 #if 0
257         nt_header->FileHeader.TimeDateStamp = time (NULL);
258 #endif
259         pfnCheckSumMappedFile (out_file.file_base,
260                                out_file.size,
261                                &headersum,
262                                &checksum);
263         nt_header->OptionalHeader.CheckSum = checksum;
264       }
265     FreeLibrary (hImagehelp);
266   }
267
268   close_file_data (&in_file);
269   close_file_data (&out_file);
270 }
271
272 /* Routines to manipulate NT executable file sections.  */
273
274 #ifndef DUMP_SEPARATE_SECTION
275 static void
276 get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start, 
277                             DWORD *p_bss_size)
278 {
279   int n, start, len;
280   char map_filename[MAX_PATH];
281   char buffer[256];
282   FILE *map;
283
284   /* Overwrite the .exe extension on the executable file name with
285      the .map extension.  */
286   strcpy (map_filename, p_infile->name);
287   n = strlen (map_filename) - 3;
288   strcpy (&map_filename[n], "map");
289
290   map = fopen (map_filename, "r");
291   if (!map)
292     {
293       printf ("Failed to open map file %s, error %d...bailing out.\n",
294               map_filename, GetLastError ());
295       exit (-1);
296     }
297
298   while (fgets (buffer, sizeof (buffer), map))
299     {
300       if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
301         continue;
302       n = sscanf (buffer, " %*d:%x %x", &start, &len);
303       if (n != 2)
304         {
305           printf ("Failed to scan the .bss section line:\n%s", buffer);
306           exit (-1);
307         }
308       break;
309     }
310   *p_bss_start = (PUCHAR) start;
311   *p_bss_size = (DWORD) len;
312 }
313 #endif
314
315 /* Flip through the executable and cache the info necessary for dumping.  */
316 static void
317 get_section_info (file_data *p_infile)
318 {
319   PIMAGE_DOS_HEADER dos_header;
320   PIMAGE_NT_HEADERS nt_header;
321   PIMAGE_SECTION_HEADER section, data_section;
322   unsigned char *ptr;
323   int i;
324   
325   dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
326   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) 
327     {
328       printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
329       exit (1);
330     }
331   nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) + 
332                                    dos_header->e_lfanew);
333   if (nt_header == NULL) 
334     {
335       printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n", 
336              p_infile->name);
337       exit (1);
338     }
339
340   /* Check the NT header signature ...  */
341   if (nt_header->Signature != IMAGE_NT_SIGNATURE) 
342     {
343       printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
344               nt_header->Signature, p_infile->name);
345     }
346
347   /* Flip through the sections for .data and .bss ...  */
348   section = (PIMAGE_SECTION_HEADER) IMAGE_FIRST_SECTION (nt_header);
349   for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 
350     {
351 #ifndef DUMP_SEPARATE_SECTION
352       if (!strcmp (section->Name, ".bss")) 
353         {
354           extern int my_ebss;           /* From lastfile.c  */
355
356           ptr = (char *) nt_header->OptionalHeader.ImageBase +
357             section->VirtualAddress;
358           bss_start = ptr;
359           bss_size = (char*)&my_ebss - (char*)bss_start;
360         }
361
362       if (!strcmp (section->Name, ".data")) 
363 #else
364       if (!strcmp (section->Name, "xdata"))
365 #endif
366         {
367           extern char my_edata[];       /* From lastfile.c  */
368
369           /* The .data section.  */
370           data_section = section;
371           ptr = (char *) nt_header->OptionalHeader.ImageBase +
372             section->VirtualAddress;
373           data_start_va = ptr;
374           data_start_file = section->PointerToRawData;
375
376 #ifndef DUMP_SEPARATE_SECTION
377           /* Write only the part of the section that contains emacs data. */
378           data_size = my_edata - data_start_va;
379 #else
380           /* Write back the full section.  */
381           data_size = section->SizeOfRawData;
382
383           /* This code doesn't know how to grow the raw size of a section. */
384           if (section->SizeOfRawData < section->Misc.VirtualSize)
385             {
386               printf ("The emacs data section is smaller than expected"
387                       "...bailing.\n");
388               exit (1);
389             }
390 #endif
391         }
392       section++;
393     }
394
395 #ifndef DUMP_SEPARATE_SECTION
396   if (bss_start == UNINIT_PTR)
397     {
398       /* Starting with MSVC 4.0, the .bss section has been eliminated
399          and appended virtually to the end of the .data section.  Our
400          only hint about where the .bss section starts in the address
401          comes from the SizeOfRawData field in the .data section
402          header.  Unfortunately, this field is only approximate, as it
403          is a rounded number and is typically rounded just beyond the
404          start of the .bss section.  To find the start and size of the
405          .bss section exactly, we have to peek into the map file.  */
406       extern int my_ebss;
407
408       get_bss_info_from_map_file (p_infile, &ptr, &bss_size);
409       bss_start = ptr + nt_header->OptionalHeader.ImageBase
410         + data_section->VirtualAddress;
411       bss_size = (char*)&my_ebss - (char*)bss_start;
412     }
413 #else
414   bss_size = 0;
415 #endif
416 }
417
418
419 /* The dump routines.  */
420
421 #ifdef DEBUG_XEMACS
422 #define DUMP_MSG(x) printf x
423 #else
424 #define DUMP_MSG(x)
425 #endif
426
427 static void
428 copy_executable_and_dump_data_section (file_data *p_infile,
429                                        file_data *p_outfile)
430 {
431   unsigned char *data_file, *data_va;
432   unsigned long size, index;
433
434   /* Get a pointer to where the raw data should go in the executable file.  */
435   data_file = (char *) p_outfile->file_base + data_start_file;
436
437   /* Get a pointer to the raw data in our address space.  */
438   data_va = data_start_va;
439
440   size = (DWORD) data_file - (DWORD) p_outfile->file_base;
441   DUMP_MSG (("Copying executable up to data section...\n"));
442   DUMP_MSG (("\t0x%08x Offset in input file.\n", 0));
443   DUMP_MSG (("\t0x%08x Offset in output file.\n", 0));
444   DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
445   memcpy (p_outfile->file_base, p_infile->file_base, size);
446
447   size = data_size;
448   DUMP_MSG (("Dumping data section...\n"));
449   DUMP_MSG (("\t0x%08x Address in process.\n", data_va));
450   DUMP_MSG (("\t0x%08x Offset in output file.\n", 
451              (char*)data_file - p_outfile->file_base));
452   DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
453   memcpy (data_file, data_va, size);
454
455   index = (DWORD) data_file + size - (DWORD) p_outfile->file_base;
456   size = p_infile->size - index;
457   DUMP_MSG (("Copying rest of executable...\n"));
458   DUMP_MSG (("\t0x%08x Offset in input file.\n", index));
459   DUMP_MSG (("\t0x%08x Offset in output file.\n", index));
460   DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
461   memcpy ((char *) p_outfile->file_base + index, 
462           (char *) p_infile->file_base + index, size);
463 }
464
465 static void
466 dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
467 {
468     unsigned char *heap_data;
469     unsigned long size, index;
470
471     DUMP_MSG (("Dumping heap onto end of executable...\n"));
472
473     index = heap_index_in_executable;
474     size = get_committed_heap_size ();
475     heap_data = get_heap_start ();
476
477     DUMP_MSG (("\t0x%08x Heap start in process.\n", heap_data));
478     DUMP_MSG (("\t0x%08x Heap offset in executable.\n", index));
479     DUMP_MSG (("\t0x%08x Heap size in bytes.\n", size));
480
481     memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
482
483 #ifndef DUMP_SEPARATE_SECTION
484     DUMP_MSG (("Dumping bss onto end of executable...\n"));
485     
486     index += size;
487     size = bss_size;
488
489     DUMP_MSG (("\t0x%08x BSS start in process.\n", bss_start));
490     DUMP_MSG (("\t0x%08x BSS offset in executable.\n", index));
491     DUMP_MSG (("\t0x%08x BSS size in bytes.\n", size));
492     memcpy ((char *) p_outfile->file_base + index, bss_start, size);
493 #endif
494 }
495
496 #undef DUMP_MSG
497
498 /* Reload and remap routines.  */
499
500
501 /* Load the dumped .bss section into the .bss area of our address space.  */
502 /* Already done if the .bss  was part of a separate emacs data section */
503 void
504 read_in_bss (char *filename)
505 {
506 #ifndef DUMP_SEPARATE_SECTION
507   HANDLE file;
508   unsigned long index, n_read;
509
510   file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
511                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
512   if (file == INVALID_HANDLE_VALUE)
513     abort ();
514   
515   /* Seek to where the .bss section is tucked away after the heap...  */
516   index = heap_index_in_executable + get_committed_heap_size ();
517   if (SetFilePointer (file, index, NULL, FILE_BEGIN) == 0xFFFFFFFF) 
518     abort ();
519
520   /* Ok, read in the saved .bss section and initialize all 
521      uninitialized variables.  */
522   if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
523     abort ();
524
525   CloseHandle (file);
526 #endif
527 }
528
529 /* Map the heap dumped into the executable file into our address space.  */
530 void 
531 map_in_heap (char *filename)
532 {
533   HANDLE file;
534   HANDLE file_mapping;
535   void  *file_base;
536   unsigned long size, upper_size, n_read;
537
538   file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
539                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
540   if (file == INVALID_HANDLE_VALUE) 
541     abort ();
542
543   size = GetFileSize (file, &upper_size);
544   file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY, 
545                                     0, size, NULL);
546   if (!file_mapping) 
547     abort ();
548
549   size = get_committed_heap_size ();
550   file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0, 
551                                heap_index_in_executable, size,
552                                get_heap_start ());
553   if (file_base != 0) 
554     {
555       return;
556     }
557
558   /* If we don't succeed with the mapping, then copy from the 
559      data into the heap.  */
560
561   CloseHandle (file_mapping);
562
563   if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
564                     MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
565     abort ();
566
567   /* Seek to the location of the heap data in the executable.  */
568   if (SetFilePointer (file, heap_index_in_executable,
569                       NULL, FILE_BEGIN) == 0xFFFFFFFF)
570     abort ();
571
572   /* Read in the data.  */
573   if (!ReadFile (file, get_heap_start (), 
574                  get_committed_heap_size (), &n_read, NULL))
575     abort ();
576
577   CloseHandle (file);
578 }