(xcar, xcdr): Print with /x.
[emacs.git] / src / unexw32.c
blobef4ed56fb9f3d6cb6ac245b23d79acbcbfad2d4a
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)
9 any later version.
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 */
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <windows.h>
29 extern BOOL ctrl_c_handler (unsigned long type);
31 #include "w32heap.h"
33 /* A convenient type for keeping all the info about a mapped file together. */
34 typedef struct file_data {
35 char *name;
36 unsigned long size;
37 HANDLE file;
38 HANDLE file_mapping;
39 unsigned char *file_base;
40 } file_data;
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;
63 DWORD data_size = 0;
65 /* Cached info about the .bss section in the executable. */
66 PUCHAR bss_start = 0;
67 DWORD bss_size = 0;
69 #ifdef HAVE_NTGUI
70 HINSTANCE hinst = NULL;
71 HINSTANCE hprevinst = NULL;
72 LPSTR lpCmdLine = "";
73 int nCmdShow = 0;
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 ()). */
80 void
81 _start (void)
83 extern void mainCRTStartup (void);
85 /* Cache system info, e.g., the NT page size. */
86 cache_system_info ();
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
92 won't work.) */
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");
100 exit (1);
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. */
108 _fmode = O_BINARY;
110 /* This prevents ctrl-c's in shells running while we're suspended from
111 having us exit. */
112 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
114 /* Invoke the NT CRT startup routine now that our housecleaning
115 is finished. */
116 #ifdef HAVE_NTGUI
117 /* determine WinMain args like crt0.c does */
118 hinst = GetModuleHandle(NULL);
119 lpCmdLine = GetCommandLine();
120 nCmdShow = SW_SHOWDEFAULT;
121 #endif
122 mainCRTStartup ();
125 /* Dump out .data and .bss sections into a new executable. */
126 void
127 unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
128 void *entry_address)
130 file_data in_file, out_file;
131 char out_filename[MAX_PATH], in_filename[MAX_PATH];
132 unsigned long size;
133 char *ptr;
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);
177 /* File handling. */
180 void
181 open_input_file (file_data *p_file, char *filename)
183 HANDLE file;
184 HANDLE file_mapping;
185 void *file_base;
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 ());
194 exit (1);
197 size = GetFileSize (file, &upper_size);
198 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
199 0, size, NULL);
200 if (!file_mapping)
202 printf ("Failed to create file mapping of %s (%d)...bailing.\n",
203 filename, GetLastError ());
204 exit (1);
207 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
208 if (file_base == 0)
210 printf ("Failed to map view of file of %s (%d)...bailing.\n",
211 filename, GetLastError ());
212 exit (1);
215 p_file->name = filename;
216 p_file->size = size;
217 p_file->file = file;
218 p_file->file_mapping = file_mapping;
219 p_file->file_base = file_base;
222 void
223 open_output_file (file_data *p_file, char *filename, unsigned long size)
225 HANDLE file;
226 HANDLE file_mapping;
227 void *file_base;
228 int i;
230 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
231 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
232 if (file == INVALID_HANDLE_VALUE)
234 i = GetLastError ();
235 printf ("open_output_file: Failed to open %s (%d).\n",
236 filename, i);
237 exit (1);
240 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
241 0, size, NULL);
242 if (!file_mapping)
244 i = GetLastError ();
245 printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
246 filename, i);
247 exit (1);
250 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
251 if (file_base == 0)
253 i = GetLastError ();
254 printf ("open_output_file: Failed to map view of file of %s (%d).\n",
255 filename, i);
256 exit (1);
259 p_file->name = filename;
260 p_file->size = size;
261 p_file->file = file;
262 p_file->file_mapping = file_mapping;
263 p_file->file_base = file_base;
266 /* Close the system structures associated with the given file. */
267 static void
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. */
278 static void
279 get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
280 DWORD *p_bss_size)
282 int n, start, len;
283 char map_filename[MAX_PATH];
284 char buffer[256];
285 FILE *map;
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");
294 if (!map)
296 printf ("Failed to open map file %s, error %d...bailing out.\n",
297 map_filename, GetLastError ());
298 exit (-1);
301 while (fgets (buffer, sizeof (buffer), map))
303 if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
304 continue;
305 n = sscanf (buffer, " %*d:%x %x", &start, &len);
306 if (n != 2)
308 printf ("Failed to scan the .bss section line:\n%s", buffer);
309 exit (-1);
311 break;
313 *p_bss_start = (PUCHAR) start;
314 *p_bss_size = (DWORD) len;
317 static unsigned long
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 ())
323 case 10:
324 return p_section->SizeOfRawData;
325 default:
326 return p_section->Misc.VirtualSize;
330 /* Flip through the executable and cache the info necessary for dumping. */
331 static void
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;
337 unsigned char *ptr;
338 int i;
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);
344 exit (1);
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",
351 p_infile->name);
352 exit (1);
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;
371 bss_start = ptr;
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;
383 data_start_va = ptr;
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;
392 section++;
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. */
414 static void
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);
434 size = data_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);
452 static void
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");
472 index += size;
473 size = bss_size;
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. */
487 void
488 read_in_bss (char *filename)
490 HANDLE file;
491 unsigned long size, index, n_read, total_read;
492 char buffer[512], *bss;
493 int i;
495 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
496 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
497 if (file == INVALID_HANDLE_VALUE)
499 i = GetLastError ();
500 exit (1);
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)
507 i = GetLastError ();
508 exit (1);
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))
516 i = GetLastError ();
517 exit (1);
520 CloseHandle (file);
523 /* Map the heap dumped into the executable file into our address space. */
524 void
525 map_in_heap (char *filename)
527 HANDLE file;
528 HANDLE file_mapping;
529 void *file_base;
530 unsigned long size, upper_size, n_read;
531 int i;
533 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
534 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
535 if (file == INVALID_HANDLE_VALUE)
537 i = GetLastError ();
538 exit (1);
541 size = GetFileSize (file, &upper_size);
542 file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
543 0, size, NULL);
544 if (!file_mapping)
546 i = GetLastError ();
547 exit (1);
550 size = get_committed_heap_size ();
551 file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0,
552 heap_index_in_executable, size,
553 get_heap_start ());
554 if (file_base != 0)
556 return;
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)
567 i = GetLastError ();
568 exit (1);
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)
575 i = GetLastError ();
576 exit (1);
579 /* Read in the data. */
580 if (!ReadFile (file, get_heap_start (),
581 get_committed_heap_size (), &n_read, NULL))
583 i = GetLastError ();
584 exit (1);
587 CloseHandle (file);