XEmacs 21.2.36 "Notos"
[chise/xemacs-chise.git.1] / src / unexhp9k800.c
1 /* Unexec for HP 9000 Series 800 machines.
2    Bob Desinger <hpsemc!bd@hplabs.hp.com>
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
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* Synched up with: Not synched with FSF. */
22
23 /*
24
25   Unexec creates a copy of the old a.out file, and replaces the old data
26   area with the current data area.  When the new file is executed, the
27   process will see the same data structures and data values that the
28   original process had when unexec was called.
29
30   Unlike other versions of unexec, this one copies symbol table and
31   debug information to the new a.out file.  Thus, the new a.out file
32   may be debugged with symbolic debuggers.
33
34   If you fix any bugs in this, I'd like to incorporate your fixes.
35   Send them to uunet!hpda!hpsemc!jmorris or jmorris%hpsemc@hplabs.HP.COM.
36
37   CAVEATS:
38   This routine saves the current value of all static and external
39   variables.  This means that any data structure that needs to be
40   initialized must be explicitly reset.  Variables will not have their
41   expected default values.
42
43   Unfortunately, the HP-UX signal handler has internal initialization
44   flags which are not explicitly reset.  Thus, for signals to work in
45   conjunction with this routine, the following code must executed when
46   the new process starts up.
47
48   void _sigreturn();
49   ...
50   sigsetreturn(_sigreturn);
51 */
52
53 \f
54 #include <config.h>
55 #include <stdlib.h>
56 #include <stdio.h>
57 #include <fcntl.h>
58 #include <errno.h>
59
60 #include <a.out.h>
61 #include "lisp.h"
62
63 /*
64  * Minor modification to enable dumping with shared libraries added by
65  * Dipankar Gupta (dg@hplb.hpl.hp.com). I studied Oliver Laumann's
66  * more elaborate dynamic loading scheme in ELK while implementing
67  * this, but don't use any of his machinery.
68  *
69  * Stores the BRK value at dump time, and uses the RUN_TIME_REMAP hook
70  * to break back to the stored value when the dumped executable is restarted.
71  *
72  * CAVEATS (addenda):
73  * 1. Text area of the shlibs are not stored. Thus, if a shared library is
74  *    replaced between the time of dump and execution, all bets are off.
75  *
76  * 2. Assumes that the data and bss area are adjacent, which is true of the
77  *    current VM implementation.
78  *
79  * 3. Any setup that defines HPUX_USE_SHLIBS *must* also define
80  *    RUN_TIME_REMAP.
81  */
82
83 #ifdef HPUX_USE_SHLIBS
84 #include <dl.h>                 /* User-space dynamic loader entry points */
85 static void Save_Shared_Data (void);
86 static void Restore_Shared_Data (void);
87 #endif
88
89 void write_header(int file, struct header *hdr, struct som_exec_auxhdr *auxhdr);
90 void read_header (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr);
91 void save_data_space (int file, struct header *hdr,
92                       struct som_exec_auxhdr *auxhdr, int size);
93 void copy_rest (int old, int new);
94 void copy_file (int old, int new, int size);
95 void update_file_ptrs(int file, struct header *hdr,
96                       struct som_exec_auxhdr *auxhdr,
97                       unsigned int location, int offset);
98 int calculate_checksum(struct header *hdr);
99
100 /* Create a new a.out file, same as old but with current data space */
101 int
102 unexec (char *new_name,         /* name of the new a.out file to be created */
103         char *old_name,         /* name of the old a.out file */
104         uintptr_t new_end_of_text, /* ptr to new edata/etext; NOT USED YET */
105         uintptr_t dummy1, uintptr_t dummy2) /* not used by emacs */
106 {
107   int old, new;
108   int old_size, new_size;
109   struct header hdr;
110   struct som_exec_auxhdr auxhdr;
111   long i;
112
113   /* For the greatest flexibility, should create a temporary file in
114      the same directory as the new file.  When everything is complete,
115      rename the temp file to the new name.
116      This way, a program could update its own a.out file even while
117      it is still executing.  If problems occur, everything is still
118      intact.  NOT implemented.  */
119
120   /* Open the input and output a.out files */
121   old = open (old_name, O_RDONLY);
122   if (old < 0)
123     { perror(old_name); exit(1); }
124   new = open (new_name, O_CREAT|O_RDWR|O_TRUNC, 0777);
125   if (new < 0)
126     { perror(new_name); exit(1); }
127
128   /* Read the old headers */
129   read_header(old, &hdr, &auxhdr);
130
131 #ifdef HPUX_USE_SHLIBS
132   Save_Shared_Data(); /* Save break value (added: dg@hplb.hpl.hp.com) */
133 #endif
134   /* Decide how large the new and old data areas are */
135   old_size = auxhdr.exec_dsize;
136   /* I suspect these two statements are separate
137      to avoid a compiler bug in hpux version 8.  */
138   i = (long) sbrk (0);
139   new_size = i - auxhdr.exec_dmem;
140
141   /* Copy the old file to the new, up to the data space */
142   lseek(old, 0, 0);
143   copy_file(old, new, auxhdr.exec_dfile);
144
145   /* Skip the old data segment and write a new one */
146   lseek(old, old_size, 1);
147   save_data_space(new, &hdr, &auxhdr, new_size);
148
149   /* Copy the rest of the file */
150   copy_rest(old, new);
151
152   /* Update file pointers since we probably changed size of data area */
153   update_file_ptrs(new, &hdr, &auxhdr, auxhdr.exec_dfile, new_size-old_size);
154
155   /* Save the modified header */
156   write_header(new, &hdr, &auxhdr);
157
158   /* Close the binary file */
159   close (old);
160   close (new);
161   return 0;
162 }
163
164 /* Save current data space in the file, update header.  */
165
166 void
167 save_data_space (int file, struct header *hdr,
168                  struct som_exec_auxhdr *auxhdr, int size)
169 {
170   /* Write the entire data space out to the file */
171   if (write(file, (void *)auxhdr->exec_dmem, size) != size)
172     { perror("Can't save new data space"); exit(1); }
173
174   /* Update the header to reflect the new data size */
175   auxhdr->exec_dsize = size;
176   auxhdr->exec_bsize = 0;
177 }
178
179 /* Update the values of file pointers when something is inserted.  */
180
181 void
182 update_file_ptrs(int file, struct header *hdr,
183                  struct som_exec_auxhdr *auxhdr,
184                  unsigned int location, int offset)
185 {
186   struct subspace_dictionary_record subspace;
187   int i;
188
189   /* Increase the overall size of the module */
190   hdr->som_length += offset;
191
192   /* Update the various file pointers in the header */
193 #define update(ptr) if (ptr > location) ptr = ptr + offset
194   update(hdr->aux_header_location);
195   update(hdr->space_strings_location);
196   update(hdr->init_array_location);
197   update(hdr->compiler_location);
198   update(hdr->symbol_location);
199   update(hdr->fixup_request_location);
200   update(hdr->symbol_strings_location);
201   update(hdr->unloadable_sp_location);
202   update(auxhdr->exec_tfile);
203   update(auxhdr->exec_dfile);
204
205   /* Do for each subspace dictionary entry */
206   lseek(file, hdr->subspace_location, 0);
207   for (i = 0; i < hdr->subspace_total; i++)
208     {
209       if (read(file, &subspace, sizeof(subspace)) != sizeof(subspace))
210         { perror("Can't read subspace record"); exit(1); }
211
212       /* If subspace has a file location, update it */
213       if (subspace.initialization_length > 0
214           && subspace.file_loc_init_value > location)
215         {
216           subspace.file_loc_init_value += offset;
217           lseek(file, -sizeof(subspace), 1);
218           if (write(file, &subspace, sizeof(subspace)) != sizeof(subspace))
219             { perror("Can't update subspace record"); exit(1); }
220         }
221     }
222
223   /* Do for each initialization pointer record */
224   /* (I don't think it applies to executable files, only relocatables) */
225 #undef update
226 }
227
228 /* Read in the header records from an a.out file.  */
229
230 void
231 read_header(int file, struct header *hdr, struct som_exec_auxhdr *auxhdr)
232 {
233
234   /* Read the header in */
235   lseek(file, 0, 0);
236   if (read(file, hdr, sizeof(*hdr)) != sizeof(*hdr))
237     { perror("Couldn't read header from a.out file"); exit(1); }
238
239   if (hdr->a_magic != EXEC_MAGIC && hdr->a_magic != SHARE_MAGIC
240       &&  hdr->a_magic != DEMAND_MAGIC)
241     {
242       fprintf(stderr, "a.out file doesn't have legal magic number\n");
243       exit(1);
244     }
245
246   lseek(file, hdr->aux_header_location, 0);
247   if (read(file, auxhdr, sizeof(*auxhdr)) != sizeof(*auxhdr))
248     {
249       perror("Couldn't read auxiliary header from a.out file");
250       exit(1);
251     }
252 }
253
254 /* Write out the header records into an a.out file.  */
255 void
256 write_header(int file, struct header *hdr, struct som_exec_auxhdr *auxhdr)
257 {
258   /* Update the checksum */
259   hdr->checksum = calculate_checksum(hdr);
260
261   /* Write the header back into the a.out file */
262   lseek(file, 0, 0);
263   if (write(file, hdr, sizeof(*hdr)) != sizeof(*hdr))
264     { perror("Couldn't write header to a.out file"); exit(1); }
265   lseek(file, hdr->aux_header_location, 0);
266   if (write(file, auxhdr, sizeof(*auxhdr)) != sizeof(*auxhdr))
267     { perror("Couldn't write auxiliary header to a.out file"); exit(1); }
268 }
269
270 /* Calculate the checksum of a SOM header record. */
271 int
272 calculate_checksum(struct header *hdr)
273 {
274   int checksum, i, *ptr;
275
276   checksum = 0;  ptr = (int *) hdr;
277
278   for (i=0; i<sizeof(*hdr)/sizeof(int)-1; i++)
279     checksum ^= ptr[i];
280
281   return(checksum);
282 }
283
284 /* Copy size bytes from the old file to the new one.  */
285 void
286 copy_file (int old, int new, int size)
287 {
288   int len;
289   int buffer[8192];  /* word aligned will be faster */
290
291   for (; size > 0; size -= len)
292     {
293       len = size < sizeof (buffer) ? size : sizeof (buffer);
294       if (read (old, buffer, len) != len)
295         {
296           perror ("Read failure on a.out file");
297           exit (1);
298         }
299       if (write (new, buffer, len) != len)
300         {
301           perror ("Write failure in a.out file");
302           exit (1);
303         }
304     }
305 }
306
307 /* Copy the rest of the file, up to EOF.  */
308 void
309 copy_rest (int old, int new)
310 {
311   int buffer[4096];
312   int len;
313
314   /* Copy bytes until end of file or error */
315   while ( (len = read(old, buffer, sizeof(buffer))) > 0)
316     if (write(new, buffer, len) != len) break;
317
318   if (len != 0)
319     { perror("Unable to copy the rest of the file"); exit(1); }
320 }
321
322 #ifdef  DEBUG
323 display_header(struct header *hdr, struct som_exec_auxhdr *auxhdr)
324 {
325   /* Display the header information (debug) */
326   printf("\n\nFILE HEADER\n");
327   printf("magic number %d \n", hdr->a_magic);
328   printf("text loc %.8x   size %d \n", auxhdr->exec_tmem, auxhdr->exec_tsize);
329   printf("data loc %.8x   size %d \n", auxhdr->exec_dmem, auxhdr->exec_dsize);
330   printf("entry     %x \n",   auxhdr->exec_entry);
331   printf("Bss  segment size %u\n", auxhdr->exec_bsize);
332   printf("\n");
333   printf("data file loc %d    size %d\n",
334          auxhdr->exec_dfile, auxhdr->exec_dsize);
335   printf("som_length %d\n", hdr->som_length);
336   printf("unloadable sploc %d    size %d\n",
337          hdr->unloadable_sp_location, hdr->unloadable_sp_size);
338 }
339 #endif /* DEBUG */
340
341 #ifdef HPUX_USE_SHLIBS
342 /* Added machinery for shared libs... see comments at the beginning of this file. */
343
344 void *Brk_On_Dump = 0;          /* Brk value to restore... stored as a global */
345
346 static void
347 Save_Shared_Data (void)
348 {
349   Brk_On_Dump = sbrk (0);
350 }
351
352 static void
353 Restore_Shared_Data (void)
354 {
355   brk (Brk_On_Dump);
356 }
357
358 /* run_time_remap is the magic called by startup code in the dumped executable
359    if RUN_TIME_REMAP is set. */
360 int
361 run_time_remap (char *dummy)
362 {
363   Restore_Shared_Data ();
364   return 0;
365 }
366 #endif /* HPUX_USE_SHLIBS */