makefile.nt (TLIB0, TOBJ, OBJ0): New macro.
[bpt/emacs.git] / src / unexw32.c
CommitLineData
3b7ad313 1/* unexec for GNU Emacs on Windows NT.
2147fb50
KH
2 Copyright (C) 1994 Free Software Foundation, Inc.
3
3b7ad313 4This file is part of GNU Emacs.
2147fb50 5
3b7ad313
EN
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
2147fb50 10
3b7ad313
EN
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
2147fb50 15
3b7ad313
EN
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
2147fb50
KH
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
29extern BOOL ctrl_c_handler (unsigned long type);
30
489f9371 31#include "w32heap.h"
2147fb50
KH
32
33/* A convenient type for keeping all the info about a mapped file together. */
34typedef 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
e54c8cd1
GV
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")
45
2147fb50
KH
46/* Basically, our "initialized" flag. */
47BOOL need_to_recreate_heap = FALSE;
48
49/* So we can find our heap in the file to recreate it. */
50unsigned long heap_index_in_executable = 0;
51
52void open_input_file (file_data *p_file, char *name);
53void open_output_file (file_data *p_file, char *name, unsigned long size);
54void close_file_data (file_data *p_file);
55
56void get_section_info (file_data *p_file);
57void copy_executable_and_dump_data_section (file_data *, file_data *);
58void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
59
60/* Cached info about the .data section in the executable. */
61PUCHAR data_start_va = 0;
62DWORD data_start_file = 0;
63DWORD data_size = 0;
64
65/* Cached info about the .bss section in the executable. */
66PUCHAR bss_start = 0;
67DWORD bss_size = 0;
68
cd6885f3
GV
69#ifdef HAVE_NTGUI
70HINSTANCE hinst = NULL;
71HINSTANCE hprevinst = NULL;
72LPSTR lpCmdLine = "";
73int nCmdShow = 0;
cd6885f3
GV
74#endif /* HAVE_NTGUI */
75
2147fb50
KH
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 ()). */
80void
81_start (void)
82{
83 extern void mainCRTStartup (void);
84
85 /* Cache system info, e.g., the NT page size. */
86 cache_system_info ();
87
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)
94 {
95 char executable_path[MAX_PATH];
96
97 if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0)
98 {
99 printf ("Failed to find path for executable.\n");
100 exit (1);
101 }
102 recreate_heap (executable_path);
103 need_to_recreate_heap = FALSE;
104 }
105
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;
109
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);
113
114 /* Invoke the NT CRT startup routine now that our housecleaning
115 is finished. */
cd6885f3 116#ifdef HAVE_NTGUI
c2ccbd43
GV
117 /* determine WinMain args like crt0.c does */
118 hinst = GetModuleHandle(NULL);
119 lpCmdLine = GetCommandLine();
120 nCmdShow = SW_SHOWDEFAULT;
121#endif
2147fb50
KH
122 mainCRTStartup ();
123}
124
8e6208c5 125/* Dump out .data and .bss sections into a new executable. */
2147fb50
KH
126void
127unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
128 void *entry_address)
129{
130 file_data in_file, out_file;
131 char out_filename[MAX_PATH], in_filename[MAX_PATH];
132 unsigned long size;
133 char *ptr;
134
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");
141
142 strcpy (out_filename, new_name);
143 ptr = out_filename + strlen (out_filename) - 4;
144 if (strcmp (ptr, ".exe"))
145 strcat (out_filename, ".exe");
146
147 printf ("Dumping from %s\n", in_filename);
148 printf (" to %s\n", out_filename);
149
150 /* We need to round off our heap to NT's allocation unit (64KB). */
151 round_heap (get_allocation_unit ());
152
153 /* Open the undumped executable file. */
154 open_input_file (&in_file, in_filename);
155
156 /* Get the interesting section info, like start and size of .bss... */
157 get_section_info (&in_file);
158
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. */
198fdd15
GV
161 heap_index_in_executable = (unsigned long)
162 round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
2147fb50
KH
163 size = heap_index_in_executable + get_committed_heap_size () + bss_size;
164 open_output_file (&out_file, out_filename, size);
165
166 /* Set the flag (before dumping). */
167 need_to_recreate_heap = TRUE;
168
169 copy_executable_and_dump_data_section (&in_file, &out_file);
170 dump_bss_and_heap (&in_file, &out_file);
171
172 close_file_data (&in_file);
173 close_file_data (&out_file);
174}
175
176
177/* File handling. */
178
179
180void
181open_input_file (file_data *p_file, char *filename)
182{
183 HANDLE file;
184 HANDLE file_mapping;
185 void *file_base;
186 unsigned long size, upper_size;
187
188 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
189 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
190 if (file == INVALID_HANDLE_VALUE)
191 {
192 printf ("Failed to open %s (%d)...bailing.\n",
193 filename, GetLastError ());
194 exit (1);
195 }
196
197 size = GetFileSize (file, &upper_size);
198 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
199 0, size, NULL);
200 if (!file_mapping)
201 {
202 printf ("Failed to create file mapping of %s (%d)...bailing.\n",
203 filename, GetLastError ());
204 exit (1);
205 }
206
207 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
208 if (file_base == 0)
209 {
210 printf ("Failed to map view of file of %s (%d)...bailing.\n",
211 filename, GetLastError ());
212 exit (1);
213 }
214
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;
220}
221
222void
223open_output_file (file_data *p_file, char *filename, unsigned long size)
224{
225 HANDLE file;
226 HANDLE file_mapping;
227 void *file_base;
cd6885f3
GV
228 int i;
229
2147fb50
KH
230 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
231 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
232 if (file == INVALID_HANDLE_VALUE)
233 {
cd6885f3 234 i = GetLastError ();
2147fb50 235 printf ("open_output_file: Failed to open %s (%d).\n",
cd6885f3 236 filename, i);
2147fb50
KH
237 exit (1);
238 }
239
240 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
241 0, size, NULL);
242 if (!file_mapping)
243 {
cd6885f3 244 i = GetLastError ();
2147fb50 245 printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
cd6885f3 246 filename, i);
2147fb50
KH
247 exit (1);
248 }
249
250 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
251 if (file_base == 0)
252 {
cd6885f3 253 i = GetLastError ();
2147fb50 254 printf ("open_output_file: Failed to map view of file of %s (%d).\n",
cd6885f3 255 filename, i);
2147fb50
KH
256 exit (1);
257 }
258
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;
264}
265
266/* Close the system structures associated with the given file. */
267static void
268close_file_data (file_data *p_file)
269{
270 UnmapViewOfFile (p_file->file_base);
271 CloseHandle (p_file->file_mapping);
272 CloseHandle (p_file->file);
273}
274
275
276/* Routines to manipulate NT executable file sections. */
277
a610993d
GV
278static void
279get_bss_info_from_map_file (file_data *p_infile, PUCHAR *p_bss_start,
280 DWORD *p_bss_size)
281{
282 int n, start, len;
283 char map_filename[MAX_PATH];
284 char buffer[256];
285 FILE *map;
286
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");
292
293 map = fopen (map_filename, "r");
294 if (!map)
295 {
296 printf ("Failed to open map file %s, error %d...bailing out.\n",
297 map_filename, GetLastError ());
298 exit (-1);
299 }
300
301 while (fgets (buffer, sizeof (buffer), map))
302 {
303 if (!(strstr (buffer, ".bss") && strstr (buffer, "DATA")))
304 continue;
305 n = sscanf (buffer, " %*d:%x %x", &start, &len);
306 if (n != 2)
307 {
308 printf ("Failed to scan the .bss section line:\n%s", buffer);
309 exit (-1);
310 }
311 break;
312 }
313 *p_bss_start = (PUCHAR) start;
314 *p_bss_size = (DWORD) len;
315}
2147fb50
KH
316
317static unsigned long
318get_section_size (PIMAGE_SECTION_HEADER p_section)
319{
320 /* The section size is in different locations in the different versions. */
fbd6baed 321 switch (get_w32_minor_version ())
2147fb50
KH
322 {
323 case 10:
324 return p_section->SizeOfRawData;
325 default:
326 return p_section->Misc.VirtualSize;
327 }
328}
329
330/* Flip through the executable and cache the info necessary for dumping. */
331static void
332get_section_info (file_data *p_infile)
333{
334 PIMAGE_DOS_HEADER dos_header;
335 PIMAGE_NT_HEADERS nt_header;
a610993d 336 PIMAGE_SECTION_HEADER section, data_section;
2147fb50
KH
337 unsigned char *ptr;
338 int i;
339
340 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
341 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
342 {
343 printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
344 exit (1);
345 }
346 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
347 dos_header->e_lfanew);
348 if (nt_header == NULL)
349 {
350 printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
351 p_infile->name);
352 exit (1);
353 }
354
355 /* Check the NT header signature ... */
356 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
357 {
358 printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
359 nt_header->Signature, p_infile->name);
360 }
361
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++)
365 {
366 if (!strcmp (section->Name, ".bss"))
367 {
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);
373 }
374 if (!strcmp (section->Name, ".data"))
375 {
198fdd15
GV
376 /* From lastfile.c */
377 extern char my_edata[];
378
2147fb50 379 /* The .data section. */
a610993d 380 data_section = section;
c2ccbd43 381 ptr = (char *) nt_header->OptionalHeader.ImageBase +
2147fb50
KH
382 section->VirtualAddress;
383 data_start_va = ptr;
384 data_start_file = section->PointerToRawData;
198fdd15
GV
385
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;
2147fb50
KH
391 }
392 section++;
393 }
a610993d
GV
394
395 if (!bss_start && !bss_size)
396 {
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;
408 }
2147fb50
KH
409}
410
411
412/* The dump routines. */
413
414static void
415copy_executable_and_dump_data_section (file_data *p_infile,
416 file_data *p_outfile)
417{
418 unsigned char *data_file, *data_va;
419 unsigned long size, index;
420
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;
423
424 /* Get a pointer to the raw data in our address space. */
425 data_va = data_start_va;
426
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);
433
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);
441
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);
450}
451
452static void
453dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
454{
455 unsigned char *heap_data, *bss_data;
456 unsigned long size, index;
457
458 printf ("Dumping heap into executable...\n");
459
460 index = heap_index_in_executable;
461 size = get_committed_heap_size ();
462 heap_data = get_heap_start ();
463
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);
467
468 memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
469
470 printf ("Dumping .bss into executable...\n");
471
472 index += size;
473 size = bss_size;
474 bss_data = bss_start;
475
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);
480}
481
482
483/* Reload and remap routines. */
484
485
486/* Load the dumped .bss section into the .bss area of our address space. */
487void
488read_in_bss (char *filename)
489{
490 HANDLE file;
491 unsigned long size, index, n_read, total_read;
492 char buffer[512], *bss;
493 int i;
494
495 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
496 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
497 if (file == INVALID_HANDLE_VALUE)
498 {
499 i = GetLastError ();
500 exit (1);
501 }
502
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)
506 {
507 i = GetLastError ();
508 exit (1);
509 }
510
511
512 /* Ok, read in the saved .bss section and initialize all
513 uninitialized variables. */
198fdd15 514 if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
2147fb50 515 {
198fdd15
GV
516 i = GetLastError ();
517 exit (1);
2147fb50 518 }
198fdd15 519
2147fb50
KH
520 CloseHandle (file);
521}
522
523/* Map the heap dumped into the executable file into our address space. */
524void
525map_in_heap (char *filename)
526{
527 HANDLE file;
528 HANDLE file_mapping;
529 void *file_base;
198fdd15 530 unsigned long size, upper_size, n_read;
2147fb50
KH
531 int i;
532
533 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
534 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
535 if (file == INVALID_HANDLE_VALUE)
536 {
537 i = GetLastError ();
538 exit (1);
539 }
540
541 size = GetFileSize (file, &upper_size);
542 file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
543 0, size, NULL);
544 if (!file_mapping)
545 {
198fdd15
GV
546 i = GetLastError ();
547 exit (1);
2147fb50
KH
548 }
549
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 ());
198fdd15
GV
554 if (file_base != 0)
555 {
556 return;
557 }
558
559 /* If we don't succeed with the mapping, then copy from the
560 data into the heap. */
561
562 CloseHandle (file_mapping);
563
564 if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
565 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
566 {
567 i = GetLastError ();
568 exit (1);
569 }
570
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)
2147fb50
KH
574 {
575 i = GetLastError ();
576 exit (1);
577 }
198fdd15
GV
578
579 /* Read in the data. */
580 if (!ReadFile (file, get_heap_start (),
581 get_committed_heap_size (), &n_read, NULL))
582 {
583 i = GetLastError ();
584 exit (1);
585 }
586
587 CloseHandle (file);
2147fb50 588}