1 /* unexec for GNU Emacs on Windows NT.
2 Copyright (C) 1994 Free Software Foundation, Inc.
4 This file is part of XEmacs.
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
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
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
21 Geoff Voelker (voelker@cs.washington.edu) 8-12-94 */
23 /* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */
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:
28 * crt0 initialised data
29 * emacs initialised data
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
37 * library global uninitialised data
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.
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
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 */
59 #include <stdlib.h> /* _fmode */
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);
75 extern BOOL ctrl_c_handler (unsigned long type);
78 /* Sync with FSF Emacs 19.34.6
79 note: struct file_data is now defined in nt.h */
82 HEAP_UNINITIALIZED = 1,
87 /* Basically, our "initialized" flag. */
88 int heap_state = HEAP_UNINITIALIZED;
90 /* So we can find our heap in the file to recreate it. */
91 unsigned long heap_index_in_executable = UNINIT_LONG;
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);
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;
102 /* Cached info about the .bss section in the executable. */
103 PUCHAR bss_start = UNINIT_PTR;
104 DWORD bss_size = UNINIT_LONG;
107 HINSTANCE hinst = NULL;
108 HINSTANCE hprevinst = NULL;
109 LPSTR lpCmdLine = "";
111 #endif /* HAVE_NTGUI */
113 /* Startup code for running on NT. When we are running as the dumped
114 version, we need to bootstrap our heap and .bss section into our
115 address space before we can actually hand off control to the startup
116 code supplied by NT (primarily because that code relies upon malloc ()). */
118 /* **********************
119 Hackers please remember, this _start() thingy is *not* called neither
120 when dumping portably, nor when running from temacs! Do not put
121 significant XEmacs initialization here!
122 ********************** */
127 extern void mainCRTStartup (void);
129 /* Cache system info, e.g., the NT page size. */
130 cache_system_info ();
132 /* If we're a dumped version of emacs then we need to recreate
133 our heap and play tricks with our .bss section. Do this before
134 start up. (WARNING: Do not put any code before this section
135 that relies upon malloc () and runs in the dumped version. It
137 if (heap_state == HEAP_UNLOADED)
139 char executable_path[MAX_PATH];
141 if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0)
146 /* #### This is super-bogus. When I rename xemacs.exe,
147 the renamed file still loads its heap from xemacs.exe --kkm */
150 /* To allow profiling, make sure executable_path names the .exe
151 file, not the file created by the profiler */
152 char *p = strrchr (executable_path, '\\');
153 strcpy (p+1, PATH_PROGNAME ".exe");
157 recreate_heap (executable_path);
158 heap_state = HEAP_LOADED;
161 /* #### This is bogus, too. _fmode is set to different values
162 when we run `xemacs' and `temacs run-emacs'. The sooner we
163 hit and fix all the weirdities this causes us, the better --kkm */
165 /* The default behavior is to treat files as binary and patch up
166 text files appropriately, in accordance with the MSDOS code. */
171 /* This prevents ctrl-c's in shells running while we're suspended from
173 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
176 /* Invoke the NT CRT startup routine now that our housecleaning
179 /* determine WinMain args like crt0.c does */
180 hinst = GetModuleHandle(NULL);
181 lpCmdLine = GetCommandLine();
182 nCmdShow = SW_SHOWDEFAULT;
187 /* Dump out .data and .bss sections into a new executable. */
189 unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
192 file_data in_file, out_file;
193 char out_filename[MAX_PATH], in_filename[MAX_PATH];
196 HINSTANCE hImagehelp;
198 /* Make sure that the input and output filenames have the
199 ".exe" extension...patch them up if they don't. */
200 strcpy (in_filename, old_name);
201 ptr = in_filename + strlen (in_filename) - 4;
202 if (strcmp (ptr, ".exe"))
203 strcat (in_filename, ".exe");
205 strcpy (out_filename, new_name);
206 ptr = out_filename + strlen (out_filename) - 4;
207 if (strcmp (ptr, ".exe"))
208 strcat (out_filename, ".exe");
210 printf ("Dumping from %s\n", in_filename);
211 printf (" to %s\n", out_filename);
213 /* We need to round off our heap to NT's allocation unit (64KB). */
214 round_heap (get_allocation_unit ());
216 /* Open the undumped executable file. */
217 if (!open_input_file (&in_file, in_filename))
219 printf ("Failed to open %s (%d)...bailing.\n",
220 in_filename, GetLastError ());
224 /* Get the interesting section info, like start and size of .bss... */
225 get_section_info (&in_file);
227 /* The size of the dumped executable is the size of the original
228 executable plus the size of the heap and the size of the .bss section. */
229 heap_index_in_executable = (unsigned long)
230 round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
231 size = heap_index_in_executable + get_committed_heap_size () + bss_size;
232 if (!open_output_file (&out_file, out_filename, size))
234 printf ("Failed to open %s (%d)...bailing.\n",
235 out_filename, GetLastError ());
239 /* Set the flag (before dumping). */
240 heap_state = HEAP_UNLOADED;
242 copy_executable_and_dump_data_section (&in_file, &out_file);
243 dump_bss_and_heap (&in_file, &out_file);
245 /* Patch up header fields; profiler is picky about this. */
246 hImagehelp = LoadLibrary ("imagehlp.dll");
249 PIMAGE_DOS_HEADER dos_header;
250 PIMAGE_NT_HEADERS nt_header;
254 pfnCheckSumMappedFile_t pfnCheckSumMappedFile;
256 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
257 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
259 nt_header->OptionalHeader.CheckSum = 0;
261 nt_header->FileHeader.TimeDateStamp = time (NULL);
262 dos_header->e_cp = size / 512;
263 nt_header->OptionalHeader.SizeOfImage = size;
266 pfnCheckSumMappedFile =
267 (pfnCheckSumMappedFile_t) GetProcAddress (hImagehelp,
268 "CheckSumMappedFile");
269 if (pfnCheckSumMappedFile)
272 nt_header->FileHeader.TimeDateStamp = time (NULL);
274 pfnCheckSumMappedFile (out_file.file_base,
278 nt_header->OptionalHeader.CheckSum = checksum;
280 FreeLibrary (hImagehelp);
283 close_file_data (&in_file);
284 close_file_data (&out_file);
287 /* Routines to manipulate NT executable file sections. */
289 #ifndef DUMP_SEPARATE_SECTION
291 get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
295 char map_filename[MAX_PATH];
299 /* Overwrite the .exe extension on the executable file name with
300 the .map extension. */
301 strcpy (map_filename, p_infile->name);
302 n = strlen (map_filename) - 3;
303 strcpy (&map_filename[n], "map");
305 map = fopen (map_filename, "r");
308 printf ("Failed to open map file %s, error %d...bailing out.\n",
309 map_filename, GetLastError ());
313 while (fgets (buffer, sizeof (buffer), map))
315 if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
317 n = sscanf (buffer, " %*d:%x %x", &start, &len);
320 printf ("Failed to scan the .bss section line:\n%s", buffer);
325 *p_bss_start = (PUCHAR) start;
326 *p_bss_size = (DWORD) len;
330 /* Flip through the executable and cache the info necessary for dumping. */
332 get_section_info (file_data *p_infile)
334 PIMAGE_DOS_HEADER dos_header;
335 PIMAGE_NT_HEADERS nt_header;
336 PIMAGE_SECTION_HEADER section, data_section;
340 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
341 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
343 printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
346 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
347 dos_header->e_lfanew);
348 if (nt_header == NULL)
350 printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
355 /* Check the NT header signature ... */
356 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
358 printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
359 nt_header->Signature, p_infile->name);
362 /* Flip through the sections for .data and .bss ... */
363 section = (PIMAGE_SECTION_HEADER) IMAGE_FIRST_SECTION (nt_header);
364 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
366 #ifndef DUMP_SEPARATE_SECTION
367 if (!strcmp (section->Name, ".bss"))
369 extern int my_ebss; /* From lastfile.c */
371 ptr = (char *) nt_header->OptionalHeader.ImageBase +
372 section->VirtualAddress;
374 bss_size = (char*)&my_ebss - (char*)bss_start;
377 if (!strcmp (section->Name, ".data"))
379 if (!strcmp (section->Name, "xdata"))
382 extern char my_edata[]; /* From lastfile.c */
384 /* The .data section. */
385 data_section = section;
386 ptr = (char *) nt_header->OptionalHeader.ImageBase +
387 section->VirtualAddress;
389 data_start_file = section->PointerToRawData;
391 #ifndef DUMP_SEPARATE_SECTION
392 /* Write only the part of the section that contains emacs data. */
393 data_size = my_edata - data_start_va;
395 /* Write back the full section. */
396 data_size = section->SizeOfRawData;
398 /* This code doesn't know how to grow the raw size of a section. */
399 if (section->SizeOfRawData < section->Misc.VirtualSize)
401 printf ("The emacs data section is smaller than expected"
410 #ifndef DUMP_SEPARATE_SECTION
411 if (bss_start == UNINIT_PTR)
413 /* Starting with MSVC 4.0, the .bss section has been eliminated
414 and appended virtually to the end of the .data section. Our
415 only hint about where the .bss section starts in the address
416 comes from the SizeOfRawData field in the .data section
417 header. Unfortunately, this field is only approximate, as it
418 is a rounded number and is typically rounded just beyond the
419 start of the .bss section. To find the start and size of the
420 .bss section exactly, we have to peek into the map file. */
423 get_bss_info_from_map_file (p_infile, &ptr, &bss_size);
424 bss_start = ptr + nt_header->OptionalHeader.ImageBase
425 + data_section->VirtualAddress;
426 bss_size = (char*)&my_ebss - (char*)bss_start;
434 /* The dump routines. */
437 #define DUMP_MSG(x) printf x
443 copy_executable_and_dump_data_section (file_data *p_infile,
444 file_data *p_outfile)
446 unsigned char *data_file, *data_va;
447 unsigned long size, index;
449 /* Get a pointer to where the raw data should go in the executable file. */
450 data_file = (char *) p_outfile->file_base + data_start_file;
452 /* Get a pointer to the raw data in our address space. */
453 data_va = data_start_va;
455 size = (DWORD) data_file - (DWORD) p_outfile->file_base;
456 DUMP_MSG (("Copying executable up to data section...\n"));
457 DUMP_MSG (("\t0x%08x Offset in input file.\n", 0));
458 DUMP_MSG (("\t0x%08x Offset in output file.\n", 0));
459 DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
460 memcpy (p_outfile->file_base, p_infile->file_base, size);
463 DUMP_MSG (("Dumping data section...\n"));
464 DUMP_MSG (("\t0x%08x Address in process.\n", data_va));
465 DUMP_MSG (("\t0x%08x Offset in output file.\n",
466 (char*)data_file - p_outfile->file_base));
467 DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
468 memcpy (data_file, data_va, size);
470 index = (DWORD) data_file + size - (DWORD) p_outfile->file_base;
471 size = p_infile->size - index;
472 DUMP_MSG (("Copying rest of executable...\n"));
473 DUMP_MSG (("\t0x%08x Offset in input file.\n", index));
474 DUMP_MSG (("\t0x%08x Offset in output file.\n", index));
475 DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
476 memcpy ((char *) p_outfile->file_base + index,
477 (char *) p_infile->file_base + index, size);
481 dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
483 unsigned char *heap_data;
484 unsigned long size, index;
486 DUMP_MSG (("Dumping heap onto end of executable...\n"));
488 index = heap_index_in_executable;
489 size = get_committed_heap_size ();
490 heap_data = get_heap_start ();
492 DUMP_MSG (("\t0x%08x Heap start in process.\n", heap_data));
493 DUMP_MSG (("\t0x%08x Heap offset in executable.\n", index));
494 DUMP_MSG (("\t0x%08x Heap size in bytes.\n", size));
496 memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
498 #ifndef DUMP_SEPARATE_SECTION
499 DUMP_MSG (("Dumping bss onto end of executable...\n"));
504 DUMP_MSG (("\t0x%08x BSS start in process.\n", bss_start));
505 DUMP_MSG (("\t0x%08x BSS offset in executable.\n", index));
506 DUMP_MSG (("\t0x%08x BSS size in bytes.\n", size));
507 memcpy ((char *) p_outfile->file_base + index, bss_start, size);
513 /* Reload and remap routines. */
516 /* Load the dumped .bss section into the .bss area of our address space. */
517 /* Already done if the .bss was part of a separate emacs data section */
519 read_in_bss (char *filename)
521 #ifndef DUMP_SEPARATE_SECTION
523 unsigned long index, n_read;
525 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
526 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
527 if (file == INVALID_HANDLE_VALUE)
530 /* Seek to where the .bss section is tucked away after the heap... */
531 index = heap_index_in_executable + get_committed_heap_size ();
532 if (SetFilePointer (file, index, NULL, FILE_BEGIN) == 0xFFFFFFFF)
535 /* Ok, read in the saved .bss section and initialize all
536 uninitialized variables. */
537 if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
544 /* Map the heap dumped into the executable file into our address space. */
546 map_in_heap (char *filename)
551 unsigned long size, upper_size, n_read;
553 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
554 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
555 if (file == INVALID_HANDLE_VALUE)
558 size = GetFileSize (file, &upper_size);
559 file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
564 size = get_committed_heap_size ();
565 file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0,
566 heap_index_in_executable, size,
573 /* If we don't succeed with the mapping, then copy from the
574 data into the heap. */
576 CloseHandle (file_mapping);
578 if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
579 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
582 /* Seek to the location of the heap data in the executable. */
583 if (SetFilePointer (file, heap_index_in_executable,
584 NULL, FILE_BEGIN) == 0xFFFFFFFF)
587 /* Read in the data. */
588 if (!ReadFile (file, get_heap_start (),
589 get_committed_heap_size (), &n_read, NULL))