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;
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 ()). */
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 ********************** */
120 extern void mainCRTStartup (void);
122 /* Cache system info, e.g., the NT page size. */
123 cache_system_info ();
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
130 if (heap_state == HEAP_UNLOADED)
132 char executable_path[MAX_PATH];
134 if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0)
139 /* #### This is super-bogus. When I rename xemacs.exe,
140 the renamed file still loads its heap from xemacs.exe --kkm */
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");
150 recreate_heap (executable_path);
151 heap_state = HEAP_LOADED;
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 */
158 /* The default behavior is to treat files as binary and patch up
159 text files appropriately. */
164 /* This prevents ctrl-c's in shells running while we're suspended from
166 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
172 /* Dump out .data and .bss sections into a new executable. */
174 unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
177 file_data in_file, out_file;
178 char out_filename[MAX_PATH], in_filename[MAX_PATH];
181 HINSTANCE hImagehelp;
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");
190 strcpy (out_filename, new_name);
191 ptr = out_filename + strlen (out_filename) - 4;
192 if (strcmp (ptr, ".exe"))
193 strcat (out_filename, ".exe");
195 printf ("Dumping from %s\n", in_filename);
196 printf (" to %s\n", out_filename);
198 /* We need to round off our heap to NT's allocation unit (64KB). */
199 round_heap (get_allocation_unit ());
201 /* Open the undumped executable file. */
202 if (!open_input_file (&in_file, in_filename))
204 printf ("Failed to open %s (%d)...bailing.\n",
205 in_filename, GetLastError ());
209 /* Get the interesting section info, like start and size of .bss... */
210 get_section_info (&in_file);
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))
219 printf ("Failed to open %s (%d)...bailing.\n",
220 out_filename, GetLastError ());
224 /* Set the flag (before dumping). */
225 heap_state = HEAP_UNLOADED;
227 copy_executable_and_dump_data_section (&in_file, &out_file);
228 dump_bss_and_heap (&in_file, &out_file);
230 /* Patch up header fields; profiler is picky about this. */
231 hImagehelp = LoadLibrary ("imagehlp.dll");
234 PIMAGE_DOS_HEADER dos_header;
235 PIMAGE_NT_HEADERS nt_header;
239 pfnCheckSumMappedFile_t pfnCheckSumMappedFile;
241 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
242 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
244 nt_header->OptionalHeader.CheckSum = 0;
246 nt_header->FileHeader.TimeDateStamp = time (NULL);
247 dos_header->e_cp = size / 512;
248 nt_header->OptionalHeader.SizeOfImage = size;
251 pfnCheckSumMappedFile =
252 (pfnCheckSumMappedFile_t) GetProcAddress (hImagehelp,
253 "CheckSumMappedFile");
254 if (pfnCheckSumMappedFile)
257 nt_header->FileHeader.TimeDateStamp = time (NULL);
259 pfnCheckSumMappedFile (out_file.file_base,
263 nt_header->OptionalHeader.CheckSum = checksum;
265 FreeLibrary (hImagehelp);
268 close_file_data (&in_file);
269 close_file_data (&out_file);
272 /* Routines to manipulate NT executable file sections. */
274 #ifndef DUMP_SEPARATE_SECTION
276 get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
280 char map_filename[MAX_PATH];
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");
290 map = fopen (map_filename, "r");
293 printf ("Failed to open map file %s, error %d...bailing out.\n",
294 map_filename, GetLastError ());
298 while (fgets (buffer, sizeof (buffer), map))
300 if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
302 n = sscanf (buffer, " %*d:%x %x", &start, &len);
305 printf ("Failed to scan the .bss section line:\n%s", buffer);
310 *p_bss_start = (PUCHAR) start;
311 *p_bss_size = (DWORD) len;
315 /* Flip through the executable and cache the info necessary for dumping. */
317 get_section_info (file_data *p_infile)
319 PIMAGE_DOS_HEADER dos_header;
320 PIMAGE_NT_HEADERS nt_header;
321 PIMAGE_SECTION_HEADER section, data_section;
325 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
326 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
328 printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
331 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
332 dos_header->e_lfanew);
333 if (nt_header == NULL)
335 printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
340 /* Check the NT header signature ... */
341 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
343 printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
344 nt_header->Signature, p_infile->name);
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++)
351 #ifndef DUMP_SEPARATE_SECTION
352 if (!strcmp (section->Name, ".bss"))
354 extern int my_ebss; /* From lastfile.c */
356 ptr = (char *) nt_header->OptionalHeader.ImageBase +
357 section->VirtualAddress;
359 bss_size = (char*)&my_ebss - (char*)bss_start;
362 if (!strcmp (section->Name, ".data"))
364 if (!strcmp (section->Name, "xdata"))
367 extern char my_edata[]; /* From lastfile.c */
369 /* The .data section. */
370 data_section = section;
371 ptr = (char *) nt_header->OptionalHeader.ImageBase +
372 section->VirtualAddress;
374 data_start_file = section->PointerToRawData;
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;
380 /* Write back the full section. */
381 data_size = section->SizeOfRawData;
383 /* This code doesn't know how to grow the raw size of a section. */
384 if (section->SizeOfRawData < section->Misc.VirtualSize)
386 printf ("The emacs data section is smaller than expected"
395 #ifndef DUMP_SEPARATE_SECTION
396 if (bss_start == UNINIT_PTR)
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. */
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;
419 /* The dump routines. */
422 #define DUMP_MSG(x) printf x
428 copy_executable_and_dump_data_section (file_data *p_infile,
429 file_data *p_outfile)
431 unsigned char *data_file, *data_va;
432 unsigned long size, index;
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;
437 /* Get a pointer to the raw data in our address space. */
438 data_va = data_start_va;
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);
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);
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);
466 dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
468 unsigned char *heap_data;
469 unsigned long size, index;
471 DUMP_MSG (("Dumping heap onto end of executable...\n"));
473 index = heap_index_in_executable;
474 size = get_committed_heap_size ();
475 heap_data = get_heap_start ();
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));
481 memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
483 #ifndef DUMP_SEPARATE_SECTION
484 DUMP_MSG (("Dumping bss onto end of executable...\n"));
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);
498 /* Reload and remap routines. */
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 */
504 read_in_bss (char *filename)
506 #ifndef DUMP_SEPARATE_SECTION
508 unsigned long index, n_read;
510 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
511 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
512 if (file == INVALID_HANDLE_VALUE)
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)
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))
529 /* Map the heap dumped into the executable file into our address space. */
531 map_in_heap (char *filename)
536 unsigned long size, upper_size, n_read;
538 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
539 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
540 if (file == INVALID_HANDLE_VALUE)
543 size = GetFileSize (file, &upper_size);
544 file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
549 size = get_committed_heap_size ();
550 file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0,
551 heap_index_in_executable, size,
558 /* If we don't succeed with the mapping, then copy from the
559 data into the heap. */
561 CloseHandle (file_mapping);
563 if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
564 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
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)
572 /* Read in the data. */
573 if (!ReadFile (file, get_heap_start (),
574 get_committed_heap_size (), &n_read, NULL))