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