comparison src/unexcw.cc @ 245:51092a27c943 r20-5b21

Import from CVS: tag r20-5b21
author cvs
date Mon, 13 Aug 2007 10:17:54 +0200
parents
children
comparison
equal deleted inserted replaced
244:78d4f1140794 245:51092a27c943
1 /* unexec for GNU Emacs on Cygwin32.
2 Copyright (C) 1994, 1998 Free Software Foundation, Inc.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING. If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20
21 */
22
23 /* Adapted from unexnt.c Andy Piper (andyp@parallax.co.uk) 13-1-98 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <config.h>
30 #include <string.h>
31
32 #define PERROR(arg) perror(arg);exit(-1)
33
34 #ifndef HAVE_COFF_H
35 extern "C" void
36 unexec (char *, char *, void *, void *, void *)
37 {
38 PERROR("cannot unexec() coff.h not installed");
39 }
40
41 extern "C" void run_time_remap (char *)
42 {}
43 #else
44
45 #include <windows.h>
46 /*
47 * unfortunately we need this c++ to get at the internals of cygwin
48 */
49 class pinfo;
50 class per_process
51 {
52 public:
53 char *initial_sp;
54
55 /* The offset of these 3 values can never change. */
56 /* magic_biscuit is the size of this class and should never change. */
57 int magic_biscuit;
58 int version_major;
59 int version_minor;
60
61 struct _reent **impure_ptr_ptr;
62 char ***envptr;
63
64 /* Used to point to the memory machine we should use -
65 usually points back into the dll, but can be overridden by the user. */
66 void *(*malloc)(size_t);
67 void (*free)(void *);
68 void *(*realloc)(void *, size_t);
69
70 int *fmode_ptr;
71
72 int (*main)(int, char **, char **);
73 void (**ctors)();
74 void (**dtors)();
75
76 /* For fork */
77 void *data_start;
78 void *data_end;
79 void *bss_start;
80 void *bss_end;
81
82 /* For future expansion of values set by the app. */
83 void *public_reserved[4];
84
85 /* The rest are *internal* to cygwin.dll.
86 Those that are here because we want the child to inherit the value from
87 the parent (which happens when bss is copied) are marked as such. */
88
89 /* FIXME: Which of these can go elsewhere? */
90
91 /* FIXME: Delete, make `self' a global. */
92 pinfo *self; /* pointer only valid in self process */
93
94 /* non-zero of ctors have been run. Inherited from parent. */
95 int run_ctors_p;
96
97 /* These will be non-zero if the above (malloc,free,realloc) have been
98 overridden. */
99 /* FIXME: not currently used */
100 int __imp_malloc;
101 int __imp_free;
102 int __imp_realloc;
103
104 /* Heap management. Inherited from parent. */
105 void *base; /* bottom of the heap */
106 void *ptr; /* current index into heap */
107 int size; /* current size of heap */
108
109 /* Mask of what to trace. Inherited from parent.
110 See sys/strace.h for details. The value of this is changeable from other
111 tasks via the `cygwin' utility so we want this in the shared data area
112 (and thus the process table since there's one of these per task).
113 However, we also want to turn on stracing as soon as possible and
114 therefore before we know which process table entry to use. So we put it
115 here, and have a pointer to it in the process table. */
116 int strace_mask;
117
118 /* Non-zero means the task was forked. The value is the pid.
119 Inherited from parent. */
120 int forkee;
121
122 void *hmodule;
123
124 void* /*HANDLE*/ signal_arrived;
125 /* For future expansion, so apps won't have to be relinked if we
126 add an item. */
127 void *internal_reserved[9];
128
129 /* struct file_queue *dq; !!! this may need to be nuked ? */
130 };
131
132 extern per_process cygwin_statu; /* pointer into the application's static data */
133
134 #include <coff.h>
135
136 #define ALLOC_UNIT 0xFFFF
137 #define ALLOC_MASK ~((unsigned long)(ALLOC_UNIT))
138 #define ALIGN_ALLOC(addr) \
139 ((((unsigned long)addr) + ALLOC_UNIT) & ALLOC_MASK)
140 #define SIZEOF_PER_PROCESS (42 * 4)
141
142 /*
143 * Heap related stuff.
144 */
145 #define get_reserved_heap_size() (*heap_size)
146 #define get_committed_heap_size() \
147 (int)((unsigned char*)(*heap_index)-(unsigned char*)(*heap_base))
148 #define get_heap_start() (*heap_base)
149 #define get_heap_end() (*heap_index)
150
151 extern "C" {
152 void** heap_base = &cygwin_statu.base;
153 void** heap_index = &cygwin_statu.ptr;
154 int* heap_size = &cygwin_statu.size;
155 int* heap_flag = &cygwin_statu.forkee;
156 void* per_process_data = &cygwin_statu;
157 /* To prevent zero-initialized variables from being placed into the bss
158 section, use non-zero values to represent an uninitialized state. */
159 #define UNINIT_PTR ((void *) 0xF0A0F0A0)
160 #define UNINIT_LONG (0xF0A0F0A0L)
161
162 void* local_heap_base=UNINIT_PTR;
163 void* local_heap_index=UNINIT_PTR;
164 unsigned long local_heap_size=UNINIT_LONG;
165
166 /* Recreate the heap created during dumping. */
167
168 enum {
169 HEAP_UNINITIALIZED = 1,
170 HEAP_UNLOADED,
171 HEAP_LOADED
172 };
173
174 /* Basically, our "initialized" flag. */
175 int heap_state = HEAP_UNINITIALIZED;
176
177 /* So we can find our heap in the file to recreate it. */
178 unsigned long heap_index_in_executable = UNINIT_LONG;
179
180 static void get_section_info (int a_out, char* a_name);
181 static void copy_executable_and_dump_data_section (int a_out, int a_new);
182 static void dump_heap (int a_out, int a_new);
183 static void dup_file_area(int a_out, int a_new, long size);
184
185 /* Cached info about the .data section in the executable. */
186 void* data_start_va = UNINIT_PTR;
187 unsigned long data_size = UNINIT_LONG;
188
189 /* Cached info about the .bss section in the executable. */
190 void* bss_start = UNINIT_PTR;
191 unsigned long bss_size = UNINIT_LONG;
192 FILHDR f_hdr;
193 PEAOUTHDR f_ohdr;
194 SCNHDR f_data, f_bss, f_text, f_idata;
195 }
196 #define PERROR(arg) perror(arg);exit(-1)
197 #define CHECK_AOUT_POS(a) \
198 if (lseek(a_out, 0, SEEK_CUR) != a) \
199 { \
200 printf("we are at %lx, should be at %lx\n", \
201 lseek(a_out, 0, SEEK_CUR), a); \
202 exit(-1); \
203 }
204
205 /* Dump out .data and .bss sections into a new executable. */
206 extern "C" void
207 unexec (char *out_name, char *in_name, void *start_data, void *, void *)
208 {
209 /* ugly nt hack - should be in lisp */
210 char new_name[MAX_PATH], a_name[MAX_PATH];
211 char *ptr;
212
213 /* Make sure that the input and output filenames have the
214 ".exe" extension...patch them up if they don't. */
215 strcpy (a_name, in_name);
216 ptr = a_name + strlen (a_name) - 4;
217 if (strcmp (ptr, ".exe"))
218 strcat (a_name, ".exe");
219
220 strcpy (new_name, out_name);
221 ptr = new_name + strlen (new_name) - 4;
222 if (strcmp (ptr, ".exe"))
223 strcat (new_name, ".exe");
224 /* save heap info in our data segment so that we can recreate after
225 dumping */
226
227 local_heap_base = *heap_base;
228 local_heap_size = *heap_size;
229 local_heap_index = *heap_index;
230
231 /* We need to round off our heap to NT's allocation unit (64KB). */
232 /* round_heap (get_allocation_unit ()); */
233
234 int a_new, a_out = -1;
235
236 if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
237 {
238 PERROR (a_name);
239 }
240 if ((a_new = creat (new_name, 0666)) < 0)
241 {
242 PERROR (new_name);
243 }
244
245 /* Get the interesting section info, like start and size of .bss... */
246 get_section_info (a_out, a_name);
247
248 /* Set the flag (before dumping). */
249 heap_state = HEAP_UNLOADED;
250
251 copy_executable_and_dump_data_section (a_out, a_new);
252 dump_heap (a_out, a_new);
253
254 close(a_out);
255 close(a_new);
256 }
257
258 /* Flip through the executable and cache the info necessary for dumping. */
259 static void get_section_info (int a_out, char* a_name)
260 {
261 if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
262 {
263 PERROR (a_name);
264 }
265
266 if (f_hdr.e_magic != DOSMAGIC)
267 {
268 PERROR("unknown exe header");
269 }
270
271 /* Check the NT header signature ... */
272 if (f_hdr.nt_signature != NT_SIGNATURE)
273 {
274 PERROR("invalid nt header");
275 }
276
277 /* Flip through the sections for .data and .bss ... */
278 if (f_hdr.f_opthdr > 0)
279 {
280 if (read (a_out, &f_ohdr, AOUTSZ) != AOUTSZ)
281 {
282 PERROR (a_name);
283 }
284 }
285 /* Loop through .data & .bss section headers, copying them in */
286 lseek (a_out, sizeof (f_hdr) + f_hdr.f_opthdr, 0);
287
288 if (read (a_out, &f_text, sizeof (f_text)) != sizeof (f_text)
289 &&
290 strcmp (f_text.s_name, ".text"))
291 {
292 PERROR ("no .text section");
293 }
294
295 /* The .bss section. */
296 if (read (a_out, &f_bss, sizeof (f_bss)) != sizeof (f_bss)
297 &&
298 strcmp (f_bss.s_name, ".bss"))
299 {
300 PERROR ("no .bss section");
301 }
302 extern int my_ebss;
303 bss_start = (void *) ((char*)f_ohdr.ImageBase + f_bss.s_vaddr);
304 bss_size = (unsigned long)((char*)&my_ebss-(char*)bss_start);
305
306 /* must keep bss data that we want to be blank as blank */
307 printf("found bss - keeping %lx of %lx bytes\n", bss_size, f_ohdr.bsize);
308
309 /* The .data section. */
310 if (read (a_out, &f_data, sizeof (f_data)) != sizeof (f_data)
311 &&
312 strcmp (f_data.s_name, ".data"))
313 {
314 PERROR ("no .data section");
315 }
316
317 /* From lastfile.c */
318 extern char my_edata[];
319
320 /* The .data section. */
321 data_start_va = (void *) ((char*)f_ohdr.ImageBase + f_data.s_vaddr);
322
323 /* We want to only write Emacs data back to the executable,
324 not any of the library data (if library data is included,
325 then a dumped Emacs won't run on system versions other
326 than the one Emacs was dumped on). */
327 data_size = my_edata - data_start_va;
328
329 /* The .idata section. */
330 if (read (a_out, &f_idata, sizeof (f_idata)) != sizeof (f_idata)
331 &&
332 strcmp (f_idata.s_name, ".idata"))
333 {
334 PERROR ("no .idata section");
335 }
336 }
337
338 /* The dump routines. */
339
340 static void
341 copy_executable_and_dump_data_section (int a_out, int a_new)
342 {
343 long size=0;
344 unsigned long new_data_size, new_bss_size, f_data_s_vaddr,
345 file_sz_change, f_data_s_scnptr, bss_padding;
346 int i;
347 SCNHDR section;
348 /* calculate new sizes f_ohdr.dsize is the total initalized data
349 size on disk which is f_data.s_size + f_idata.s_size.
350 f_ohdr.data_start is the base addres of all data and so should
351 not be changed. *.s_vaddr is the virtual address of the start
352 of the section normalzed from f_ohdr.ImageBase. *.s_paddr
353 appears to be the number of bytes in the section actually used
354 (whereas *.s_size is aligned).
355
356 bsize is now 0 since subsumed into .data
357 dsize is dsize + (f_data.s_vaddr - f_bss.s_vaddr)
358 f_data.s_vaddr is f_bss.s_vaddr
359 f_data.s_size is new dsize maybe.
360 what about s_paddr & s_scnptr? */
361 /* this is the amount the file increases in size */
362 *heap_flag=1; // kludge to get mem to remap
363 new_bss_size=f_data.s_vaddr - f_bss.s_vaddr;
364 file_sz_change=new_bss_size;
365 new_data_size=f_ohdr.dsize + new_bss_size;
366 f_data_s_scnptr = f_data.s_scnptr;
367 f_data_s_vaddr = f_data.s_vaddr;
368 f_data.s_vaddr = f_bss.s_vaddr;
369 f_data.s_paddr += new_bss_size;
370
371 if (f_data.s_size + f_idata.s_size != f_ohdr.dsize)
372 {
373 printf("section size doesn't tally with dsize %lx != %lx\n",
374 f_data.s_size + f_idata.s_size, f_ohdr.dsize);
375 }
376 f_data.s_size += new_bss_size;
377 lseek (a_new, 0, SEEK_SET);
378 /* write file header */
379 f_hdr.f_symptr += file_sz_change;
380 f_hdr.f_nscns--;
381 printf("writing file header\n");
382 if (write(a_new, &f_hdr, sizeof(f_hdr)) != sizeof(f_hdr))
383 {
384 PERROR("failed to write file header");
385 }
386 /* write optional header fixing dsize & bsize*/
387 printf("writing optional header\n");
388 printf("new data size is %lx, >= %lx\n", new_data_size,
389 f_ohdr.dsize + f_ohdr.bsize);
390 if (new_data_size < f_ohdr.dsize + f_ohdr.bsize )
391 {
392 PERROR("new data size is < approx");
393 }
394 f_ohdr.dsize=new_data_size;
395 f_ohdr.bsize=0;
396 if (write(a_new, &f_ohdr, sizeof(f_ohdr)) != sizeof(f_ohdr))
397 {
398 PERROR("failed to write optional header");
399 }
400 /* write text as is */
401 printf("writing text header (unchanged)\n");
402
403 if (write(a_new, &f_text, sizeof(f_text)) != sizeof(f_text))
404 {
405 PERROR("failed to write text header");
406 }
407
408 /* write new data header */
409 printf("writing .data header\n");
410
411 if (write(a_new, &f_data, sizeof(f_data)) != sizeof(f_data))
412 {
413 PERROR("failed to write data header");
414 }
415
416 printf("writing .idata header\n");
417 f_idata.s_scnptr += file_sz_change;
418 if (f_idata.s_lnnoptr != 0) f_idata.s_lnnoptr += file_sz_change;
419 if (f_idata.s_relptr != 0) f_idata.s_relptr += file_sz_change;
420 if (write(a_new, &f_idata, sizeof(f_idata)) != sizeof(f_idata))
421 {
422 PERROR("failed to write idata header");
423 }
424
425 /* copy other section headers adjusting the file offset */
426 for (i=0; i<(f_hdr.f_nscns-3); i++)
427 {
428 if (read (a_out, &section, sizeof (section)) != sizeof (section))
429 {
430 PERROR ("no .data section");
431 }
432
433 section.s_scnptr += file_sz_change;
434 if (section.s_lnnoptr != 0) section.s_lnnoptr += file_sz_change;
435 if (section.s_relptr != 0) section.s_relptr += file_sz_change;
436
437 if (write(a_new, &section, sizeof(section)) != sizeof(section))
438 {
439 PERROR("failed to write data header");
440 }
441 }
442
443 /* dump bss to maintain offsets */
444 memset(&f_bss, 0, sizeof(f_bss));
445 if (write(a_new, &f_bss, sizeof(f_bss)) != sizeof(f_bss))
446 {
447 PERROR("failed to write bss header");
448 }
449
450
451 size=lseek(a_new, 0, SEEK_CUR);
452 CHECK_AOUT_POS(size);
453
454 /* copy eveything else until start of data */
455 size = f_data_s_scnptr - lseek (a_out, 0, SEEK_CUR);
456
457 printf ("copying executable up to data section ... %lx bytes\n",
458 size);
459 dup_file_area(a_out, a_new, size);
460
461 CHECK_AOUT_POS(f_data_s_scnptr);
462
463 /* dump bss + padding between sections */
464 printf ("dumping .bss into executable... %lx bytes\n", bss_size);
465 if (write(a_new, bss_start, bss_size) != (int)bss_size)
466 {
467 PERROR("failed to write bss section");
468 }
469 /* pad, needs to be zero */
470 bss_padding = new_bss_size - bss_size;
471 printf ("padding .bss ... %lx bytes\n", bss_padding);
472 void* empty_space = malloc(bss_padding);
473 memset(empty_space, 0, bss_padding);
474 if (write(a_new, empty_space, bss_padding) != (int)bss_padding)
475 {
476 PERROR("failed to write bss section");
477 }
478 free(empty_space);
479
480 /* Get a pointer to the raw data in our address space. */
481 printf ("dumping .data section... %lx bytes\n", data_size);
482 if (write(a_new, data_start_va, data_size) != (int)data_size)
483 {
484 PERROR("failed to write data section");
485 }
486
487 lseek(a_out, f_data_s_scnptr + data_size, SEEK_SET);
488
489 size = (((unsigned long)per_process_data-f_ohdr.ImageBase)-f_data_s_vaddr)
490 - data_size;
491 /* write rest of .data to cygwin per process */
492 printf ("copying from .data to cygwin per_process... %lx bytes\n", size);
493 dup_file_area(a_out, a_new, size);
494
495 /* now write out the per process information */
496 printf ("dumping to cygwin per_process... %x bytes at %p\n",
497 SIZEOF_PER_PROCESS, per_process_data);
498
499 per_process newpp;
500 memset(&newpp, 0, SIZEOF_PER_PROCESS);
501 newpp.base = cygwin_statu.base;
502 newpp.ptr = cygwin_statu.ptr;
503 newpp.size = cygwin_statu.size;
504
505 if (write(a_new, &newpp, SIZEOF_PER_PROCESS)
506 != (int)SIZEOF_PER_PROCESS)
507 {
508 PERROR("failed to write per_process info");
509 }
510 free(empty_space);
511
512 /* dump the rest */
513 size = lseek(a_out, SIZEOF_PER_PROCESS, SEEK_CUR);
514 size = f_idata.s_scnptr - size;
515 dup_file_area(a_out, a_new, size);
516
517 // lseek(a_out, f_idata.s_scnptr, SEEK_CUR);
518 CHECK_AOUT_POS(f_idata.s_scnptr);
519 /* now dump - idata don't need to do this cygwin ds is in .data! */
520 printf ("dumping .idata section... %lx bytes\n", f_idata.s_size);
521
522 dup_file_area(a_out,a_new,f_idata.s_size);
523
524 /* write rest of file */
525 printf ("writing rest of file\n");
526 size = lseek(a_out, 0, SEEK_END);
527 size = size - (f_idata.s_scnptr + f_idata.s_size); /* length remaining in a_out */
528 lseek(a_out, f_idata.s_scnptr + f_idata.s_size, SEEK_SET);
529
530 dup_file_area(a_out, a_new, size);
531 }
532
533 /*
534 * copy from aout to anew
535 */
536 static void dup_file_area(int a_out, int a_new, long size)
537 {
538 char page[BUFSIZ];
539 long n;
540 for (; size > 0; size -= sizeof (page))
541 {
542 n = size > sizeof (page) ? sizeof (page) : size;
543 if (read (a_out, page, n) != n || write (a_new, page, n) != n)
544 {
545 PERROR ("dump_out()");
546 }
547 }
548 }
549
550 static void dump_heap (int a_out, int a_new)
551 {
552 void *heap_data;
553 unsigned long heap_size;
554
555 printf ("Dumping heap into executable...\n");
556
557 heap_size = get_committed_heap_size ();
558 heap_data = get_heap_start ();
559
560 printf ("heap start in process - %p \n", heap_data);
561 printf ("heap size in bytes %lx\n", heap_size);
562
563 /* nt version rounds heap start - don't see why we should */
564 heap_index_in_executable = lseek(a_new, 0, SEEK_CUR);
565
566 if (write(a_new, heap_data, heap_size) != (int)heap_size)
567 {
568 PERROR("failed to write data section");
569 }
570 }
571
572 extern "C" void run_time_remap (char *a_name)
573 {
574 int a_out=-1;
575
576 if ((*heap_base)!=local_heap_base
577 ||(*heap_index)<local_heap_index
578 ||(*heap_size)<local_heap_size)
579 {
580 PERROR("heap parameters not in bss");
581 }
582
583 if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
584 {
585 PERROR (a_name);
586 }
587
588 /* load the heap */
589 lseek(a_out, heap_index_in_executable, SEEK_SET);
590
591 if (read (a_out, get_heap_start(),
592 (int)((unsigned char*)(local_heap_index)
593 -(unsigned char*)(local_heap_base))) < 0)
594 {
595 PERROR (a_name);
596 }
597 close(a_out);
598
599 /* switch to new heap */
600 heap_state=HEAP_LOADED;
601 *heap_flag=0;
602
603 close(a_out);
604 }
605
606 #endif /* HAVE_COFF_H */