1 /* Unexec for HP 9000 Series 800 machines.
  2 
  3   This file is in the public domain.
  4 
  5   Author: John V. Morris
  6 
  7   This file was written by John V. Morris at Hewlett Packard.
  8   Both the author and Hewlett Packard Co. have disclaimed the
  9   copyright on this file, and it is therefore in the public domain.
 10   (Search for "hp9k800" in copyright.list.)
 11 */
 12 
 13 /*
 14    Bob Desinger <hpsemc!bd@hplabs.hp.com>
 15 
 16    Note that the GNU project considers support for HP operation a
 17    peripheral activity which should not be allowed to divert effort
 18    from development of the GNU system.  Changes in this code will be
 19    installed when users send them in, but aside from that we don't
 20    plan to think about it, or about whether other Emacs maintenance
 21    might break it.
 22 
 23 
 24   Unexec creates a copy of the old a.out file, and replaces the old data
 25   area with the current data area.  When the new file is executed, the
 26   process will see the same data structures and data values that the
 27   original process had when unexec was called.
 28 
 29   Unlike other versions of unexec, this one copies symbol table and
 30   debug information to the new a.out file.  Thus, the new a.out file
 31   may be debugged with symbolic debuggers.
 32 
 33   If you fix any bugs in this, I'd like to incorporate your fixes.
 34   Send them to uunet!hpda!hpsemc!jmorris or jmorris%hpsemc@hplabs.HP.COM.
 35 
 36   CAVEATS:
 37   This routine saves the current value of all static and external
 38   variables.  This means that any data structure that needs to be
 39   initialized must be explicitly reset.  Variables will not have their
 40   expected default values.
 41 
 42   Unfortunately, the HP-UX signal handler has internal initialization
 43   flags which are not explicitly reset.  Thus, for signals to work in
 44   conjunction with this routine, the following code must executed when
 45   the new process starts up.
 46 
 47   void _sigreturn ();
 48   ...
 49   sigsetreturn (_sigreturn);
 50 */
 51 
 52 #ifdef emacs
 53 #include <config.h>
 54 #endif
 55 
 56 #include <stdio.h>
 57 #include <fcntl.h>
 58 #include <errno.h>
 59 
 60 #include <a.out.h>
 61 
 62 #include <dl.h>
 63 
 64 /* brk value to restore, stored as a global.
 65    This is really used only if we used shared libraries.  */
 66 static long brk_on_dump = 0;
 67 
 68 /* Called from main, if we use shared libraries.  */
 69 int
 70 run_time_remap (ignored)
 71      char *ignored;
 72 {
 73   brk ((char *) brk_on_dump);
 74 }
 75 
 76 #undef roundup
 77 #define roundup(x,n) (((x) + ((n) - 1)) & ~((n) - 1))  /* n is power of 2 */
 78 #define min(x,y)  (((x) < (y)) ? (x) : (y))
 79 
 80 
 81 /* Create a new a.out file, same as old but with current data space */
 82 
 83 unexec (new_name, old_name, new_end_of_text, dummy1, dummy2)
 84      char new_name[];           /* name of the new a.out file to be created */
 85      char old_name[];           /* name of the old a.out file */
 86      char *new_end_of_text;     /* ptr to new edata/etext; NOT USED YET */
 87      int dummy1, dummy2;        /* not used by emacs */
 88 {
 89   int old, new;
 90   int old_size, new_size;
 91   struct header hdr;
 92   struct som_exec_auxhdr auxhdr;
 93   long i;
 94 
 95   /* For the greatest flexibility, should create a temporary file in
 96      the same directory as the new file.  When everything is complete,
 97      rename the temp file to the new name.
 98      This way, a program could update its own a.out file even while
 99      it is still executing.  If problems occur, everything is still
100      intact.  NOT implemented.  */
101 
102   /* Open the input and output a.out files */
103   old = open (old_name, O_RDONLY);
104   if (old < 0)
105     { perror (old_name); exit (1); }
106   new = open (new_name, O_CREAT|O_RDWR|O_TRUNC, 0777);
107   if (new < 0)
108     { perror (new_name); exit (1); }
109 
110   /* Read the old headers */
111   read_header (old, &hdr, &auxhdr);
112 
113   brk_on_dump = (long) sbrk (0);
114 
115   /* Decide how large the new and old data areas are */
116   old_size = auxhdr.exec_dsize;
117   /* I suspect these two statements are separate
118      to avoid a compiler bug in hpux version 8.  */
119   i = (long) sbrk (0);
120   new_size = i - auxhdr.exec_dmem;
121 
122   /* Copy the old file to the new, up to the data space */
123   lseek (old, 0, 0);
124   copy_file (old, new, auxhdr.exec_dfile);
125 
126   /* Skip the old data segment and write a new one */
127   lseek (old, old_size, 1);
128   save_data_space (new, &hdr, &auxhdr, new_size);
129 
130   /* Copy the rest of the file */
131   copy_rest (old, new);
132 
133   /* Update file pointers since we probably changed size of data area */
134   update_file_ptrs (new, &hdr, &auxhdr, auxhdr.exec_dfile, new_size-old_size);
135 
136   /* Save the modified header */
137   write_header (new, &hdr, &auxhdr);
138 
139   /* Close the binary file */
140   close (old);
141   close (new);
142   return 0;
143 }
144 
145 /* Save current data space in the file, update header.  */
146 
147 save_data_space (file, hdr, auxhdr, size)
148      int file;
149      struct header *hdr;
150      struct som_exec_auxhdr *auxhdr;
151      int size;
152 {
153   /* Write the entire data space out to the file */
154   if (write (file, auxhdr->exec_dmem, size) != size)
155     { perror ("Can't save new data space"); exit (1); }
156 
157   /* Update the header to reflect the new data size */
158   auxhdr->exec_dsize = size;
159   auxhdr->exec_bsize = 0;
160 }
161 
162 /* Update the values of file pointers when something is inserted.  */
163 
164 update_file_ptrs (file, hdr, auxhdr, location, offset)
165      int file;
166      struct header *hdr;
167      struct som_exec_auxhdr *auxhdr;
168      unsigned int location;
169      int offset;
170 {
171   struct subspace_dictionary_record subspace;
172   int i;
173 
174   /* Increase the overall size of the module */
175   hdr->som_length += offset;
176 
177   /* Update the various file pointers in the header */
178 #define update(ptr) if (ptr > location) ptr = ptr + offset
179   update (hdr->aux_header_location);
180   update (hdr->space_strings_location);
181   update (hdr->init_array_location);
182   update (hdr->compiler_location);
183   update (hdr->symbol_location);
184   update (hdr->fixup_request_location);
185   update (hdr->symbol_strings_location);
186   update (hdr->unloadable_sp_location);
187   update (auxhdr->exec_tfile);
188   update (auxhdr->exec_dfile);
189 
190   /* Do for each subspace dictionary entry */
191   lseek (file, hdr->subspace_location, 0);
192   for (i = 0; i < hdr->subspace_total; i++)
193     {
194       if (read (file, &subspace, sizeof (subspace)) != sizeof (subspace))
195         { perror ("Can't read subspace record"); exit (1); }
196 
197       /* If subspace has a file location, update it */
198       if (subspace.initialization_length > 0
199           && subspace.file_loc_init_value > location)
200         {
201           subspace.file_loc_init_value += offset;
202           lseek (file, -sizeof (subspace), 1);
203           if (write (file, &subspace, sizeof (subspace)) != sizeof (subspace))
204             { perror ("Can't update subspace record"); exit (1); }
205         }
206     }
207 
208   /* Do for each initialization pointer record */
209   /* (I don't think it applies to executable files, only relocatables) */
210 #undef update
211 }
212 
213 /* Read in the header records from an a.out file.  */
214 
215 read_header (file, hdr, auxhdr)
216      int file;
217      struct header *hdr;
218      struct som_exec_auxhdr *auxhdr;
219 {
220 
221   /* Read the header in */
222   lseek (file, 0, 0);
223   if (read (file, hdr, sizeof (*hdr)) != sizeof (*hdr))
224     { perror ("Couldn't read header from a.out file"); exit (1); }
225 
226   if (hdr->a_magic != EXEC_MAGIC && hdr->a_magic != SHARE_MAGIC
227       &&  hdr->a_magic != DEMAND_MAGIC)
228     {
229       fprintf (stderr, "a.out file doesn't have valid magic number\n");
230       exit (1);
231     }
232 
233   lseek (file, hdr->aux_header_location, 0);
234   if (read (file, auxhdr, sizeof (*auxhdr)) != sizeof (*auxhdr))
235     {
236       perror ("Couldn't read auxiliary header from a.out file");
237       exit (1);
238     }
239 }
240 
241 /* Write out the header records into an a.out file.  */
242 
243 write_header (file, hdr, auxhdr)
244      int file;
245      struct header *hdr;
246      struct som_exec_auxhdr *auxhdr;
247 {
248   /* Update the checksum */
249   hdr->checksum = calculate_checksum (hdr);
250 
251   /* Write the header back into the a.out file */
252   lseek (file, 0, 0);
253   if (write (file, hdr, sizeof (*hdr)) != sizeof (*hdr))
254     { perror ("Couldn't write header to a.out file"); exit (1); }
255   lseek (file, hdr->aux_header_location, 0);
256   if (write (file, auxhdr, sizeof (*auxhdr)) != sizeof (*auxhdr))
257     { perror ("Couldn't write auxiliary header to a.out file"); exit (1); }
258 }
259 
260 /* Calculate the checksum of a SOM header record. */
261 
262 calculate_checksum (hdr)
263      struct header *hdr;
264 {
265   int checksum, i, *ptr;
266 
267   checksum = 0;  ptr = (int *) hdr;
268 
269   for (i = 0; i < sizeof (*hdr) / sizeof (int) - 1; i++)
270     checksum ^= ptr[i];
271 
272   return (checksum);
273 }
274 
275 /* Copy size bytes from the old file to the new one.  */
276 
277 copy_file (old, new, size)
278      int new, old;
279      int size;
280 {
281   int len;
282   int buffer[8192];  /* word aligned will be faster */
283 
284   for (; size > 0; size -= len)
285     {
286       len = min (size, sizeof (buffer));
287       if (read (old, buffer, len) != len)
288         { perror ("Read failure on a.out file"); exit (1); }
289       if (write (new, buffer, len) != len)
290         { perror ("Write failure in a.out file"); exit (1); }
291     }
292 }
293 
294 /* Copy the rest of the file, up to EOF.  */
295 
296 copy_rest (old, new)
297      int new, old;
298 {
299   int buffer[4096];
300   int len;
301 
302   /* Copy bytes until end of file or error */
303   while ((len = read (old, buffer, sizeof (buffer))) > 0)
304     if (write (new, buffer, len) != len) break;
305 
306   if (len != 0)
307     { perror ("Unable to copy the rest of the file"); exit (1); }
308 }
309 
310 #ifdef  DEBUG
311 display_header (hdr, auxhdr)
312      struct header *hdr;
313      struct som_exec_auxhdr *auxhdr;
314 {
315   /* Display the header information (debug) */
316   printf ("\n\nFILE HEADER\n");
317   printf ("magic number %d \n", hdr->a_magic);
318   printf ("text loc %.8x   size %d \n", auxhdr->exec_tmem, auxhdr->exec_tsize);
319   printf ("data loc %.8x   size %d \n", auxhdr->exec_dmem, auxhdr->exec_dsize);
320   printf ("entry     %x \n",   auxhdr->exec_entry);
321   printf ("Bss  segment size %u\n", auxhdr->exec_bsize);
322   printf ("\n");
323   printf ("data file loc %d    size %d\n",
324           auxhdr->exec_dfile, auxhdr->exec_dsize);
325   printf ("som_length %d\n", hdr->som_length);
326   printf ("unloadable sploc %d    size %d\n",
327           hdr->unloadable_sp_location, hdr->unloadable_sp_size);
328 }
329 #endif /* DEBUG */
330 
331 /* arch-tag: d55a09ac-9427-4ec4-8496-cb9d7710774f
332    (do not change this comment) */