1 /* unexec for GNU Emacs on Windows NT.
2 Copyright (C) 1994 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
21 Geoff Voelker (voelker@cs.washington.edu) 8-12-94
24 #include <stdlib.h> /* _fmode */
29 extern BOOL
ctrl_c_handler (unsigned long type
);
33 /* A convenient type for keeping all the info about a mapped file together. */
34 typedef struct file_data
{
39 unsigned char *file_base
;
42 /* Force zero initialized variables to be placed in the .data segment;
43 MSVC 5.0 otherwise places them in .bss, which breaks the dumping code. */
44 #pragma data_seg(".data")
46 /* Basically, our "initialized" flag. */
47 BOOL need_to_recreate_heap
= FALSE
;
49 /* So we can find our heap in the file to recreate it. */
50 unsigned long heap_index_in_executable
= 0;
52 void open_input_file (file_data
*p_file
, char *name
);
53 void open_output_file (file_data
*p_file
, char *name
, unsigned long size
);
54 void close_file_data (file_data
*p_file
);
56 void get_section_info (file_data
*p_file
);
57 void copy_executable_and_dump_data_section (file_data
*, file_data
*);
58 void dump_bss_and_heap (file_data
*p_infile
, file_data
*p_outfile
);
60 /* Cached info about the .data section in the executable. */
61 PUCHAR data_start_va
= 0;
62 DWORD data_start_file
= 0;
65 /* Cached info about the .bss section in the executable. */
70 HINSTANCE hinst
= NULL
;
71 HINSTANCE hprevinst
= NULL
;
74 #endif /* HAVE_NTGUI */
76 /* Startup code for running on NT. When we are running as the dumped
77 version, we need to bootstrap our heap and .bss section into our
78 address space before we can actually hand off control to the startup
79 code supplied by NT (primarily because that code relies upon malloc ()). */
83 extern void mainCRTStartup (void);
85 /* Cache system info, e.g., the NT page size. */
88 /* If we're a dumped version of emacs then we need to recreate
89 our heap and play tricks with our .bss section. Do this before
90 start up. (WARNING: Do not put any code before this section
91 that relies upon malloc () and runs in the dumped version. It
93 if (need_to_recreate_heap
)
95 char executable_path
[MAX_PATH
];
97 if (GetModuleFileName (NULL
, executable_path
, MAX_PATH
) == 0)
99 printf ("Failed to find path for executable.\n");
102 recreate_heap (executable_path
);
103 need_to_recreate_heap
= FALSE
;
106 /* The default behavior is to treat files as binary and patch up
107 text files appropriately, in accordance with the MSDOS code. */
110 /* This prevents ctrl-c's in shells running while we're suspended from
112 SetConsoleCtrlHandler ((PHANDLER_ROUTINE
) ctrl_c_handler
, TRUE
);
114 /* Invoke the NT CRT startup routine now that our housecleaning
117 /* determine WinMain args like crt0.c does */
118 hinst
= GetModuleHandle(NULL
);
119 lpCmdLine
= GetCommandLine();
120 nCmdShow
= SW_SHOWDEFAULT
;
125 /* Dump out .data and .bss sections into a new executable. */
127 unexec (char *new_name
, char *old_name
, void *start_data
, void *start_bss
,
130 file_data in_file
, out_file
;
131 char out_filename
[MAX_PATH
], in_filename
[MAX_PATH
];
135 /* Make sure that the input and output filenames have the
136 ".exe" extension...patch them up if they don't. */
137 strcpy (in_filename
, old_name
);
138 ptr
= in_filename
+ strlen (in_filename
) - 4;
139 if (strcmp (ptr
, ".exe"))
140 strcat (in_filename
, ".exe");
142 strcpy (out_filename
, new_name
);
143 ptr
= out_filename
+ strlen (out_filename
) - 4;
144 if (strcmp (ptr
, ".exe"))
145 strcat (out_filename
, ".exe");
147 printf ("Dumping from %s\n", in_filename
);
148 printf (" to %s\n", out_filename
);
150 /* We need to round off our heap to NT's allocation unit (64KB). */
151 round_heap (get_allocation_unit ());
153 /* Open the undumped executable file. */
154 open_input_file (&in_file
, in_filename
);
156 /* Get the interesting section info, like start and size of .bss... */
157 get_section_info (&in_file
);
159 /* The size of the dumped executable is the size of the original
160 executable plus the size of the heap and the size of the .bss section. */
161 heap_index_in_executable
= (unsigned long)
162 round_to_next ((unsigned char *) in_file
.size
, get_allocation_unit ());
163 size
= heap_index_in_executable
+ get_committed_heap_size () + bss_size
;
164 open_output_file (&out_file
, out_filename
, size
);
166 /* Set the flag (before dumping). */
167 need_to_recreate_heap
= TRUE
;
169 copy_executable_and_dump_data_section (&in_file
, &out_file
);
170 dump_bss_and_heap (&in_file
, &out_file
);
172 close_file_data (&in_file
);
173 close_file_data (&out_file
);
181 open_input_file (file_data
*p_file
, char *filename
)
186 unsigned long size
, upper_size
;
188 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
189 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
190 if (file
== INVALID_HANDLE_VALUE
)
192 printf ("Failed to open %s (%d)...bailing.\n",
193 filename
, GetLastError ());
197 size
= GetFileSize (file
, &upper_size
);
198 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READONLY
,
202 printf ("Failed to create file mapping of %s (%d)...bailing.\n",
203 filename
, GetLastError ());
207 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_READ
, 0, 0, size
);
210 printf ("Failed to map view of file of %s (%d)...bailing.\n",
211 filename
, GetLastError ());
215 p_file
->name
= filename
;
218 p_file
->file_mapping
= file_mapping
;
219 p_file
->file_base
= file_base
;
223 open_output_file (file_data
*p_file
, char *filename
, unsigned long size
)
230 file
= CreateFile (filename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
231 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
232 if (file
== INVALID_HANDLE_VALUE
)
235 printf ("open_output_file: Failed to open %s (%d).\n",
240 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READWRITE
,
245 printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
250 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_WRITE
, 0, 0, size
);
254 printf ("open_output_file: Failed to map view of file of %s (%d).\n",
259 p_file
->name
= filename
;
262 p_file
->file_mapping
= file_mapping
;
263 p_file
->file_base
= file_base
;
266 /* Close the system structures associated with the given file. */
268 close_file_data (file_data
*p_file
)
270 UnmapViewOfFile (p_file
->file_base
);
271 CloseHandle (p_file
->file_mapping
);
272 CloseHandle (p_file
->file
);
276 /* Routines to manipulate NT executable file sections. */
279 get_bss_info_from_map_file (file_data
*p_infile
, PUCHAR
*p_bss_start
,
283 char map_filename
[MAX_PATH
];
287 /* Overwrite the .exe extension on the executable file name with
288 the .map extension. */
289 strcpy (map_filename
, p_infile
->name
);
290 n
= strlen (map_filename
) - 3;
291 strcpy (&map_filename
[n
], "map");
293 map
= fopen (map_filename
, "r");
296 printf ("Failed to open map file %s, error %d...bailing out.\n",
297 map_filename
, GetLastError ());
301 while (fgets (buffer
, sizeof (buffer
), map
))
303 if (!(strstr (buffer
, ".bss") && strstr (buffer
, "DATA")))
305 n
= sscanf (buffer
, " %*d:%x %x", &start
, &len
);
308 printf ("Failed to scan the .bss section line:\n%s", buffer
);
313 *p_bss_start
= (PUCHAR
) start
;
314 *p_bss_size
= (DWORD
) len
;
318 get_section_size (PIMAGE_SECTION_HEADER p_section
)
320 /* The section size is in different locations in the different versions. */
321 switch (get_w32_minor_version ())
324 return p_section
->SizeOfRawData
;
326 return p_section
->Misc
.VirtualSize
;
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 if (!strcmp (section
->Name
, ".bss"))
368 /* The .bss section. */
369 ptr
= (char *) nt_header
->OptionalHeader
.ImageBase
+
370 section
->VirtualAddress
;
372 bss_size
= get_section_size (section
);
374 if (!strcmp (section
->Name
, ".data"))
376 /* From lastfile.c */
377 extern char my_edata
[];
379 /* The .data section. */
380 data_section
= section
;
381 ptr
= (char *) nt_header
->OptionalHeader
.ImageBase
+
382 section
->VirtualAddress
;
384 data_start_file
= section
->PointerToRawData
;
386 /* We want to only write Emacs data back to the executable,
387 not any of the library data (if library data is included,
388 then a dumped Emacs won't run on system versions other
389 than the one Emacs was dumped on). */
390 data_size
= my_edata
- data_start_va
;
395 if (!bss_start
&& !bss_size
)
397 /* Starting with MSVC 4.0, the .bss section has been eliminated
398 and appended virtually to the end of the .data section. Our
399 only hint about where the .bss section starts in the address
400 comes from the SizeOfRawData field in the .data section
401 header. Unfortunately, this field is only approximate, as it
402 is a rounded number and is typically rounded just beyond the
403 start of the .bss section. To find the start and size of the
404 .bss section exactly, we have to peek into the map file. */
405 get_bss_info_from_map_file (p_infile
, &ptr
, &bss_size
);
406 bss_start
= ptr
+ nt_header
->OptionalHeader
.ImageBase
407 + data_section
->VirtualAddress
;
412 /* The dump routines. */
415 copy_executable_and_dump_data_section (file_data
*p_infile
,
416 file_data
*p_outfile
)
418 unsigned char *data_file
, *data_va
;
419 unsigned long size
, index
;
421 /* Get a pointer to where the raw data should go in the executable file. */
422 data_file
= (char *) p_outfile
->file_base
+ data_start_file
;
424 /* Get a pointer to the raw data in our address space. */
425 data_va
= data_start_va
;
427 size
= (DWORD
) data_file
- (DWORD
) p_outfile
->file_base
;
428 printf ("Copying executable up to data section...\n");
429 printf ("\t0x%08x Offset in input file.\n", 0);
430 printf ("\t0x%08x Offset in output file.\n", 0);
431 printf ("\t0x%08x Size in bytes.\n", size
);
432 memcpy (p_outfile
->file_base
, p_infile
->file_base
, size
);
435 printf ("Dumping .data section...\n");
436 printf ("\t0x%08x Address in process.\n", data_va
);
437 printf ("\t0x%08x Offset in output file.\n",
438 data_file
- p_outfile
->file_base
);
439 printf ("\t0x%08x Size in bytes.\n", size
);
440 memcpy (data_file
, data_va
, size
);
442 index
= (DWORD
) data_file
+ size
- (DWORD
) p_outfile
->file_base
;
443 size
= p_infile
->size
- index
;
444 printf ("Copying rest of executable...\n");
445 printf ("\t0x%08x Offset in input file.\n", index
);
446 printf ("\t0x%08x Offset in output file.\n", index
);
447 printf ("\t0x%08x Size in bytes.\n", size
);
448 memcpy ((char *) p_outfile
->file_base
+ index
,
449 (char *) p_infile
->file_base
+ index
, size
);
453 dump_bss_and_heap (file_data
*p_infile
, file_data
*p_outfile
)
455 unsigned char *heap_data
, *bss_data
;
456 unsigned long size
, index
;
458 printf ("Dumping heap into executable...\n");
460 index
= heap_index_in_executable
;
461 size
= get_committed_heap_size ();
462 heap_data
= get_heap_start ();
464 printf ("\t0x%08x Heap start in process.\n", heap_data
);
465 printf ("\t0x%08x Heap offset in executable.\n", index
);
466 printf ("\t0x%08x Heap size in bytes.\n", size
);
468 memcpy ((PUCHAR
) p_outfile
->file_base
+ index
, heap_data
, size
);
470 printf ("Dumping .bss into executable...\n");
474 bss_data
= bss_start
;
476 printf ("\t0x%08x BSS start in process.\n", bss_data
);
477 printf ("\t0x%08x BSS offset in executable.\n", index
);
478 printf ("\t0x%08x BSS size in bytes.\n", size
);
479 memcpy ((char *) p_outfile
->file_base
+ index
, bss_data
, size
);
483 /* Reload and remap routines. */
486 /* Load the dumped .bss section into the .bss area of our address space. */
488 read_in_bss (char *filename
)
491 unsigned long size
, index
, n_read
, total_read
;
492 char buffer
[512], *bss
;
495 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
496 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
497 if (file
== INVALID_HANDLE_VALUE
)
503 /* Seek to where the .bss section is tucked away after the heap... */
504 index
= heap_index_in_executable
+ get_committed_heap_size ();
505 if (SetFilePointer (file
, index
, NULL
, FILE_BEGIN
) == 0xFFFFFFFF)
512 /* Ok, read in the saved .bss section and initialize all
513 uninitialized variables. */
514 if (!ReadFile (file
, bss_start
, bss_size
, &n_read
, NULL
))
523 /* Map the heap dumped into the executable file into our address space. */
525 map_in_heap (char *filename
)
530 unsigned long size
, upper_size
, n_read
;
533 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
534 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
535 if (file
== INVALID_HANDLE_VALUE
)
541 size
= GetFileSize (file
, &upper_size
);
542 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
)
571 /* Seek to the location of the heap data in the executable. */
572 i
= heap_index_in_executable
;
573 if (SetFilePointer (file
, i
, NULL
, FILE_BEGIN
) == 0xFFFFFFFF)
579 /* Read in the data. */
580 if (!ReadFile (file
, get_heap_start (),
581 get_committed_heap_size (), &n_read
, NULL
))