]> code.delx.au - gnu-emacs/blob - src/unexw32.c
(LIBS_SYSTEM): Fix typo in prev change.
[gnu-emacs] / src / unexw32.c
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
30 extern 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. */
35 typedef 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. */
44 BOOL need_to_recreate_heap = FALSE;
45
46 /* So we can find our heap in the file to recreate it. */
47 unsigned long heap_index_in_executable = 0;
48
49 void open_input_file (file_data *p_file, char *name);
50 void open_output_file (file_data *p_file, char *name, unsigned long size);
51 void close_file_data (file_data *p_file);
52
53 void get_section_info (file_data *p_file);
54 void copy_executable_and_dump_data_section (file_data *, file_data *);
55 void dump_bss_and_heap (file_data *p_infile, file_data *p_outfile);
56
57 /* Cached info about the .data section in the executable. */
58 PUCHAR data_start_va = 0;
59 DWORD data_start_file = 0;
60 DWORD data_size = 0;
61
62 /* Cached info about the .bss section in the executable. */
63 PUCHAR bss_start = 0;
64 DWORD bss_size = 0;
65
66 #ifdef HAVE_NTGUI
67 HINSTANCE hinst = NULL;
68 HINSTANCE hprevinst = NULL;
69 LPSTR lpCmdLine = "";
70 int nCmdShow = 0;
71
72 int __stdcall
73 WinMain (_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
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 ()). */
94 void
95 _start (void)
96 {
97 #ifdef HAVE_NTGUI
98 extern void WinMainCRTStartup (void);
99 #else
100 extern void mainCRTStartup (void);
101 #endif /* HAVE_NTGUI */
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. */
134 #ifdef HAVE_NTGUI
135 WinMainCRTStartup ();
136 #else
137 mainCRTStartup ();
138 #endif /* HAVE_NTGUI */
139 }
140
141 /* Dump out .data and .bss sections into a new exectubale. */
142 void
143 unexec (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. */
177 heap_index_in_executable = (unsigned long)
178 round_to_next ((unsigned char *) in_file.size, get_allocation_unit ());
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
196 void
197 open_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
238 void
239 open_output_file (file_data *p_file, char *filename, unsigned long size)
240 {
241 HANDLE file;
242 HANDLE file_mapping;
243 void *file_base;
244 int i;
245
246 file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
247 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
248 if (file == INVALID_HANDLE_VALUE)
249 {
250 i = GetLastError ();
251 printf ("open_output_file: Failed to open %s (%d).\n",
252 filename, i);
253 exit (1);
254 }
255
256 file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
257 0, size, NULL);
258 if (!file_mapping)
259 {
260 i = GetLastError ();
261 printf ("open_output_file: Failed to create file mapping of %s (%d).\n",
262 filename, i);
263 exit (1);
264 }
265
266 file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
267 if (file_base == 0)
268 {
269 i = GetLastError ();
270 printf ("open_output_file: Failed to map view of file of %s (%d).\n",
271 filename, i);
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. */
283 static void
284 close_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
295 static unsigned long
296 get_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. */
309 static void
310 get_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 {
354 /* From lastfile.c */
355 extern char my_edata[];
356
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;
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;
368 }
369 section++;
370 }
371 }
372
373
374 /* The dump routines. */
375
376 static void
377 copy_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
414 static void
415 dump_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. */
449 void
450 read_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. */
476 if (!ReadFile (file, bss_start, bss_size, &n_read, NULL))
477 {
478 i = GetLastError ();
479 exit (1);
480 }
481
482 CloseHandle (file);
483 }
484
485 /* Map the heap dumped into the executable file into our address space. */
486 void
487 map_in_heap (char *filename)
488 {
489 HANDLE file;
490 HANDLE file_mapping;
491 void *file_base;
492 unsigned long size, upper_size, n_read;
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 {
508 i = GetLastError ();
509 exit (1);
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 ());
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)
536 {
537 i = GetLastError ();
538 exit (1);
539 }
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);
550 }