Change identifiers of the form win32* to w32*.
[bpt/emacs.git] / src / unexw32.c
1 /* unexec for GNU Emacs on Windows NT.
2 Copyright (C) 1994 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
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.
10
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.
15
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.
20
21 Geoff Voelker (voelker@cs.washington.edu) 8-12-94
22 */
23
24 #include <stdlib.h> /* _fmode */
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <windows.h>
28
29 extern BOOL ctrl_c_handler (unsigned long type);
30
31 #include "ntheap.h"
32
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;
41
42 /* Basically, our "initialized" flag. */
43 BOOL need_to_recreate_heap = FALSE;
44
45 /* So we can find our heap in the file to recreate it. */
46 unsigned long heap_index_in_executable = 0;
47
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);
51
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);
55
56 /* Cached info about the .data section in the executable. */
57 PUCHAR data_start_va = 0;
58 DWORD data_start_file = 0;
59 DWORD data_size = 0;
60
61 /* Cached info about the .bss section in the executable. */
62 PUCHAR bss_start = 0;
63 DWORD bss_size = 0;
64
65 #ifdef HAVE_NTGUI
66 HINSTANCE hinst = NULL;
67 HINSTANCE hprevinst = NULL;
68 LPSTR lpCmdLine = "";
69 int nCmdShow = 0;
70 #endif /* HAVE_NTGUI */
71
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 ()). */
76 void
77 _start (void)
78 {
79 extern void mainCRTStartup (void);
80
81 /* Cache system info, e.g., the NT page size. */
82 cache_system_info ();
83
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
88 won't work.) */
89 if (need_to_recreate_heap)
90 {
91 char executable_path[MAX_PATH];
92
93 if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0)
94 {
95 printf ("Failed to find path for executable.\n");
96 exit (1);
97 }
98 recreate_heap (executable_path);
99 need_to_recreate_heap = FALSE;
100 }
101
102 /* The default behavior is to treat files as binary and patch up
103 text files appropriately, in accordance with the MSDOS code. */
104 _fmode = O_BINARY;
105
106 /* This prevents ctrl-c's in shells running while we're suspended from
107 having us exit. */
108 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
109
110 /* Invoke the NT CRT startup routine now that our housecleaning
111 is finished. */
112 #ifdef HAVE_NTGUI
113 /* determine WinMain args like crt0.c does */
114 hinst = GetModuleHandle(NULL);
115 lpCmdLine = GetCommandLine();
116 nCmdShow = SW_SHOWDEFAULT;
117 #endif
118 mainCRTStartup ();
119 }
120
121 /* Dump out .data and .bss sections into a new executable. */
122 void
123 unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
124 void *entry_address)
125 {
126 file_data in_file, out_file;
127 char out_filename[MAX_PATH], in_filename[MAX_PATH];
128 unsigned long size;
129 char *ptr;
130
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");
137
138 strcpy (out_filename, new_name);
139 ptr = out_filename + strlen (out_filename) - 4;
140 if (strcmp (ptr, ".exe"))
141 strcat (out_filename, ".exe");
142
143 printf ("Dumping from %s\n", in_filename);
144 printf (" to %s\n", out_filename);
145
146 /* We need to round off our heap to NT's allocation unit (64KB). */
147 round_heap (get_allocation_unit ());
148
149 /* Open the undumped executable file. */
150 open_input_file (&in_file, in_filename);
151
152 /* Get the interesting section info, like start and size of .bss... */
153 get_section_info (&in_file);
154
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);
161
162 /* Set the flag (before dumping). */
163 need_to_recreate_heap = TRUE;
164
165 copy_executable_and_dump_data_section (&in_file, &out_file);
166 dump_bss_and_heap (&in_file, &out_file);
167
168 close_file_data (&in_file);
169 close_file_data (&out_file);
170 }
171
172
173 /* File handling. */
174
175
176 void
177 open_input_file (file_data *p_file, char *filename)
178 {
179 HANDLE file;
180 HANDLE file_mapping;
181 void *file_base;
182 unsigned long size, upper_size;
183
184 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
185 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
186 if (file == INVALID_HANDLE_VALUE)
187 {
188 printf ("Failed to open %s (%d)...bailing.\n",
189 filename, GetLastError ());
190 exit (1);
191 }
192
193 size = GetFileSize (file, &upper_size);
194 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
195 0, size, NULL);
196 if (!file_mapping)
197 {
198 printf ("Failed to create file mapping of %s (%d)...bailing.\n",
199 filename, GetLastError ());
200 exit (1);
201 }
202
203 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
204 if (file_base == 0)
205 {
206 printf ("Failed to map view of file of %s (%d)...bailing.\n",
207 filename, GetLastError ());
208 exit (1);
209 }
210
211 p_file->name = filename;
212 p_file->size = size;
213 p_file->file = file;
214 p_file->file_mapping = file_mapping;
215 p_file->file_base = file_base;
216 }
217
218 void
219 open_output_file (file_data *p_file, char *filename, unsigned long size)
220 {
221 HANDLE file;
222 HANDLE file_mapping;
223 void *file_base;
224 int i;
225
226 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
227 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
228 if (file == INVALID_HANDLE_VALUE)
229 {
230 i = GetLastError ();
231 printf ("open_output_file: Failed to open %s (%d).\n",
232 filename, i);
233 exit (1);
234 }
235
236 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
237 0, size, NULL);
238 if (!file_mapping)
239 {
240 i = GetLastError ();
241 printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
242 filename, i);
243 exit (1);
244 }
245
246 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
247 if (file_base == 0)
248 {
249 i = GetLastError ();
250 printf ("open_output_file: Failed to map view of file of %s (%d).\n",
251 filename, i);
252 exit (1);
253 }
254
255 p_file->name = filename;
256 p_file->size = size;
257 p_file->file = file;
258 p_file->file_mapping = file_mapping;
259 p_file->file_base = file_base;
260 }
261
262 /* Close the system structures associated with the given file. */
263 static void
264 close_file_data (file_data *p_file)
265 {
266 UnmapViewOfFile (p_file->file_base);
267 CloseHandle (p_file->file_mapping);
268 CloseHandle (p_file->file);
269 }
270
271
272 /* Routines to manipulate NT executable file sections. */
273
274 static void
275 get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
276 DWORD *p_bss_size)
277 {
278 int n, start, len;
279 char map_filename[MAX_PATH];
280 char buffer[256];
281 FILE *map;
282
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");
288
289 map = fopen (map_filename, "r");
290 if (!map)
291 {
292 printf ("Failed to open map file %s, error %d...bailing out.\n",
293 map_filename, GetLastError ());
294 exit (-1);
295 }
296
297 while (fgets (buffer, sizeof (buffer), map))
298 {
299 if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
300 continue;
301 n = sscanf (buffer, " %*d:%x %x", &start, &len);
302 if (n != 2)
303 {
304 printf ("Failed to scan the .bss section line:\n%s", buffer);
305 exit (-1);
306 }
307 break;
308 }
309 *p_bss_start = (PUCHAR) start;
310 *p_bss_size = (DWORD) len;
311 }
312
313 static unsigned long
314 get_section_size (PIMAGE_SECTION_HEADER p_section)
315 {
316 /* The section size is in different locations in the different versions. */
317 switch (get_w32_minor_version ())
318 {
319 case 10:
320 return p_section->SizeOfRawData;
321 default:
322 return p_section->Misc.VirtualSize;
323 }
324 }
325
326 /* Flip through the executable and cache the info necessary for dumping. */
327 static void
328 get_section_info (file_data *p_infile)
329 {
330 PIMAGE_DOS_HEADER dos_header;
331 PIMAGE_NT_HEADERS nt_header;
332 PIMAGE_SECTION_HEADER section, data_section;
333 unsigned char *ptr;
334 int i;
335
336 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
337 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
338 {
339 printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
340 exit (1);
341 }
342 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
343 dos_header->e_lfanew);
344 if (nt_header == NULL)
345 {
346 printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
347 p_infile->name);
348 exit (1);
349 }
350
351 /* Check the NT header signature ... */
352 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
353 {
354 printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
355 nt_header->Signature, p_infile->name);
356 }
357
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++)
361 {
362 if (!strcmp (section->Name, ".bss"))
363 {
364 /* The .bss section. */
365 ptr = (char *) nt_header->OptionalHeader.ImageBase +
366 section->VirtualAddress;
367 bss_start = ptr;
368 bss_size = get_section_size (section);
369 }
370 if (!strcmp (section->Name, ".data"))
371 {
372 /* From lastfile.c */
373 extern char my_edata[];
374
375 /* The .data section. */
376 data_section = section;
377 ptr = (char *) nt_header->OptionalHeader.ImageBase +
378 section->VirtualAddress;
379 data_start_va = ptr;
380 data_start_file = section->PointerToRawData;
381
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;
387 }
388 section++;
389 }
390
391 if (!bss_start && !bss_size)
392 {
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;
404 }
405 }
406
407
408 /* The dump routines. */
409
410 static void
411 copy_executable_and_dump_data_section (file_data *p_infile,
412 file_data *p_outfile)
413 {
414 unsigned char *data_file, *data_va;
415 unsigned long size, index;
416
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;
419
420 /* Get a pointer to the raw data in our address space. */
421 data_va = data_start_va;
422
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);
429
430 size = data_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);
437
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);
446 }
447
448 static void
449 dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
450 {
451 unsigned char *heap_data, *bss_data;
452 unsigned long size, index;
453
454 printf ("Dumping heap into executable...\n");
455
456 index = heap_index_in_executable;
457 size = get_committed_heap_size ();
458 heap_data = get_heap_start ();
459
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);
463
464 memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
465
466 printf ("Dumping .bss into executable...\n");
467
468 index += size;
469 size = bss_size;
470 bss_data = bss_start;
471
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);
476 }
477
478
479 /* Reload and remap routines. */
480
481
482 /* Load the dumped .bss section into the .bss area of our address space. */
483 void
484 read_in_bss (char *filename)
485 {
486 HANDLE file;
487 unsigned long size, index, n_read, total_read;
488 char buffer[512], *bss;
489 int i;
490
491 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
492 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
493 if (file == INVALID_HANDLE_VALUE)
494 {
495 i = GetLastError ();
496 exit (1);
497 }
498
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)
502 {
503 i = GetLastError ();
504 exit (1);
505 }
506
507
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))
511 {
512 i = GetLastError ();
513 exit (1);
514 }
515
516 CloseHandle (file);
517 }
518
519 /* Map the heap dumped into the executable file into our address space. */
520 void
521 map_in_heap (char *filename)
522 {
523 HANDLE file;
524 HANDLE file_mapping;
525 void *file_base;
526 unsigned long size, upper_size, n_read;
527 int i;
528
529 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
530 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
531 if (file == INVALID_HANDLE_VALUE)
532 {
533 i = GetLastError ();
534 exit (1);
535 }
536
537 size = GetFileSize (file, &upper_size);
538 file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
539 0, size, NULL);
540 if (!file_mapping)
541 {
542 i = GetLastError ();
543 exit (1);
544 }
545
546 size = get_committed_heap_size ();
547 file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0,
548 heap_index_in_executable, size,
549 get_heap_start ());
550 if (file_base != 0)
551 {
552 return;
553 }
554
555 /* If we don't succeed with the mapping, then copy from the
556 data into the heap. */
557
558 CloseHandle (file_mapping);
559
560 if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
561 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
562 {
563 i = GetLastError ();
564 exit (1);
565 }
566
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)
570 {
571 i = GetLastError ();
572 exit (1);
573 }
574
575 /* Read in the data. */
576 if (!ReadFile (file, get_heap_start (),
577 get_committed_heap_size (), &n_read, NULL))
578 {
579 i = GetLastError ();
580 exit (1);
581 }
582
583 CloseHandle (file);
584 }