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 */
61 #include "syswindows.h"
66 /* From IMAGEHLP.H which is not installed by default by MSVC < 5 */
67 /* The IMAGEHLP.DLL library is not distributed by default with Windows95 */
68 typedef PIMAGE_NT_HEADERS
69 (__stdcall * pfnCheckSumMappedFile_t) (LPVOID BaseAddress, DWORD FileLength,
70 LPDWORD HeaderSum, LPDWORD CheckSum);
74 extern BOOL ctrl_c_handler (unsigned long type);
77 /* Sync with FSF Emacs 19.34.6
78 note: struct file_data is now defined in nt.h */
81 HEAP_UNINITIALIZED = 1,
86 /* Basically, our "initialized" flag. */
87 int heap_state = HEAP_UNINITIALIZED;
89 /* So we can find our heap in the file to recreate it. */
90 unsigned long heap_index_in_executable = UNINIT_LONG;
92 void get_section_info (file_data *p_file);
93 void copy_executable_and_dump_data_section (file_data *, file_data *);
94 void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
96 /* Cached info about the .data section in the executable. */
97 PUCHAR data_start_va = UNINIT_PTR;
98 DWORD data_start_file = UNINIT_LONG;
99 DWORD data_size = UNINIT_LONG;
101 /* Cached info about the .bss section in the executable. */
102 PUCHAR bss_start = UNINIT_PTR;
103 DWORD bss_size = UNINIT_LONG;
105 /* Startup code for running on NT. When we are running as the dumped
106 version, we need to bootstrap our heap and .bss section into our
107 address space before we can actually hand off control to the startup
108 code supplied by NT (primarily because that code relies upon malloc ()). */
110 /* **********************
111 Hackers please remember, this _start() thingy is *not* called neither
112 when dumping portably, nor when running from temacs! Do not put
113 significant XEmacs initialization here!
114 ********************** */
119 extern void mainCRTStartup (void);
121 /* Cache system info, e.g., the NT page size. */
122 cache_system_info ();
124 /* If we're a dumped version of emacs then we need to recreate
125 our heap and play tricks with our .bss section. Do this before
126 start up. (WARNING: Do not put any code before this section
127 that relies upon malloc () and runs in the dumped version. It
129 if (heap_state == HEAP_UNLOADED)
131 char executable_path[MAX_PATH];
133 if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0)
138 /* #### This is super-bogus. When I rename xemacs.exe,
139 the renamed file still loads its heap from xemacs.exe --kkm */
142 /* To allow profiling, make sure executable_path names the .exe
143 file, not the file created by the profiler */
144 char *p = strrchr (executable_path, '\\');
145 strcpy (p+1, PATH_PROGNAME ".exe");
149 recreate_heap (executable_path);
150 heap_state = HEAP_LOADED;
153 /* #### This is bogus, too. _fmode is set to different values
154 when we run `xemacs' and `temacs run-emacs'. The sooner we
155 hit and fix all the weirdities this causes us, the better --kkm */
157 /* The default behavior is to treat files as binary and patch up
158 text files appropriately. */
163 /* This prevents ctrl-c's in shells running while we're suspended from
165 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
171 /* Dump out .data and .bss sections into a new executable. */
173 unexec (char *new_name, char *old_name, unsigned int start_data,
174 unsigned int start_bss, unsigned int entry_address)
176 file_data in_file, out_file;
177 char out_filename[MAX_PATH], in_filename[MAX_PATH];
180 HINSTANCE hImagehelp;
182 /* Make sure that the input and output filenames have the
183 ".exe" extension...patch them up if they don't. */
184 strcpy (in_filename, old_name);
185 ptr = in_filename + strlen (in_filename) - 4;
186 if (strcmp (ptr, ".exe"))
187 strcat (in_filename, ".exe");
189 strcpy (out_filename, new_name);
190 ptr = out_filename + strlen (out_filename) - 4;
191 if (strcmp (ptr, ".exe"))
192 strcat (out_filename, ".exe");
194 printf ("Dumping from %s\n", in_filename);
195 printf (" to %s\n", out_filename);
197 /* We need to round off our heap to NT's allocation unit (64KB). */
198 round_heap (get_allocation_unit ());
200 /* Open the undumped executable file. */
201 if (!open_input_file (&in_file, in_filename))
203 printf ("Failed to open %s (%d)...bailing.\n",
204 in_filename, GetLastError ());
208 /* Get the interesting section info, like start and size of .bss... */
209 get_section_info (&in_file);
211 /* The size of the dumped executable is the size of the original
212 executable plus the size of the heap and the size of the .bss section. */
213 heap_index_in_executable = (unsigned long)
214 round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
215 size = heap_index_in_executable + get_committed_heap_size () + bss_size;
216 if (!open_output_file (&out_file, out_filename, size))
218 printf ("Failed to open %s (%d)...bailing.\n",
219 out_filename, GetLastError ());
223 /* Set the flag (before dumping). */
224 heap_state = HEAP_UNLOADED;
226 copy_executable_and_dump_data_section (&in_file, &out_file);
227 dump_bss_and_heap (&in_file, &out_file);
229 /* Patch up header fields; profiler is picky about this. */
230 hImagehelp = LoadLibrary ("imagehlp.dll");
233 PIMAGE_DOS_HEADER dos_header;
234 PIMAGE_NT_HEADERS nt_header;
238 pfnCheckSumMappedFile_t pfnCheckSumMappedFile;
240 dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
241 nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
243 nt_header->OptionalHeader.CheckSum = 0;
245 nt_header->FileHeader.TimeDateStamp = time (NULL);
246 dos_header->e_cp = size / 512;
247 nt_header->OptionalHeader.SizeOfImage = size;
250 pfnCheckSumMappedFile =
251 (pfnCheckSumMappedFile_t) GetProcAddress (hImagehelp,
252 "CheckSumMappedFile");
253 if (pfnCheckSumMappedFile)
256 nt_header->FileHeader.TimeDateStamp = time (NULL);
258 pfnCheckSumMappedFile (out_file.file_base,
262 nt_header->OptionalHeader.CheckSum = checksum;
264 FreeLibrary (hImagehelp);
267 close_file_data (&in_file);
268 close_file_data (&out_file);
273 /* Routines to manipulate NT executable file sections. */
275 #ifndef DUMP_SEPARATE_SECTION
277 get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
281 char map_filename[MAX_PATH];
285 /* Overwrite the .exe extension on the executable file name with
286 the .map extension. */
287 strcpy (map_filename, p_infile->name);
288 n = strlen (map_filename) - 3;
289 strcpy (&map_filename[n], "map");
291 map = fopen (map_filename, "r");
294 printf ("Failed to open map file %s, error %d...bailing out.\n",
295 map_filename, GetLastError ());
299 while (fgets (buffer, sizeof (buffer), map))
301 if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
303 n = sscanf (buffer, " %*d:%x %x", &start, &len);
306 printf ("Failed to scan the .bss section line:\n%s", buffer);
311 *p_bss_start = (PUCHAR) start;
312 *p_bss_size = (DWORD) len;
316 /* Flip through the executable and cache the info necessary for dumping. */
318 get_section_info (file_data *p_infile)
320 PIMAGE_DOS_HEADER dos_header;
321 PIMAGE_NT_HEADERS nt_header;
322 PIMAGE_SECTION_HEADER section, data_section;
326 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
327 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
329 printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
332 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
333 dos_header->e_lfanew);
334 if (nt_header == NULL)
336 printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
341 /* Check the NT header signature ... */
342 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
344 printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
345 nt_header->Signature, p_infile->name);
348 /* Flip through the sections for .data and .bss ... */
349 section = (PIMAGE_SECTION_HEADER) IMAGE_FIRST_SECTION (nt_header);
350 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
352 #ifndef DUMP_SEPARATE_SECTION
353 if (!strcmp (section->Name, ".bss"))
355 extern int my_ebss; /* From lastfile.c */
357 ptr = (char *) nt_header->OptionalHeader.ImageBase +
358 section->VirtualAddress;
360 bss_size = (char*)&my_ebss - (char*)bss_start;
363 if (!strcmp (section->Name, ".data"))
365 if (!strcmp (section->Name, "xdata"))
368 extern char my_edata[]; /* From lastfile.c */
370 /* The .data section. */
371 data_section = section;
372 ptr = (char *) nt_header->OptionalHeader.ImageBase +
373 section->VirtualAddress;
375 data_start_file = section->PointerToRawData;
377 #ifndef DUMP_SEPARATE_SECTION
378 /* Write only the part of the section that contains emacs data. */
379 data_size = my_edata - data_start_va;
381 /* Write back the full section. */
382 data_size = section->SizeOfRawData;
384 /* This code doesn't know how to grow the raw size of a section. */
385 if (section->SizeOfRawData < section->Misc.VirtualSize)
387 printf ("The emacs data section is smaller than expected"
396 #ifndef DUMP_SEPARATE_SECTION
397 if (bss_start == UNINIT_PTR)
399 /* Starting with MSVC 4.0, the .bss section has been eliminated
400 and appended virtually to the end of the .data section. Our
401 only hint about where the .bss section starts in the address
402 comes from the SizeOfRawData field in the .data section
403 header. Unfortunately, this field is only approximate, as it
404 is a rounded number and is typically rounded just beyond the
405 start of the .bss section. To find the start and size of the
406 .bss section exactly, we have to peek into the map file. */
409 get_bss_info_from_map_file (p_infile, &ptr, &bss_size);
410 bss_start = ptr + nt_header->OptionalHeader.ImageBase
411 + data_section->VirtualAddress;
412 bss_size = (char*)&my_ebss - (char*)bss_start;
420 /* The dump routines. */
423 #define DUMP_MSG(x) printf x
429 copy_executable_and_dump_data_section (file_data *p_infile,
430 file_data *p_outfile)
432 unsigned char *data_file, *data_va;
433 unsigned long size, index;
435 /* Get a pointer to where the raw data should go in the executable file. */
436 data_file = (char *) p_outfile->file_base + data_start_file;
438 /* Get a pointer to the raw data in our address space. */
439 data_va = data_start_va;
441 size = (DWORD) data_file - (DWORD) p_outfile->file_base;
442 DUMP_MSG (("Copying executable up to data section...\n"));
443 DUMP_MSG (("\t0x%08x Offset in input file.\n", 0));
444 DUMP_MSG (("\t0x%08x Offset in output file.\n", 0));
445 DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
446 memcpy (p_outfile->file_base, p_infile->file_base, size);
449 DUMP_MSG (("Dumping data section...\n"));
450 DUMP_MSG (("\t0x%08x Address in process.\n", data_va));
451 DUMP_MSG (("\t0x%08x Offset in output file.\n",
452 (char*)data_file - p_outfile->file_base));
453 DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
454 memcpy (data_file, data_va, size);
456 index = (DWORD) data_file + size - (DWORD) p_outfile->file_base;
457 size = p_infile->size - index;
458 DUMP_MSG (("Copying rest of executable...\n"));
459 DUMP_MSG (("\t0x%08x Offset in input file.\n", index));
460 DUMP_MSG (("\t0x%08x Offset in output file.\n", index));
461 DUMP_MSG (("\t0x%08x Size in bytes.\n", size));
462 memcpy ((char *) p_outfile->file_base + index,
463 (char *) p_infile->file_base + index, size);
467 dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
469 unsigned char *heap_data;
470 unsigned long size, index;
472 DUMP_MSG (("Dumping heap onto end of executable...\n"));
474 index = heap_index_in_executable;
475 size = get_committed_heap_size ();
476 heap_data = get_heap_start ();
478 DUMP_MSG (("\t0x%08x Heap start in process.\n", heap_data));
479 DUMP_MSG (("\t0x%08x Heap offset in executable.\n", index));
480 DUMP_MSG (("\t0x%08x Heap size in bytes.\n", size));
482 memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
484 #ifndef DUMP_SEPARATE_SECTION
485 DUMP_MSG (("Dumping bss onto end of executable...\n"));
490 DUMP_MSG (("\t0x%08x BSS start in process.\n", bss_start));
491 DUMP_MSG (("\t0x%08x BSS offset in executable.\n", index));
492 DUMP_MSG (("\t0x%08x BSS size in bytes.\n", size));
493 memcpy ((char *) p_outfile->file_base + index, bss_start, size);
499 /* Reload and remap routines. */
502 /* Load the dumped .bss section into the .bss area of our address space. */
503 /* Already done if the .bss was part of a separate emacs data section */
505 read_in_bss (char *filename)
507 #ifndef DUMP_SEPARATE_SECTION
509 unsigned long index, n_read;
511 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
512 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
513 if (file == INVALID_HANDLE_VALUE)
516 /* Seek to where the .bss section is tucked away after the heap... */
517 index = heap_index_in_executable + get_committed_heap_size ();
518 if (SetFilePointer (file, index, NULL, FILE_BEGIN) == 0xFFFFFFFF)
521 /* Ok, read in the saved .bss section and initialize all
522 uninitialized variables. */
523 if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
530 /* Map the heap dumped into the executable file into our address space. */
532 map_in_heap (char *filename)
537 unsigned long size, upper_size, n_read;
539 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
540 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
541 if (file == INVALID_HANDLE_VALUE)
544 size = GetFileSize (file, &upper_size);
545 file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
550 size = get_committed_heap_size ();
551 file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0,
552 heap_index_in_executable, size,
559 /* If we don't succeed with the mapping, then copy from the
560 data into the heap. */
562 CloseHandle (file_mapping);
564 if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
565 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
568 /* Seek to the location of the heap data in the executable. */
569 if (SetFilePointer (file, heap_index_in_executable,
570 NULL, FILE_BEGIN) == 0xFFFFFFFF)
573 /* Read in the data. */
574 if (!ReadFile (file, get_heap_start (),
575 get_committed_heap_size (), &n_read, NULL))