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 /* Basically, our "initialized" flag. */
43 BOOL need_to_recreate_heap
= FALSE
;
45 /* So we can find our heap in the file to recreate it. */
46 unsigned long heap_index_in_executable
= 0;
48 void open_input_file (file_data
*p_file
, char *name
);
49 void open_output_file (file_data
*p_file
, char *name
, unsigned long size
);
50 void close_file_data (file_data
*p_file
);
52 void get_section_info (file_data
*p_file
);
53 void copy_executable_and_dump_data_section (file_data
*, file_data
*);
54 void dump_bss_and_heap (file_data
*p_infile
, file_data
*p_outfile
);
56 /* Cached info about the .data section in the executable. */
57 PUCHAR data_start_va
= 0;
58 DWORD data_start_file
= 0;
61 /* Cached info about the .bss section in the executable. */
66 HINSTANCE hinst
= NULL
;
67 HINSTANCE hprevinst
= NULL
;
70 #endif /* HAVE_NTGUI */
72 /* Startup code for running on NT. When we are running as the dumped
73 version, we need to bootstrap our heap and .bss section into our
74 address space before we can actually hand off control to the startup
75 code supplied by NT (primarily because that code relies upon malloc ()). */
79 extern void mainCRTStartup (void);
81 /* Cache system info, e.g., the NT page size. */
84 /* If we're a dumped version of emacs then we need to recreate
85 our heap and play tricks with our .bss section. Do this before
86 start up. (WARNING: Do not put any code before this section
87 that relies upon malloc () and runs in the dumped version. It
89 if (need_to_recreate_heap
)
91 char executable_path
[MAX_PATH
];
93 if (GetModuleFileName (NULL
, executable_path
, MAX_PATH
) == 0)
95 printf ("Failed to find path for executable.\n");
98 recreate_heap (executable_path
);
99 need_to_recreate_heap
= FALSE
;
102 /* The default behavior is to treat files as binary and patch up
103 text files appropriately, in accordance with the MSDOS code. */
106 /* This prevents ctrl-c's in shells running while we're suspended from
108 SetConsoleCtrlHandler ((PHANDLER_ROUTINE
) ctrl_c_handler
, TRUE
);
110 /* Invoke the NT CRT startup routine now that our housecleaning
113 /* determine WinMain args like crt0.c does */
114 hinst
= GetModuleHandle(NULL
);
115 lpCmdLine
= GetCommandLine();
116 nCmdShow
= SW_SHOWDEFAULT
;
121 /* Dump out .data and .bss sections into a new executable. */
123 unexec (char *new_name
, char *old_name
, void *start_data
, void *start_bss
,
126 file_data in_file
, out_file
;
127 char out_filename
[MAX_PATH
], in_filename
[MAX_PATH
];
131 /* Make sure that the input and output filenames have the
132 ".exe" extension...patch them up if they don't. */
133 strcpy (in_filename
, old_name
);
134 ptr
= in_filename
+ strlen (in_filename
) - 4;
135 if (strcmp (ptr
, ".exe"))
136 strcat (in_filename
, ".exe");
138 strcpy (out_filename
, new_name
);
139 ptr
= out_filename
+ strlen (out_filename
) - 4;
140 if (strcmp (ptr
, ".exe"))
141 strcat (out_filename
, ".exe");
143 printf ("Dumping from %s\n", in_filename
);
144 printf (" to %s\n", out_filename
);
146 /* We need to round off our heap to NT's allocation unit (64KB). */
147 round_heap (get_allocation_unit ());
149 /* Open the undumped executable file. */
150 open_input_file (&in_file
, in_filename
);
152 /* Get the interesting section info, like start and size of .bss... */
153 get_section_info (&in_file
);
155 /* The size of the dumped executable is the size of the original
156 executable plus the size of the heap and the size of the .bss section. */
157 heap_index_in_executable
= (unsigned long)
158 round_to_next ((unsigned char *) in_file
.size
, get_allocation_unit ());
159 size
= heap_index_in_executable
+ get_committed_heap_size () + bss_size
;
160 open_output_file (&out_file
, out_filename
, size
);
162 /* Set the flag (before dumping). */
163 need_to_recreate_heap
= TRUE
;
165 copy_executable_and_dump_data_section (&in_file
, &out_file
);
166 dump_bss_and_heap (&in_file
, &out_file
);
168 close_file_data (&in_file
);
169 close_file_data (&out_file
);
177 open_input_file (file_data
*p_file
, char *filename
)
182 unsigned long size
, upper_size
;
184 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
185 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
186 if (file
== INVALID_HANDLE_VALUE
)
188 printf ("Failed to open %s (%d)...bailing.\n",
189 filename
, GetLastError ());
193 size
= GetFileSize (file
, &upper_size
);
194 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READONLY
,
198 printf ("Failed to create file mapping of %s (%d)...bailing.\n",
199 filename
, GetLastError ());
203 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_READ
, 0, 0, size
);
206 printf ("Failed to map view of file of %s (%d)...bailing.\n",
207 filename
, GetLastError ());
211 p_file
->name
= filename
;
214 p_file
->file_mapping
= file_mapping
;
215 p_file
->file_base
= file_base
;
219 open_output_file (file_data
*p_file
, char *filename
, unsigned long size
)
226 file
= CreateFile (filename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
227 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
228 if (file
== INVALID_HANDLE_VALUE
)
231 printf ("open_output_file: Failed to open %s (%d).\n",
236 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_READWRITE
,
241 printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
246 file_base
= MapViewOfFile (file_mapping
, FILE_MAP_WRITE
, 0, 0, size
);
250 printf ("open_output_file: Failed to map view of file of %s (%d).\n",
255 p_file
->name
= filename
;
258 p_file
->file_mapping
= file_mapping
;
259 p_file
->file_base
= file_base
;
262 /* Close the system structures associated with the given file. */
264 close_file_data (file_data
*p_file
)
266 UnmapViewOfFile (p_file
->file_base
);
267 CloseHandle (p_file
->file_mapping
);
268 CloseHandle (p_file
->file
);
272 /* Routines to manipulate NT executable file sections. */
275 get_bss_info_from_map_file (file_data
*p_infile
, PUCHAR
*p_bss_start
,
279 char map_filename
[MAX_PATH
];
283 /* Overwrite the .exe extension on the executable file name with
284 the .map extension. */
285 strcpy (map_filename
, p_infile
->name
);
286 n
= strlen (map_filename
) - 3;
287 strcpy (&map_filename
[n
], "map");
289 map
= fopen (map_filename
, "r");
292 printf ("Failed to open map file %s, error %d...bailing out.\n",
293 map_filename
, GetLastError ());
297 while (fgets (buffer
, sizeof (buffer
), map
))
299 if (!(strstr (buffer
, ".bss") && strstr (buffer
, "DATA")))
301 n
= sscanf (buffer
, " %*d:%x %x", &start
, &len
);
304 printf ("Failed to scan the .bss section line:\n%s", buffer
);
309 *p_bss_start
= (PUCHAR
) start
;
310 *p_bss_size
= (DWORD
) len
;
314 get_section_size (PIMAGE_SECTION_HEADER p_section
)
316 /* The section size is in different locations in the different versions. */
317 switch (get_w32_minor_version ())
320 return p_section
->SizeOfRawData
;
322 return p_section
->Misc
.VirtualSize
;
326 /* Flip through the executable and cache the info necessary for dumping. */
328 get_section_info (file_data
*p_infile
)
330 PIMAGE_DOS_HEADER dos_header
;
331 PIMAGE_NT_HEADERS nt_header
;
332 PIMAGE_SECTION_HEADER section
, data_section
;
336 dos_header
= (PIMAGE_DOS_HEADER
) p_infile
->file_base
;
337 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
)
339 printf ("Unknown EXE header in %s...bailing.\n", p_infile
->name
);
342 nt_header
= (PIMAGE_NT_HEADERS
) (((unsigned long) dos_header
) +
343 dos_header
->e_lfanew
);
344 if (nt_header
== NULL
)
346 printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
351 /* Check the NT header signature ... */
352 if (nt_header
->Signature
!= IMAGE_NT_SIGNATURE
)
354 printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
355 nt_header
->Signature
, p_infile
->name
);
358 /* Flip through the sections for .data and .bss ... */
359 section
= (PIMAGE_SECTION_HEADER
) IMAGE_FIRST_SECTION (nt_header
);
360 for (i
= 0; i
< nt_header
->FileHeader
.NumberOfSections
; i
++)
362 if (!strcmp (section
->Name
, ".bss"))
364 /* The .bss section. */
365 ptr
= (char *) nt_header
->OptionalHeader
.ImageBase
+
366 section
->VirtualAddress
;
368 bss_size
= get_section_size (section
);
370 if (!strcmp (section
->Name
, ".data"))
372 /* From lastfile.c */
373 extern char my_edata
[];
375 /* The .data section. */
376 data_section
= section
;
377 ptr
= (char *) nt_header
->OptionalHeader
.ImageBase
+
378 section
->VirtualAddress
;
380 data_start_file
= section
->PointerToRawData
;
382 /* We want to only write Emacs data back to the executable,
383 not any of the library data (if library data is included,
384 then a dumped Emacs won't run on system versions other
385 than the one Emacs was dumped on). */
386 data_size
= my_edata
- data_start_va
;
391 if (!bss_start
&& !bss_size
)
393 /* Starting with MSVC 4.0, the .bss section has been eliminated
394 and appended virtually to the end of the .data section. Our
395 only hint about where the .bss section starts in the address
396 comes from the SizeOfRawData field in the .data section
397 header. Unfortunately, this field is only approximate, as it
398 is a rounded number and is typically rounded just beyond the
399 start of the .bss section. To find the start and size of the
400 .bss section exactly, we have to peek into the map file. */
401 get_bss_info_from_map_file (p_infile
, &ptr
, &bss_size
);
402 bss_start
= ptr
+ nt_header
->OptionalHeader
.ImageBase
403 + data_section
->VirtualAddress
;
408 /* The dump routines. */
411 copy_executable_and_dump_data_section (file_data
*p_infile
,
412 file_data
*p_outfile
)
414 unsigned char *data_file
, *data_va
;
415 unsigned long size
, index
;
417 /* Get a pointer to where the raw data should go in the executable file. */
418 data_file
= (char *) p_outfile
->file_base
+ data_start_file
;
420 /* Get a pointer to the raw data in our address space. */
421 data_va
= data_start_va
;
423 size
= (DWORD
) data_file
- (DWORD
) p_outfile
->file_base
;
424 printf ("Copying executable up to data section...\n");
425 printf ("\t0x%08x Offset in input file.\n", 0);
426 printf ("\t0x%08x Offset in output file.\n", 0);
427 printf ("\t0x%08x Size in bytes.\n", size
);
428 memcpy (p_outfile
->file_base
, p_infile
->file_base
, size
);
431 printf ("Dumping .data section...\n");
432 printf ("\t0x%08x Address in process.\n", data_va
);
433 printf ("\t0x%08x Offset in output file.\n",
434 data_file
- p_outfile
->file_base
);
435 printf ("\t0x%08x Size in bytes.\n", size
);
436 memcpy (data_file
, data_va
, size
);
438 index
= (DWORD
) data_file
+ size
- (DWORD
) p_outfile
->file_base
;
439 size
= p_infile
->size
- index
;
440 printf ("Copying rest of executable...\n");
441 printf ("\t0x%08x Offset in input file.\n", index
);
442 printf ("\t0x%08x Offset in output file.\n", index
);
443 printf ("\t0x%08x Size in bytes.\n", size
);
444 memcpy ((char *) p_outfile
->file_base
+ index
,
445 (char *) p_infile
->file_base
+ index
, size
);
449 dump_bss_and_heap (file_data
*p_infile
, file_data
*p_outfile
)
451 unsigned char *heap_data
, *bss_data
;
452 unsigned long size
, index
;
454 printf ("Dumping heap into executable...\n");
456 index
= heap_index_in_executable
;
457 size
= get_committed_heap_size ();
458 heap_data
= get_heap_start ();
460 printf ("\t0x%08x Heap start in process.\n", heap_data
);
461 printf ("\t0x%08x Heap offset in executable.\n", index
);
462 printf ("\t0x%08x Heap size in bytes.\n", size
);
464 memcpy ((PUCHAR
) p_outfile
->file_base
+ index
, heap_data
, size
);
466 printf ("Dumping .bss into executable...\n");
470 bss_data
= bss_start
;
472 printf ("\t0x%08x BSS start in process.\n", bss_data
);
473 printf ("\t0x%08x BSS offset in executable.\n", index
);
474 printf ("\t0x%08x BSS size in bytes.\n", size
);
475 memcpy ((char *) p_outfile
->file_base
+ index
, bss_data
, size
);
479 /* Reload and remap routines. */
482 /* Load the dumped .bss section into the .bss area of our address space. */
484 read_in_bss (char *filename
)
487 unsigned long size
, index
, n_read
, total_read
;
488 char buffer
[512], *bss
;
491 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
492 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
493 if (file
== INVALID_HANDLE_VALUE
)
499 /* Seek to where the .bss section is tucked away after the heap... */
500 index
= heap_index_in_executable
+ get_committed_heap_size ();
501 if (SetFilePointer (file
, index
, NULL
, FILE_BEGIN
) == 0xFFFFFFFF)
508 /* Ok, read in the saved .bss section and initialize all
509 uninitialized variables. */
510 if (!ReadFile (file
, bss_start
, bss_size
, &n_read
, NULL
))
519 /* Map the heap dumped into the executable file into our address space. */
521 map_in_heap (char *filename
)
526 unsigned long size
, upper_size
, n_read
;
529 file
= CreateFile (filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
530 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
531 if (file
== INVALID_HANDLE_VALUE
)
537 size
= GetFileSize (file
, &upper_size
);
538 file_mapping
= CreateFileMapping (file
, NULL
, PAGE_WRITECOPY
,
546 size
= get_committed_heap_size ();
547 file_base
= MapViewOfFileEx (file_mapping
, FILE_MAP_COPY
, 0,
548 heap_index_in_executable
, size
,
555 /* If we don't succeed with the mapping, then copy from the
556 data into the heap. */
558 CloseHandle (file_mapping
);
560 if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
561 MEM_RESERVE
| MEM_COMMIT
, PAGE_READWRITE
) == NULL
)
567 /* Seek to the location of the heap data in the executable. */
568 i
= heap_index_in_executable
;
569 if (SetFilePointer (file
, i
, NULL
, FILE_BEGIN
) == 0xFFFFFFFF)
575 /* Read in the data. */
576 if (!ReadFile (file
, get_heap_start (),
577 get_committed_heap_size (), &n_read
, NULL
))