This commit was generated by cvs2svn to compensate for changes in r1383,
[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 /* From IMAGEHLP.H which is not installed by default by MSVC < 5 */
65 /* The IMAGEHLP.DLL library is not distributed by default with Windows95 */
66 typedef PIMAGE_NT_HEADERS
67 (__stdcall * pfnCheckSumMappedFile_t) (LPVOID BaseAddress, DWORD FileLength,
68                                        LPDWORD HeaderSum, LPDWORD CheckSum);
69
70
71 #if 0
72 extern BOOL ctrl_c_handler (unsigned long type);
73 #endif
74
75 #include "ntheap.h"
76
77 /* Sync with FSF Emacs 19.34.6 note: struct file_data is now defined in ntheap.h */
78
79 enum {
80   HEAP_UNINITIALIZED = 1,
81   HEAP_UNLOADED,
82   HEAP_LOADED
83 };
84
85 /* Basically, our "initialized" flag.  */
86 int heap_state = HEAP_UNINITIALIZED;
87
88 /* So we can find our heap in the file to recreate it.  */
89 unsigned long heap_index_in_executable = UNINIT_LONG;
90
91 void get_section_info (file_data *p_file);
92 void copy_executable_and_dump_data_section (file_data *, file_data *);
93 void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
94
95 /* Cached info about the .data section in the executable.  */
96 PUCHAR data_start_va = UNINIT_PTR;
97 DWORD  data_start_file = UNINIT_LONG;
98 DWORD  data_size = UNINIT_LONG;
99
100 /* Cached info about the .bss section in the executable.  */
101 PUCHAR bss_start = UNINIT_PTR;
102 DWORD  bss_size = UNINIT_LONG;
103
104 #ifdef HAVE_NTGUI
105 HINSTANCE hinst = NULL;
106 HINSTANCE hprevinst = NULL;
107 LPSTR lpCmdLine = "";
108 int nCmdShow = 0;
109 #endif /* HAVE_NTGUI */
110
111 /* Startup code for running on NT.  When we are running as the dumped
112    version, we need to bootstrap our heap and .bss section into our
113    address space before we can actually hand off control to the startup
114    code supplied by NT (primarily because that code relies upon malloc ()).  */
115
116 /* **********************
117    Hackers please remember, this _start() thingy is *not* called neither
118    when dumping portably, nor when running from temacs! Do not put
119    significant XEmacs initialization here!
120    ********************** */
121
122 void
123 _start (void)
124 {
125   extern void mainCRTStartup (void);
126
127   /* Cache system info, e.g., the NT page size.  */
128   cache_system_info ();
129
130   /* If we're a dumped version of emacs then we need to recreate
131      our heap and play tricks with our .bss section.  Do this before
132      start up.  (WARNING:  Do not put any code before this section
133      that relies upon malloc () and runs in the dumped version.  It
134      won't work.)  */
135   if (heap_state == HEAP_UNLOADED) 
136     {
137       char executable_path[MAX_PATH];
138
139       if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0) 
140         {
141           exit (1);
142         }
143
144       /* #### This is super-bogus. When I rename xemacs.exe,
145          the renamed file still loads its heap from xemacs.exe --kkm */
146 #if 0
147       {
148         /* To allow profiling, make sure executable_path names the .exe
149            file, not the file created by the profiler */
150         char *p = strrchr (executable_path, '\\');
151         strcpy (p+1, PATH_PROGNAME ".exe");
152       }
153 #endif
154
155       recreate_heap (executable_path);
156       heap_state = HEAP_LOADED;
157     }
158
159   /* #### This is bogus, too. _fmode is set to different values
160      when we run `xemacs' and `temacs run-emacs'. The sooner we
161      hit and fix all the weirdities this causes us, the better --kkm */
162 #if 0
163   /* The default behavior is to treat files as binary and patch up
164      text files appropriately, in accordance with the MSDOS code.  */
165   _fmode = O_BINARY;
166 #endif
167
168 #if 0
169   /* This prevents ctrl-c's in shells running while we're suspended from
170      having us exit.  */
171   SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
172 #endif
173
174   /* Invoke the NT CRT startup routine now that our housecleaning
175      is finished.  */
176 #ifdef HAVE_NTGUI
177   /* determine WinMain args like crt0.c does */
178   hinst = GetModuleHandle(NULL);
179   lpCmdLine = GetCommandLine();
180   nCmdShow = SW_SHOWDEFAULT;
181 #endif
182   mainCRTStartup ();
183 }
184
185 /* Dump out .data and .bss sections into a new executable.  */
186 void
187 unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
188         void *entry_address)
189 {
190   file_data in_file, out_file;
191   char out_filename[MAX_PATH], in_filename[MAX_PATH];
192   unsigned long size;
193   char *ptr;
194   HINSTANCE hImagehelp;
195   
196   /* Make sure that the input and output filenames have the
197      ".exe" extension...patch them up if they don't.  */
198   strcpy (in_filename, old_name);
199   ptr = in_filename + strlen (in_filename) - 4;
200   if (strcmp (ptr, ".exe"))
201     strcat (in_filename, ".exe");
202
203   strcpy (out_filename, new_name);
204   ptr = out_filename + strlen (out_filename) - 4;
205   if (strcmp (ptr, ".exe"))
206     strcat (out_filename, ".exe");
207
208   printf ("Dumping from %s\n", in_filename);
209   printf ("          to %s\n", out_filename);
210
211   /* We need to round off our heap to NT's allocation unit (64KB).  */
212   round_heap (get_allocation_unit ());
213
214   /* Open the undumped executable file.  */
215   if (!open_input_file (&in_file, in_filename))
216     {
217       printf ("Failed to open %s (%d)...bailing.\n", 
218               in_filename, GetLastError ());
219       exit (1);
220     }
221
222   /* Get the interesting section info, like start and size of .bss...  */
223   get_section_info (&in_file);
224
225   /* The size of the dumped executable is the size of the original
226      executable plus the size of the heap and the size of the .bss section.  */
227   heap_index_in_executable = (unsigned long)
228     round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
229   size = heap_index_in_executable + get_committed_heap_size () + bss_size;
230   if (!open_output_file (&out_file, out_filename, size))
231     {
232       printf ("Failed to open %s (%d)...bailing.\n", 
233               out_filename, GetLastError ());
234       exit (1);
235     }
236
237   /* Set the flag (before dumping).  */
238   heap_state = HEAP_UNLOADED;
239
240   copy_executable_and_dump_data_section (&in_file, &out_file);
241   dump_bss_and_heap (&in_file, &out_file);
242
243   /* Patch up header fields; profiler is picky about this. */
244   hImagehelp = LoadLibrary ("imagehlp.dll");
245   if (hImagehelp)
246   {
247     PIMAGE_DOS_HEADER dos_header;
248     PIMAGE_NT_HEADERS nt_header;
249
250     DWORD  headersum;
251     DWORD  checksum;
252     pfnCheckSumMappedFile_t pfnCheckSumMappedFile;
253
254     dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
255     nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
256
257     nt_header->OptionalHeader.CheckSum = 0;
258 #if 0
259     nt_header->FileHeader.TimeDateStamp = time (NULL);
260     dos_header->e_cp = size / 512;
261     nt_header->OptionalHeader.SizeOfImage = size;
262 #endif
263
264     pfnCheckSumMappedFile =
265       (pfnCheckSumMappedFile_t) GetProcAddress (hImagehelp,
266                                                 "CheckSumMappedFile");
267     if (pfnCheckSumMappedFile)
268       {
269 #if 0
270         nt_header->FileHeader.TimeDateStamp = time (NULL);
271 #endif
272         pfnCheckSumMappedFile (out_file.file_base,
273                                out_file.size,
274                                &headersum,
275                                &checksum);
276         nt_header->OptionalHeader.CheckSum = checksum;
277       }
278     FreeLibrary (hImagehelp);
279   }
280
281   close_file_data (&in_file);
282   close_file_data (&out_file);
283 }
284
285
286 /* File handling.  */
287
288
289 int
290 open_output_file (file_data *p_file, const char *filename, unsigned long size)
291 {
292   HANDLE file;
293   HANDLE file_mapping;
294   void  *file_base;
295
296   file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
297                      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
298   if (file == INVALID_HANDLE_VALUE) 
299     return FALSE;
300
301   file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE, 
302                                     0, size, NULL);
303   if (!file_mapping) 
304     return FALSE;
305   
306   file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
307   if (file_base == NULL) 
308     return FALSE;
309   
310   p_file->name = filename;
311   p_file->size = size;
312   p_file->file = file;
313   p_file->file_mapping = file_mapping;
314   p_file->file_base = (char*) file_base;
315
316   return TRUE;
317 }
318
319 /* Routines to manipulate NT executable file sections.  */
320
321 #ifndef DUMP_SEPARATE_SECTION
322 static void
323 get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start, 
324                             DWORD *p_bss_size)
325 {
326   int n, start, len;
327   char map_filename[MAX_PATH];
328   char buffer[256];
329   FILE *map;
330
331   /* Overwrite the .exe extension on the executable file name with
332      the .map extension.  */
333   strcpy (map_filename, p_infile->name);
334   n = strlen (map_filename) - 3;
335   strcpy (&map_filename[n], "map");
336
337   map = fopen (map_filename, "r");
338   if (!map)
339     {
340       printf ("Failed to open map file %s, error %d...bailing out.\n",
341               map_filename, GetLastError ());
342       exit (-1);
343     }
344
345   while (fgets (buffer, sizeof (buffer), map))
346     {
347       if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
348         continue;
349       n = sscanf (buffer, " %*d:%x %x", &start, &len);
350       if (n != 2)
351         {
352           printf ("Failed to scan the .bss section line:\n%s", buffer);
353           exit (-1);
354         }
355       break;
356     }
357   *p_bss_start = (PUCHAR) start;
358   *p_bss_size = (DWORD) len;
359 }
360 #endif
361
362 /* Flip through the executable and cache the info necessary for dumping.  */
363 static void
364 get_section_info (file_data *p_infile)
365 {
366   PIMAGE_DOS_HEADER dos_header;
367   PIMAGE_NT_HEADERS nt_header;
368   PIMAGE_SECTION_HEADER section, data_section;
369   unsigned char *ptr;
370   int i;
371   
372   dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
373   if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) 
374     {
375       printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
376       exit (1);
377     }
378   nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) + 
379                                    dos_header->e_lfanew);
380   if (nt_header == NULL) 
381     {
382       printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n", 
383              p_infile->name);
384       exit (1);
385     }
386
387   /* Check the NT header signature ...  */
388   if (nt_header->Signature != IMAGE_NT_SIGNATURE) 
389     {
390       printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
391               nt_header->Signature, p_infile->name);
392     }
393
394   /* Flip through the sections for .data and .bss ...  */
395   section = (PIMAGE_SECTION_HEADER) IMAGE_FIRST_SECTION (nt_header);
396   for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 
397     {
398 #ifndef DUMP_SEPARATE_SECTION
399       if (!strcmp (section->Name, ".bss")) 
400         {
401           extern int my_ebss;           /* From lastfile.c  */
402
403           ptr = (char *) nt_header->OptionalHeader.ImageBase +
404             section->VirtualAddress;
405           bss_start = ptr;
406           bss_size = (char*)&my_ebss - (char*)bss_start;
407         }
408
409       if (!strcmp (section->Name, ".data")) 
410 #else
411       if (!strcmp (section->Name, "xdata"))
412 #endif
413         {
414           extern char my_edata[];       /* From lastfile.c  */
415
416           /* The .data section.  */
417           data_section = section;
418           ptr = (char *) nt_header->OptionalHeader.ImageBase +
419             section->VirtualAddress;
420           data_start_va = ptr;
421           data_start_file = section->PointerToRawData;
422
423 #ifndef DUMP_SEPARATE_SECTION
424           /* Write only the part of the section that contains emacs data. */
425           data_size = my_edata - data_start_va;
426 #else
427           /* Write back the full section.  */
428           data_size = section->SizeOfRawData;
429
430           /* This code doesn't know how to grow the raw size of a section. */
431           if (section->SizeOfRawData < section->Misc.VirtualSize)
432             {
433               printf ("The emacs data section is smaller than expected"
434                       "...bailing.\n");
435               exit (1);
436             }
437 #endif
438         }
439       section++;
440     }
441
442 #ifndef DUMP_SEPARATE_SECTION
443   if (bss_start == UNINIT_PTR)
444     {
445       /* Starting with MSVC 4.0, the .bss section has been eliminated
446          and appended virtually to the end of the .data section.  Our
447          only hint about where the .bss section starts in the address
448          comes from the SizeOfRawData field in the .data section
449          header.  Unfortunately, this field is only approximate, as it
450          is a rounded number and is typically rounded just beyond the
451          start of the .bss section.  To find the start and size of the
452          .bss section exactly, we have to peek into the map file.  */
453       extern int my_ebss;
454
455       get_bss_info_from_map_file (p_infile, &ptr, &bss_size);
456       bss_start = ptr + nt_header->OptionalHeader.ImageBase
457         + data_section->VirtualAddress;
458       bss_size = (char*)&my_ebss - (char*)bss_start;
459     }
460 #else
461   bss_size = 0;
462 #endif
463 }
464
465
466 /* The dump routines.  */
467
468 #ifdef DEBUG_XEMACS
469 #define DUMP_MSG(x) printf x
470 #else
471 #define DUMP_MSG(x)
472 #endif
473
474 static void
475 copy_executable_and_dump_data_section (file_data *p_infile,
476                                        file_data *p_outfile)
477 {
478   unsigned char *data_file, *data_va;
479   unsigned long size, index;
480
481   /* Get a pointer to where the raw data should go in the executable file.  */
482   data_file = (char *) p_outfile->file_base + data_start_file;
483
484   /* Get a pointer to the raw data in our address space.  */
485   data_va = data_start_va;
486
487   size = (DWORD) data_file - (DWORD) p_outfile->file_base;
488   DUMP_MSG (("Copying executable up to data section...\n"));
489   DUMP_MSG (("\t0x%08x Offset in input file.\n", 0));
490   DUMP_MSG (("\t0x%08x Offset in output file.\n", 0));
491   DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
492   memcpy (p_outfile->file_base, p_infile->file_base, size);
493
494   size = data_size;
495   DUMP_MSG (("Dumping data section...\n"));
496   DUMP_MSG (("\t0x%08x Address in process.\n", data_va));
497   DUMP_MSG (("\t0x%08x Offset in output file.\n", 
498              (char*)data_file - p_outfile->file_base));
499   DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
500   memcpy (data_file, data_va, size);
501
502   index = (DWORD) data_file + size - (DWORD) p_outfile->file_base;
503   size = p_infile->size - index;
504   DUMP_MSG (("Copying rest of executable...\n"));
505   DUMP_MSG (("\t0x%08x Offset in input file.\n", index));
506   DUMP_MSG (("\t0x%08x Offset in output file.\n", index));
507   DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
508   memcpy ((char *) p_outfile->file_base + index, 
509           (char *) p_infile->file_base + index, size);
510 }
511
512 static void
513 dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
514 {
515     unsigned char *heap_data;
516     unsigned long size, index;
517
518     DUMP_MSG (("Dumping heap onto end of executable...\n"));
519
520     index = heap_index_in_executable;
521     size = get_committed_heap_size ();
522     heap_data = get_heap_start ();
523
524     DUMP_MSG (("\t0x%08x Heap start in process.\n", heap_data));
525     DUMP_MSG (("\t0x%08x Heap offset in executable.\n", index));
526     DUMP_MSG (("\t0x%08x Heap size in bytes.\n", size));
527
528     memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
529
530 #ifndef DUMP_SEPARATE_SECTION
531     DUMP_MSG (("Dumping bss onto end of executable...\n"));
532     
533     index += size;
534     size = bss_size;
535
536     DUMP_MSG (("\t0x%08x BSS start in process.\n", bss_start));
537     DUMP_MSG (("\t0x%08x BSS offset in executable.\n", index));
538     DUMP_MSG (("\t0x%08x BSS size in bytes.\n", size));
539     memcpy ((char *) p_outfile->file_base + index, bss_start, size);
540 #endif
541 }
542
543 #undef DUMP_MSG
544
545 /* Reload and remap routines.  */
546
547
548 /* Load the dumped .bss section into the .bss area of our address space.  */
549 /* Already done if the .bss  was part of a separate emacs data section */
550 void
551 read_in_bss (char *filename)
552 {
553 #ifndef DUMP_SEPARATE_SECTION
554   HANDLE file;
555   unsigned long index, n_read;
556
557   file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
558                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
559   if (file == INVALID_HANDLE_VALUE)
560     abort ();
561   
562   /* Seek to where the .bss section is tucked away after the heap...  */
563   index = heap_index_in_executable + get_committed_heap_size ();
564   if (SetFilePointer (file, index, NULL, FILE_BEGIN) == 0xFFFFFFFF) 
565     abort ();
566
567   /* Ok, read in the saved .bss section and initialize all 
568      uninitialized variables.  */
569   if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
570     abort ();
571
572   CloseHandle (file);
573 #endif
574 }
575
576 /* Map the heap dumped into the executable file into our address space.  */
577 void 
578 map_in_heap (char *filename)
579 {
580   HANDLE file;
581   HANDLE file_mapping;
582   void  *file_base;
583   unsigned long size, upper_size, n_read;
584
585   file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
586                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
587   if (file == INVALID_HANDLE_VALUE) 
588     abort ();
589
590   size = GetFileSize (file, &upper_size);
591   file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY, 
592                                     0, size, NULL);
593   if (!file_mapping) 
594     abort ();
595
596   size = get_committed_heap_size ();
597   file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0, 
598                                heap_index_in_executable, size,
599                                get_heap_start ());
600   if (file_base != 0) 
601     {
602       return;
603     }
604
605   /* If we don't succeed with the mapping, then copy from the 
606      data into the heap.  */
607
608   CloseHandle (file_mapping);
609
610   if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
611                     MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
612     abort ();
613
614   /* Seek to the location of the heap data in the executable.  */
615   if (SetFilePointer (file, heap_index_in_executable,
616                       NULL, FILE_BEGIN) == 0xFFFFFFFF)
617     abort ();
618
619   /* Read in the data.  */
620   if (!ReadFile (file, get_heap_start (), 
621                  get_committed_heap_size (), &n_read, NULL))
622     abort ();
623
624   CloseHandle (file);
625 }