(LINK_FLAGS): Dump symbol map when linking.
[bpt/emacs.git] / src / unexw32.c
CommitLineData
2147fb50
KH
1/*
2 unexec for GNU Emacs on Windows NT.
3
4 Copyright (C) 1994 Free Software Foundation, Inc.
5
6 This file is part of GNU Emacs.
7
8 GNU Emacs is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 more details.
17
18 You should have received a copy of the GNU General Public License along
19 with GNU Emacs; see the file COPYING. If not, write to the Free Software
20 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 Geoff Voelker (voelker@cs.washington.edu) 8-12-94
23*/
24
25#include <stdlib.h> /* _fmode */
26#include <stdio.h>
27#include <fcntl.h>
28#include <windows.h>
29
30extern BOOL ctrl_c_handler (unsigned long type);
31
32#include "ntheap.h"
33
34/* A convenient type for keeping all the info about a mapped file together. */
35typedef struct file_data {
36 char *name;
37 unsigned long size;
38 HANDLE file;
39 HANDLE file_mapping;
40 unsigned char *file_base;
41} file_data;
42
43/* Basically, our "initialized" flag. */
44BOOL need_to_recreate_heap = FALSE;
45
46/* So we can find our heap in the file to recreate it. */
47unsigned long heap_index_in_executable = 0;
48
49void open_input_file (file_data *p_file, char *name);
50void open_output_file (file_data *p_file, char *name, unsigned long size);
51void close_file_data (file_data *p_file);
52
53void get_section_info (file_data *p_file);
54void copy_executable_and_dump_data_section (file_data *, file_data *);
55void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
56
57/* Cached info about the .data section in the executable. */
58PUCHAR data_start_va = 0;
59DWORD data_start_file = 0;
60DWORD data_size = 0;
61
62/* Cached info about the .bss section in the executable. */
63PUCHAR bss_start = 0;
64DWORD bss_size = 0;
65
cd6885f3
GV
66#ifdef HAVE_NTGUI
67HINSTANCE hinst = NULL;
68HINSTANCE hprevinst = NULL;
69LPSTR lpCmdLine = "";
70int nCmdShow = 0;
71
72int __stdcall
73WinMain (_hinst, _hPrevInst, _lpCmdLine, _nCmdShow)
74 HINSTANCE _hinst;
75 HINSTANCE _hPrevInst;
76 LPSTR _lpCmdLine;
77 int _nCmdShow;
78{
79 /* Need to parse command line */
80
81 hinst = _hinst;
82 hprevinst = _hPrevInst;
83 lpCmdLine = _lpCmdLine;
84 nCmdShow = _nCmdShow;
85
86 return (main (__argc,__argv,_environ));
87}
88#endif /* HAVE_NTGUI */
89
2147fb50
KH
90/* Startup code for running on NT. When we are running as the dumped
91 version, we need to bootstrap our heap and .bss section into our
92 address space before we can actually hand off control to the startup
93 code supplied by NT (primarily because that code relies upon malloc ()). */
94void
95_start (void)
96{
cd6885f3
GV
97#ifdef HAVE_NTGUI
98 extern void WinMainCRTStartup (void);
99#else
2147fb50 100 extern void mainCRTStartup (void);
cd6885f3 101#endif /* HAVE_NTGUI */
2147fb50
KH
102
103 /* Cache system info, e.g., the NT page size. */
104 cache_system_info ();
105
106 /* If we're a dumped version of emacs then we need to recreate
107 our heap and play tricks with our .bss section. Do this before
108 start up. (WARNING: Do not put any code before this section
109 that relies upon malloc () and runs in the dumped version. It
110 won't work.) */
111 if (need_to_recreate_heap)
112 {
113 char executable_path[MAX_PATH];
114
115 if (GetModuleFileName (NULL, executable_path, MAX_PATH) == 0)
116 {
117 printf ("Failed to find path for executable.\n");
118 exit (1);
119 }
120 recreate_heap (executable_path);
121 need_to_recreate_heap = FALSE;
122 }
123
124 /* The default behavior is to treat files as binary and patch up
125 text files appropriately, in accordance with the MSDOS code. */
126 _fmode = O_BINARY;
127
128 /* This prevents ctrl-c's in shells running while we're suspended from
129 having us exit. */
130 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
131
132 /* Invoke the NT CRT startup routine now that our housecleaning
133 is finished. */
cd6885f3
GV
134#ifdef HAVE_NTGUI
135 WinMainCRTStartup ();
136#else
2147fb50 137 mainCRTStartup ();
cd6885f3 138#endif /* HAVE_NTGUI */
2147fb50
KH
139}
140
141/* Dump out .data and .bss sections into a new exectubale. */
142void
143unexec (char *new_name, char *old_name, void *start_data, void *start_bss,
144 void *entry_address)
145{
146 file_data in_file, out_file;
147 char out_filename[MAX_PATH], in_filename[MAX_PATH];
148 unsigned long size;
149 char *ptr;
150
151 /* Make sure that the input and output filenames have the
152 ".exe" extension...patch them up if they don't. */
153 strcpy (in_filename, old_name);
154 ptr = in_filename + strlen (in_filename) - 4;
155 if (strcmp (ptr, ".exe"))
156 strcat (in_filename, ".exe");
157
158 strcpy (out_filename, new_name);
159 ptr = out_filename + strlen (out_filename) - 4;
160 if (strcmp (ptr, ".exe"))
161 strcat (out_filename, ".exe");
162
163 printf ("Dumping from %s\n", in_filename);
164 printf (" to %s\n", out_filename);
165
166 /* We need to round off our heap to NT's allocation unit (64KB). */
167 round_heap (get_allocation_unit ());
168
169 /* Open the undumped executable file. */
170 open_input_file (&in_file, in_filename);
171
172 /* Get the interesting section info, like start and size of .bss... */
173 get_section_info (&in_file);
174
175 /* The size of the dumped executable is the size of the original
176 executable plus the size of the heap and the size of the .bss section. */
198fdd15
GV
177 heap_index_in_executable = (unsigned long)
178 round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
2147fb50
KH
179 size = heap_index_in_executable + get_committed_heap_size () + bss_size;
180 open_output_file (&out_file, out_filename, size);
181
182 /* Set the flag (before dumping). */
183 need_to_recreate_heap = TRUE;
184
185 copy_executable_and_dump_data_section (&in_file, &out_file);
186 dump_bss_and_heap (&in_file, &out_file);
187
188 close_file_data (&in_file);
189 close_file_data (&out_file);
190}
191
192
193/* File handling. */
194
195
196void
197open_input_file (file_data *p_file, char *filename)
198{
199 HANDLE file;
200 HANDLE file_mapping;
201 void *file_base;
202 unsigned long size, upper_size;
203
204 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
205 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
206 if (file == INVALID_HANDLE_VALUE)
207 {
208 printf ("Failed to open %s (%d)...bailing.\n",
209 filename, GetLastError ());
210 exit (1);
211 }
212
213 size = GetFileSize (file, &upper_size);
214 file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
215 0, size, NULL);
216 if (!file_mapping)
217 {
218 printf ("Failed to create file mapping of %s (%d)...bailing.\n",
219 filename, GetLastError ());
220 exit (1);
221 }
222
223 file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
224 if (file_base == 0)
225 {
226 printf ("Failed to map view of file of %s (%d)...bailing.\n",
227 filename, GetLastError ());
228 exit (1);
229 }
230
231 p_file->name = filename;
232 p_file->size = size;
233 p_file->file = file;
234 p_file->file_mapping = file_mapping;
235 p_file->file_base = file_base;
236}
237
238void
239open_output_file (file_data *p_file, char *filename, unsigned long size)
240{
241 HANDLE file;
242 HANDLE file_mapping;
243 void *file_base;
cd6885f3
GV
244 int i;
245
2147fb50
KH
246 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
247 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
248 if (file == INVALID_HANDLE_VALUE)
249 {
cd6885f3 250 i = GetLastError ();
2147fb50 251 printf ("open_output_file: Failed to open %s (%d).\n",
cd6885f3 252 filename, i);
2147fb50
KH
253 exit (1);
254 }
255
256 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
257 0, size, NULL);
258 if (!file_mapping)
259 {
cd6885f3 260 i = GetLastError ();
2147fb50 261 printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
cd6885f3 262 filename, i);
2147fb50
KH
263 exit (1);
264 }
265
266 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
267 if (file_base == 0)
268 {
cd6885f3 269 i = GetLastError ();
2147fb50 270 printf ("open_output_file: Failed to map view of file of %s (%d).\n",
cd6885f3 271 filename, i);
2147fb50
KH
272 exit (1);
273 }
274
275 p_file->name = filename;
276 p_file->size = size;
277 p_file->file = file;
278 p_file->file_mapping = file_mapping;
279 p_file->file_base = file_base;
280}
281
282/* Close the system structures associated with the given file. */
283static void
284close_file_data (file_data *p_file)
285{
286 UnmapViewOfFile (p_file->file_base);
287 CloseHandle (p_file->file_mapping);
288 CloseHandle (p_file->file);
289}
290
291
292/* Routines to manipulate NT executable file sections. */
293
294
295static unsigned long
296get_section_size (PIMAGE_SECTION_HEADER p_section)
297{
298 /* The section size is in different locations in the different versions. */
299 switch (get_nt_minor_version ())
300 {
301 case 10:
302 return p_section->SizeOfRawData;
303 default:
304 return p_section->Misc.VirtualSize;
305 }
306}
307
308/* Flip through the executable and cache the info necessary for dumping. */
309static void
310get_section_info (file_data *p_infile)
311{
312 PIMAGE_DOS_HEADER dos_header;
313 PIMAGE_NT_HEADERS nt_header;
314 PIMAGE_SECTION_HEADER section;
315 unsigned char *ptr;
316 int i;
317
318 dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
319 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
320 {
321 printf ("Unknown EXE header in %s...bailing.\n", p_infile->name);
322 exit (1);
323 }
324 nt_header = (PIMAGE_NT_HEADERS) (((unsigned long) dos_header) +
325 dos_header->e_lfanew);
326 if (nt_header == NULL)
327 {
328 printf ("Failed to find IMAGE_NT_HEADER in %s...bailing.\n",
329 p_infile->name);
330 exit (1);
331 }
332
333 /* Check the NT header signature ... */
334 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
335 {
336 printf ("Invalid IMAGE_NT_SIGNATURE 0x%x in %s...bailing.\n",
337 nt_header->Signature, p_infile->name);
338 }
339
340 /* Flip through the sections for .data and .bss ... */
341 section = (PIMAGE_SECTION_HEADER) IMAGE_FIRST_SECTION (nt_header);
342 for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
343 {
344 if (!strcmp (section->Name, ".bss"))
345 {
346 /* The .bss section. */
347 ptr = (char *) nt_header->OptionalHeader.ImageBase +
348 section->VirtualAddress;
349 bss_start = ptr;
350 bss_size = get_section_size (section);
351 }
352 if (!strcmp (section->Name, ".data"))
353 {
198fdd15
GV
354 /* From lastfile.c */
355 extern char my_edata[];
356
2147fb50
KH
357 /* The .data section. */
358 ptr = (char *) nt_header->OptionalHeader.ImageBase +
359 section->VirtualAddress;
360 data_start_va = ptr;
361 data_start_file = section->PointerToRawData;
198fdd15
GV
362
363 /* We want to only write Emacs data back to the executable,
364 not any of the library data (if library data is included,
365 then a dumped Emacs won't run on system versions other
366 than the one Emacs was dumped on). */
367 data_size = my_edata - data_start_va;
2147fb50
KH
368 }
369 section++;
370 }
371}
372
373
374/* The dump routines. */
375
376static void
377copy_executable_and_dump_data_section (file_data *p_infile,
378 file_data *p_outfile)
379{
380 unsigned char *data_file, *data_va;
381 unsigned long size, index;
382
383 /* Get a pointer to where the raw data should go in the executable file. */
384 data_file = (char *) p_outfile->file_base + data_start_file;
385
386 /* Get a pointer to the raw data in our address space. */
387 data_va = data_start_va;
388
389 size = (DWORD) data_file - (DWORD) p_outfile->file_base;
390 printf ("Copying executable up to data section...\n");
391 printf ("\t0x%08x Offset in input file.\n", 0);
392 printf ("\t0x%08x Offset in output file.\n", 0);
393 printf ("\t0x%08x Size in bytes.\n", size);
394 memcpy (p_outfile->file_base, p_infile->file_base, size);
395
396 size = data_size;
397 printf ("Dumping .data section...\n");
398 printf ("\t0x%08x Address in process.\n", data_va);
399 printf ("\t0x%08x Offset in output file.\n",
400 data_file - p_outfile->file_base);
401 printf ("\t0x%08x Size in bytes.\n", size);
402 memcpy (data_file, data_va, size);
403
404 index = (DWORD) data_file + size - (DWORD) p_outfile->file_base;
405 size = p_infile->size - index;
406 printf ("Copying rest of executable...\n");
407 printf ("\t0x%08x Offset in input file.\n", index);
408 printf ("\t0x%08x Offset in output file.\n", index);
409 printf ("\t0x%08x Size in bytes.\n", size);
410 memcpy ((char *) p_outfile->file_base + index,
411 (char *) p_infile->file_base + index, size);
412}
413
414static void
415dump_bss_and_heap (file_data *p_infile, file_data *p_outfile)
416{
417 unsigned char *heap_data, *bss_data;
418 unsigned long size, index;
419
420 printf ("Dumping heap into executable...\n");
421
422 index = heap_index_in_executable;
423 size = get_committed_heap_size ();
424 heap_data = get_heap_start ();
425
426 printf ("\t0x%08x Heap start in process.\n", heap_data);
427 printf ("\t0x%08x Heap offset in executable.\n", index);
428 printf ("\t0x%08x Heap size in bytes.\n", size);
429
430 memcpy ((PUCHAR) p_outfile->file_base + index, heap_data, size);
431
432 printf ("Dumping .bss into executable...\n");
433
434 index += size;
435 size = bss_size;
436 bss_data = bss_start;
437
438 printf ("\t0x%08x BSS start in process.\n", bss_data);
439 printf ("\t0x%08x BSS offset in executable.\n", index);
440 printf ("\t0x%08x BSS size in bytes.\n", size);
441 memcpy ((char *) p_outfile->file_base + index, bss_data, size);
442}
443
444
445/* Reload and remap routines. */
446
447
448/* Load the dumped .bss section into the .bss area of our address space. */
449void
450read_in_bss (char *filename)
451{
452 HANDLE file;
453 unsigned long size, index, n_read, total_read;
454 char buffer[512], *bss;
455 int i;
456
457 file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
458 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
459 if (file == INVALID_HANDLE_VALUE)
460 {
461 i = GetLastError ();
462 exit (1);
463 }
464
465 /* Seek to where the .bss section is tucked away after the heap... */
466 index = heap_index_in_executable + get_committed_heap_size ();
467 if (SetFilePointer (file, index, NULL, FILE_BEGIN) == 0xFFFFFFFF)
468 {
469 i = GetLastError ();
470 exit (1);
471 }
472
473
474 /* Ok, read in the saved .bss section and initialize all
475 uninitialized variables. */
198fdd15 476 if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
2147fb50 477 {
198fdd15
GV
478 i = GetLastError ();
479 exit (1);
2147fb50 480 }
198fdd15 481
2147fb50
KH
482 CloseHandle (file);
483}
484
485/* Map the heap dumped into the executable file into our address space. */
486void
487map_in_heap (char *filename)
488{
489 HANDLE file;
490 HANDLE file_mapping;
491 void *file_base;
198fdd15 492 unsigned long size, upper_size, n_read;
2147fb50
KH
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 size = GetFileSize (file, &upper_size);
504 file_mapping = CreateFileMapping (file, NULL, PAGE_WRITECOPY,
505 0, size, NULL);
506 if (!file_mapping)
507 {
198fdd15
GV
508 i = GetLastError ();
509 exit (1);
2147fb50
KH
510 }
511
512 size = get_committed_heap_size ();
513 file_base = MapViewOfFileEx (file_mapping, FILE_MAP_COPY, 0,
514 heap_index_in_executable, size,
515 get_heap_start ());
198fdd15
GV
516 if (file_base != 0)
517 {
518 return;
519 }
520
521 /* If we don't succeed with the mapping, then copy from the
522 data into the heap. */
523
524 CloseHandle (file_mapping);
525
526 if (VirtualAlloc (get_heap_start (), get_committed_heap_size (),
527 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) == NULL)
528 {
529 i = GetLastError ();
530 exit (1);
531 }
532
533 /* Seek to the location of the heap data in the executable. */
534 i = heap_index_in_executable;
535 if (SetFilePointer (file, i, NULL, FILE_BEGIN) == 0xFFFFFFFF)
2147fb50
KH
536 {
537 i = GetLastError ();
538 exit (1);
539 }
198fdd15
GV
540
541 /* Read in the data. */
542 if (!ReadFile (file, get_heap_start (),
543 get_committed_heap_size (), &n_read, NULL))
544 {
545 i = GetLastError ();
546 exit (1);
547 }
548
549 CloseHandle (file);
2147fb50 550}