update.
[chise/xemacs-chise.git] / src / unexcw.c
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 /* This is a complete rewrite, some code snarfed from unexnt.c and
24    unexec.c, Andy Piper (andy@xemacs.org) 13-1-98 */
25
26 #include <config.h>
27 #include "lisp.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <string.h>
34
35 #define DONT_ENCAPSULATE /* filenames are external in unex*.c */
36 #include "sysfile.h"
37
38 #define PERROR(arg) perror(arg);exit(-1) 
39
40 #if !defined(HAVE_A_OUT_H) && !defined(WIN32_NATIVE)
41 unexec (char *, char *, void *, void *, void *)
42 {
43   PERROR("cannot unexec() a.out.h not installed");
44 }
45 #else
46
47 #ifndef MAX_PATH
48 #define MAX_PATH 260
49 #endif
50
51 #ifdef MINGW
52 #include <../../include/a.out.h>
53 #else
54 #include <a.out.h>
55 #endif
56
57 #define STACK_SIZE 0x800000
58 #define ALLOC_UNIT 0xFFFF
59 #define ALLOC_MASK ~((unsigned long)(ALLOC_UNIT))
60 #define ALIGN_ALLOC(addr) \
61 ((((unsigned long)addr) + ALLOC_UNIT) & ALLOC_MASK)
62 /* Note that all sections must be aligned on a 0x1000 boundary so
63    this is the minimum size that our dummy bss can be. */
64 #ifndef NO_DEBUG
65 #define BSS_PAD_SIZE    0x1000
66 #else
67 #define BSS_PAD_SIZE    0
68 #endif
69
70 /* To prevent zero-initialized variables from being placed into the bss
71    section, use non-zero values to represent an uninitialized state.  */
72 #define UNINIT_PTR ((void *) 0xF0A0F0A0)
73 #define UNINIT_LONG (0xF0A0F0A0L)
74
75 static void get_section_info (int a_out, char* a_name);
76 static void copy_executable_and_dump_data_section (int a_out, int a_new);
77 static void dup_file_area(int a_out, int a_new, long size);
78 #if 0
79 static void write_int_to_bss(int a_out, int a_new, void* va, void* newval);
80 #endif
81
82 /* Cached info about the .data section in the executable.  */
83 void* data_start_va = UNINIT_PTR;
84 unsigned long  data_size = UNINIT_LONG;
85
86 /* Cached info about the .bss section in the executable.  */
87 void* bss_start = UNINIT_PTR;
88 unsigned long  bss_size = UNINIT_LONG;
89 int sections_reversed = 0;
90 FILHDR f_hdr;
91 PEAOUTHDR f_ohdr;
92 SCNHDR f_data, f_bss, f_text, f_nextdata;
93
94 #define PERROR(arg) perror(arg);exit(-1) 
95 #define CHECK_AOUT_POS(a) \
96 if (lseek(a_out, 0, SEEK_CUR) != a) \
97 { \
98   printf("we are at %lx, should be at %lx\n", \
99          lseek(a_out, 0, SEEK_CUR), a); \
100   exit(-1); \
101 }
102
103 /* Dump out .data and .bss sections into a new executable.  */
104 int
105 unexec (char *out_name, char *in_name, uintptr_t start_data, 
106         uintptr_t d1, uintptr_t d2)
107 {
108   /* ugly nt hack - should be in lisp */
109   int a_new, a_out = -1;
110   char new_name[MAX_PATH], a_name[MAX_PATH];
111   char *ptr;
112   
113   /* Make sure that the input and output filenames have the
114      ".exe" extension...patch them up if they don't.  */
115   strcpy (a_name, in_name);
116   ptr = a_name + strlen (a_name) - 4;
117   if (strcmp (ptr, ".exe"))
118     strcat (a_name, ".exe");
119
120   strcpy (new_name, out_name);
121   ptr = new_name + strlen (new_name) - 4;
122   if (strcmp (ptr, ".exe"))
123     strcat (new_name, ".exe");
124
125   /* We need to round off our heap to NT's allocation unit (64KB).  */
126   /* round_heap (get_allocation_unit ()); */
127
128   if (a_name && (a_out = open (a_name, O_RDONLY | OPEN_BINARY)) < 0)
129     {
130       PERROR (a_name);
131     }
132
133   if ((a_new = open (new_name, O_WRONLY | O_TRUNC | O_CREAT | OPEN_BINARY,
134                      0755)) < 0)
135     {
136       PERROR (new_name);
137     }
138
139   /* Get the interesting section info, like start and size of .bss...  */
140   get_section_info (a_out, a_name);
141
142   copy_executable_and_dump_data_section (a_out, a_new);
143
144   close(a_out);
145   close(a_new);
146   return 0;
147 }
148
149 /* Flip through the executable and cache the info necessary for dumping.  */
150 static void get_section_info (int a_out, char* a_name)
151 {
152   extern char my_ebss[];
153   /* From lastfile.c  */
154   extern char my_edata[];
155
156   if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
157     {
158       PERROR (a_name);
159     }
160
161   if (f_hdr.e_magic != DOSMAGIC) 
162     {
163       PERROR("unknown exe header");
164     }
165
166   /* Check the NT header signature ...  */
167   if (f_hdr.nt_signature != NT_SIGNATURE) 
168     {
169       PERROR("invalid nt header");
170     }
171
172   /* Flip through the sections for .data and .bss ...  */
173   if (f_hdr.f_opthdr > 0)
174     {
175       if (read (a_out, &f_ohdr, AOUTSZ) != AOUTSZ)
176         {
177           PERROR (a_name);
178         }
179     }
180   /* Loop through .data & .bss section headers, copying them in.
181      With newer lds these are reversed so we have to cope with both */
182   lseek (a_out, sizeof (f_hdr) + f_hdr.f_opthdr, 0);
183
184   if (read (a_out, &f_text, sizeof (f_text)) != sizeof (f_text)
185       ||
186       strcmp (f_text.s_name, ".text"))
187     {
188       PERROR ("no .text section");
189     }
190
191   /* The .bss section.  */
192   if (read (a_out, &f_bss, sizeof (f_bss)) != sizeof (f_bss)
193       ||
194       (strcmp (f_bss.s_name, ".bss") && strcmp (f_bss.s_name, ".data")))
195     {
196       PERROR ("no .bss / .data section");
197     }
198
199   /* check for reversed .bss and .data */
200   if (!strcmp(f_bss.s_name, ".data"))
201     {
202       printf(".data and .bss reversed\n");
203       sections_reversed = 1;
204       memcpy(&f_data, &f_bss, sizeof(f_bss));
205     }
206
207   /* The .data section.  */
208   if (!sections_reversed)
209     {
210       if (read (a_out, &f_data, sizeof (f_data)) != sizeof (f_data)
211           ||
212           strcmp (f_data.s_name, ".data"))
213         {
214           PERROR ("no .data section");
215         }
216     }
217   else
218     {
219       if (read (a_out, &f_bss, sizeof (f_bss)) != sizeof (f_bss)
220           ||
221           strcmp (f_bss.s_name, ".bss"))
222         {
223           PERROR ("no .bss section");
224         }
225     }
226   
227   bss_start = (void *) ((char*)f_ohdr.ImageBase + f_bss.s_vaddr);
228   bss_size = (unsigned long)((char*)&my_ebss-(char*)bss_start);
229   
230   /* must keep bss data that we want to be blank as blank */
231   printf("found bss - keeping %lx of %lx bytes\n", bss_size, f_ohdr.bsize);
232
233   /* The .data section.  */
234   data_start_va = (void *) ((char*)f_ohdr.ImageBase + f_data.s_vaddr);
235
236   /* We want to only write Emacs data back to the executable,
237      not any of the library data (if library data is included,
238      then a dumped Emacs won't run on system versions other
239      than the one Emacs was dumped on).  */
240   data_size = (unsigned long)my_edata - (unsigned long)data_start_va;
241   printf("found data - keeping %lx of %lx bytes\n", data_size, f_ohdr.dsize);
242
243   /* The following data section - often .idata */
244   if (read (a_out, &f_nextdata, sizeof (f_nextdata)) != sizeof (f_nextdata)
245       &&
246       strcmp (&f_nextdata.s_name[2], "data"))
247     {
248       PERROR ("no other data section");
249     }
250 }
251
252 /* The dump routines.  */
253
254 static void
255 copy_executable_and_dump_data_section (int a_out, int a_new)
256 {
257   long size=0;
258   unsigned long new_data_size, new_bss_size, 
259     bss_padding, file_sz_change, data_padding=0,
260     f_data_s_vaddr = f_data.s_vaddr,
261     f_data_s_scnptr = f_data.s_scnptr,
262     f_bss_s_vaddr = f_bss.s_vaddr, 
263     f_nextdata_s_scnptr = f_nextdata.s_scnptr;
264
265   int i;
266   void* empty_space;
267   extern int static_heap_dumped;
268   SCNHDR section;
269   /* calculate new sizes:
270
271      f_ohdr.dsize is the total initialized data size on disk which is
272      f_data.s_size + f_idata.s_size.
273
274      f_ohdr.data_start is the base addres of all data and so should
275      not be changed.
276      
277      *.s_vaddr is the virtual address of the start of the section
278      *normalized from f_ohdr.ImageBase.
279
280      *.s_paddr appears to be the number of bytes in the section
281      *actually used (whereas *.s_size is aligned).
282
283      bsize is now 0 since subsumed into .data
284      dsize is dsize + (f_data.s_vaddr - f_bss.s_vaddr)
285      f_data.s_vaddr is f_bss.s_vaddr
286      f_data.s_size is new dsize maybe.
287      what about s_paddr & s_scnptr?  */
288
289   /* this is the amount the file increases in size */
290   if (!sections_reversed)
291     {
292       new_bss_size = f_data.s_vaddr - f_bss.s_vaddr;
293       data_padding = 0;
294     }
295   else
296     {
297       new_bss_size = f_nextdata.s_vaddr - f_bss.s_vaddr;
298       data_padding = (f_bss.s_vaddr - f_data.s_vaddr) - f_data.s_size;
299     }
300
301   if ((new_bss_size - bss_size) < BSS_PAD_SIZE)
302     { 
303       PERROR (".bss free space too small");
304     }
305
306   file_sz_change=(new_bss_size + data_padding) - BSS_PAD_SIZE;
307   new_data_size=f_ohdr.dsize + file_sz_change;
308
309   if (!sections_reversed)
310     {
311       f_data.s_vaddr = f_bss.s_vaddr;
312     }
313   f_data.s_paddr += file_sz_change;
314 #if 0 
315   if (f_data.s_size + f_nextdata.s_size != f_ohdr.dsize)
316     {
317       printf("section size doesn't tally with dsize %lx != %lx\n", 
318              f_data.s_size + f_nextdata.s_size, f_ohdr.dsize);
319     }
320 #endif
321   f_data.s_size += file_sz_change;
322   lseek (a_new, 0, SEEK_SET);
323   /* write file header */
324   f_hdr.f_symptr += file_sz_change;
325 #ifdef NO_DEBUG
326   f_hdr.f_nscns--;
327 #endif
328
329   printf("writing file header\n");
330   if (write(a_new, &f_hdr, sizeof(f_hdr)) != sizeof(f_hdr))
331     {
332       PERROR("failed to write file header");
333     }
334   /* write optional header fixing dsize & bsize*/
335   printf("writing optional header\n");
336   printf("new data size is %lx, >= %lx\n", new_data_size,
337          f_ohdr.dsize + f_ohdr.bsize);
338   if (new_data_size < f_ohdr.dsize + f_ohdr.bsize )
339     {
340       printf("warning: new data size is < approx\n");
341     }
342   f_ohdr.dsize=new_data_size;
343   f_ohdr.bsize=BSS_PAD_SIZE;
344   /* Prevent stack overflow with regexp usage. */
345   f_ohdr.SizeOfStackReserve = STACK_SIZE;
346
347   if (write(a_new, &f_ohdr, sizeof(f_ohdr)) != sizeof(f_ohdr))
348     {
349       PERROR("failed to write optional header");
350     }
351   /* write text as is */
352   printf("writing text header (unchanged)\n");
353
354   if (write(a_new, &f_text, sizeof(f_text)) != sizeof(f_text))
355     {
356       PERROR("failed to write text header");
357     }
358 #ifndef NO_DEBUG
359   /* Write small bss section. */
360   if (!sections_reversed)
361     {
362       f_bss.s_size = BSS_PAD_SIZE;
363       f_bss.s_paddr = BSS_PAD_SIZE;
364       f_bss.s_vaddr = f_data.s_vaddr - BSS_PAD_SIZE;
365       if (write(a_new, &f_bss, sizeof(f_bss)) != sizeof(f_bss))
366         {
367           PERROR("failed to write bss header");
368         }
369     }
370 #endif
371   /* write new data header */
372   printf("writing .data header\n");
373
374   if (write(a_new, &f_data, sizeof(f_data)) != sizeof(f_data))
375     {
376       PERROR("failed to write data header");
377     }
378 #ifndef NO_DEBUG
379   /* Write small bss section. */
380   if (sections_reversed)
381     {
382       f_bss.s_size = BSS_PAD_SIZE;
383       f_bss.s_paddr = BSS_PAD_SIZE;
384       f_bss.s_vaddr = f_nextdata.s_vaddr - BSS_PAD_SIZE;
385       if (write(a_new, &f_bss, sizeof(f_bss)) != sizeof(f_bss))
386         {
387           PERROR("failed to write bss header");
388         }
389     }
390 #endif
391   printf("writing following data header\n");
392   f_nextdata.s_scnptr += file_sz_change;
393   if (f_nextdata.s_lnnoptr != 0) f_nextdata.s_lnnoptr += file_sz_change;
394   if (f_nextdata.s_relptr != 0) f_nextdata.s_relptr += file_sz_change;
395   if (write(a_new, &f_nextdata, sizeof(f_nextdata)) != sizeof(f_nextdata))
396     {
397       PERROR("failed to write nextdata header");
398     }
399
400   /* copy other section headers adjusting the file offset */
401   for (i=0; i<(f_hdr.f_nscns-3); i++)
402     {
403       if (read (a_out, &section, sizeof (section)) != sizeof (section))
404         {
405           PERROR ("no .data section");
406         }
407       
408       section.s_scnptr += file_sz_change;
409       if (section.s_lnnoptr != 0) section.s_lnnoptr += file_sz_change;
410       if (section.s_relptr != 0) section.s_relptr += file_sz_change;
411
412       if (write(a_new, &section, sizeof(section)) != sizeof(section))
413         {
414           PERROR("failed to write data header");
415         }
416     }
417 #ifdef NO_DEBUG
418   /* dump bss to maintain offsets */
419   memset(&f_bss, 0, sizeof(f_bss));
420   if (write(a_new, &f_bss, sizeof(f_bss)) != sizeof(f_bss))
421     {
422       PERROR("failed to write bss header");
423     }
424 #endif
425   size=lseek(a_new, 0, SEEK_CUR);
426   CHECK_AOUT_POS(size);
427
428   /* copy eveything else until start of data */
429   size = f_data_s_scnptr - lseek (a_out, 0, SEEK_CUR);
430
431   printf ("copying executable up to data section ... %lx bytes\n", 
432           size);
433   dup_file_area(a_out, a_new, size);
434
435   CHECK_AOUT_POS(f_data_s_scnptr);
436
437   if (!sections_reversed)
438     {
439       /* dump bss + padding between sections, sans small bss pad */
440       printf ("dumping .bss into executable... %lx bytes\n", bss_size);
441       if (write(a_new, bss_start, bss_size) != (int)bss_size)
442         {
443           PERROR("failed to write bss section");
444         }
445       
446       /* pad, needs to be zero */
447       bss_padding = (new_bss_size - bss_size) - BSS_PAD_SIZE;
448       if (bss_padding < 0)
449         {
450           PERROR("padded .bss too small");
451         }
452       printf ("padding .bss ... %lx bytes\n", bss_padding);
453       empty_space = malloc(bss_padding);
454       memset(empty_space, 0, bss_padding);
455       if (write(a_new, empty_space, bss_padding) != (int)bss_padding)
456         {
457           PERROR("failed to write bss section");
458         }
459       free(empty_space);
460     }
461
462   /* tell dumped version not to free pure heap */
463   static_heap_dumped = 1;
464   /* Get a pointer to the raw data in our address space.  */
465   printf ("dumping .data section... %lx bytes\n", data_size);
466   if (write(a_new, data_start_va, data_size) != (int)data_size)
467     {
468       PERROR("failed to write data section");
469     }
470   /* were going to use free again ... */
471   static_heap_dumped = 0;
472   
473   size = lseek(a_out, f_data_s_scnptr + data_size, SEEK_SET);
474
475   if (!sections_reversed)
476     {
477       size = f_nextdata_s_scnptr - size;
478       dup_file_area(a_out, a_new, size);
479     }
480   else
481     {
482       /* need to pad to bss with data in file */
483       printf ("padding .data ... %lx bytes\n", data_padding);
484       size = (f_bss_s_vaddr - f_data_s_vaddr) - data_size;
485       dup_file_area(a_out, a_new, size);
486
487       /* dump bss + padding between sections */
488       printf ("dumping .bss into executable... %lx bytes\n", bss_size);
489       if (write(a_new, bss_start, bss_size) != (int)bss_size)
490         {
491           PERROR("failed to write bss section");
492         }
493       
494       /* pad, needs to be zero */
495       bss_padding = (new_bss_size - bss_size) - BSS_PAD_SIZE;
496       if (bss_padding < 0)
497         {
498           PERROR("padded .bss too small");
499         }
500       printf ("padding .bss ... %lx bytes\n", bss_padding);
501       empty_space = malloc(bss_padding);
502       memset(empty_space, 0, bss_padding);
503       if (write(a_new, empty_space, bss_padding) != (int)bss_padding)
504         {
505           PERROR("failed to write bss section");
506         }
507       free(empty_space);
508       if (lseek(a_new, 0, SEEK_CUR) != f_nextdata.s_scnptr)
509         {
510           printf("at %lx should be at %lx\n", 
511                  lseek(a_new, 0, SEEK_CUR),
512                  f_nextdata.s_scnptr);
513           PERROR("file positioning error\n");
514         }
515       lseek(a_out, f_nextdata_s_scnptr, SEEK_SET);
516     }
517
518   CHECK_AOUT_POS(f_nextdata_s_scnptr);
519
520   /* now dump - nextdata don't need to do this cygwin ds is in .data! */
521   printf ("dumping following data section... %lx bytes\n", f_nextdata.s_size);
522
523   dup_file_area(a_out,a_new,f_nextdata.s_size);
524
525   /* write rest of file */
526   printf ("writing rest of file\n");
527   size = lseek(a_out, 0, SEEK_END);
528   size = size - (f_nextdata_s_scnptr + f_nextdata.s_size); /* length remaining in a_out */
529   lseek(a_out, f_nextdata_s_scnptr + f_nextdata.s_size, SEEK_SET);
530
531   dup_file_area(a_out, a_new, size);
532 }
533
534 /*
535  * copy from aout to anew
536  */
537 static void dup_file_area(int a_out, int a_new, long size)
538 {
539   char page[BUFSIZ];
540   long n;
541   for (; size > 0; size -= sizeof (page))
542     {
543       n = size > sizeof (page) ? sizeof (page) : size;
544       if (read (a_out, page, n) != n || write (a_new, page, n) != n)
545         {
546           PERROR ("dump_out()");
547         }
548     }
549 }
550
551 #if 0
552 static void write_int_to_bss(int a_out, int a_new, void* va, void* newval)
553 {
554   int cpos;
555
556   cpos = lseek(a_new, 0, SEEK_CUR);
557   if (va < bss_start || va > bss_start + f_data.s_size)
558     {
559       PERROR("address not in data space\n");
560     }
561   lseek(a_new, f_data.s_scnptr + ((unsigned long)va - 
562                                   (unsigned long)bss_start), SEEK_SET);
563   if (write(a_new, newval, sizeof(int)) != (int)sizeof(int))
564     {
565       PERROR("failed to write int value");
566     }
567   lseek(a_new, cpos, SEEK_SET);
568 }
569 #endif
570
571 #endif /* HAVE_A_OUT_H */