unexhp9k800.c 9.59 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/* Unexec for HP 9000 Series 800 machines.

  This file is in the public domain.

  Author: John V. Morris

  This file was written by John V. Morris at Hewlett Packard.
  Both the author and Hewlett Packard Co. have disclaimed the
  copyright on this file, and it is therefore in the public domain.
  (Search for "hp9k800" in copyright.list.)
*/

/*
   Bob Desinger <hpsemc!bd@hplabs.hp.com>

   Note that the GNU project considers support for HP operation a
   peripheral activity which should not be allowed to divert effort
   from development of the GNU system.  Changes in this code will be
   installed when users send them in, but aside from that we don't
   plan to think about it, or about whether other Emacs maintenance
   might break it.


  Unexec creates a copy of the old a.out file, and replaces the old data
  area with the current data area.  When the new file is executed, the
  process will see the same data structures and data values that the
  original process had when unexec was called.

  Unlike other versions of unexec, this one copies symbol table and
  debug information to the new a.out file.  Thus, the new a.out file
  may be debugged with symbolic debuggers.

  If you fix any bugs in this, I'd like to incorporate your fixes.
  Send them to uunet!hpda!hpsemc!jmorris or jmorris%hpsemc@hplabs.HP.COM.

  CAVEATS:
  This routine saves the current value of all static and external
  variables.  This means that any data structure that needs to be
  initialized must be explicitly reset.  Variables will not have their
  expected default values.

  Unfortunately, the HP-UX signal handler has internal initialization
  flags which are not explicitly reset.  Thus, for signals to work in
  conjunction with this routine, the following code must executed when
  the new process starts up.

  void _sigreturn ();
  ...
  sigsetreturn (_sigreturn);
*/

#include <config.h>
53
#include "unexec.h"
54
#include "lisp.h"
55

56 57 58 59 60 61 62 63 64 65 66 67
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <a.out.h>
#include <dl.h>

/* brk value to restore, stored as a global.
   This is really used only if we used shared libraries.  */
static long brk_on_dump = 0;

/* Called from main, if we use shared libraries.  */
int
Andreas Schwab's avatar
Andreas Schwab committed
68
run_time_remap (char *ignored)
69 70 71 72 73 74 75
{
  brk ((char *) brk_on_dump);
}

#undef roundup
#define roundup(x,n) (((x) + ((n) - 1)) & ~((n) - 1))  /* n is power of 2 */

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
/* Report a fatal error and exit.  */
static _Noreturn void
unexec_error (char const *msg)
{
  perror (msg);
  exit (1);
}

/* Do an lseek and check the result.  */
static void
check_lseek (int fd, off_t offset, int whence)
{
  if (lseek (fd, offset, whence) < 0)
    unexec_error ("Cannot lseek");
}

92 93
/* Save current data space in the file, update header.  */

Andreas Schwab's avatar
Andreas Schwab committed
94 95 96
static void
save_data_space (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr,
		 int size)
97 98 99
{
  /* Write the entire data space out to the file */
  if (write (file, auxhdr->exec_dmem, size) != size)
100
    unexec_error ("Can't save new data space");
101 102 103 104 105 106 107 108

  /* Update the header to reflect the new data size */
  auxhdr->exec_dsize = size;
  auxhdr->exec_bsize = 0;
}

/* Update the values of file pointers when something is inserted.  */

Andreas Schwab's avatar
Andreas Schwab committed
109 110 111
static void
update_file_ptrs (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr,
		  unsigned int location, int offset)
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
{
  struct subspace_dictionary_record subspace;
  int i;

  /* Increase the overall size of the module */
  hdr->som_length += offset;

  /* Update the various file pointers in the header */
#define update(ptr) if (ptr > location) ptr = ptr + offset
  update (hdr->aux_header_location);
  update (hdr->space_strings_location);
  update (hdr->init_array_location);
  update (hdr->compiler_location);
  update (hdr->symbol_location);
  update (hdr->fixup_request_location);
  update (hdr->symbol_strings_location);
  update (hdr->unloadable_sp_location);
  update (auxhdr->exec_tfile);
  update (auxhdr->exec_dfile);

  /* Do for each subspace dictionary entry */
133
  check_lseek (file, hdr->subspace_location, 0);
134 135
  for (i = 0; i < hdr->subspace_total; i++)
    {
136 137 138
      ptrdiff_t subspace_size = sizeof subspace;
      if (read (file, &subspace, subspace_size) != subspace_size)
	unexec_error ("Can't read subspace record");
139 140 141 142 143 144

      /* If subspace has a file location, update it */
      if (subspace.initialization_length > 0
	  && subspace.file_loc_init_value > location)
	{
	  subspace.file_loc_init_value += offset;
145 146 147
	  check_lseek (file, -subspace_size, 1);
	  if (write (file, &subspace, subspace_size) != subspace_size)
	    unexec_error ("Can't update subspace record");
148 149 150 151 152 153 154 155 156 157
	}
    }

  /* Do for each initialization pointer record */
  /* (I don't think it applies to executable files, only relocatables) */
#undef update
}

/* Read in the header records from an a.out file.  */

Andreas Schwab's avatar
Andreas Schwab committed
158 159
static void
read_header (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr)
160 161 162
{

  /* Read the header in */
163
  check_lseek (file, 0, 0);
164
  if (read (file, hdr, sizeof (*hdr)) != sizeof (*hdr))
165
    unexec_error ("Couldn't read header from a.out file");
166 167 168 169

  if (hdr->a_magic != EXEC_MAGIC && hdr->a_magic != SHARE_MAGIC
      &&  hdr->a_magic != DEMAND_MAGIC)
    {
170
      fprintf (stderr, "a.out file doesn't have valid magic number\n");
171 172 173
      exit (1);
    }

174
  check_lseek (file, hdr->aux_header_location, 0);
175
  if (read (file, auxhdr, sizeof (*auxhdr)) != sizeof (*auxhdr))
176
    unexec_error ("Couldn't read auxiliary header from a.out file");
177 178 179 180
}

/* Write out the header records into an a.out file.  */

Andreas Schwab's avatar
Andreas Schwab committed
181 182
static void
write_header (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr)
183 184 185 186 187
{
  /* Update the checksum */
  hdr->checksum = calculate_checksum (hdr);

  /* Write the header back into the a.out file */
188
  check_lseek (file, 0, 0);
189
  if (write (file, hdr, sizeof (*hdr)) != sizeof (*hdr))
190 191
    unexec_error ("Couldn't write header to a.out file");
  check_lseek (file, hdr->aux_header_location, 0);
192
  if (write (file, auxhdr, sizeof (*auxhdr)) != sizeof (*auxhdr))
193
    unexec_error ("Couldn't write auxiliary header to a.out file");
194 195 196 197
}

/* Calculate the checksum of a SOM header record. */

Andreas Schwab's avatar
Andreas Schwab committed
198 199
static int
calculate_checksum (struct header *hdr)
200 201 202 203 204 205 206 207 208 209 210 211 212
{
  int checksum, i, *ptr;

  checksum = 0;  ptr = (int *) hdr;

  for (i = 0; i < sizeof (*hdr) / sizeof (int) - 1; i++)
    checksum ^= ptr[i];

  return (checksum);
}

/* Copy size bytes from the old file to the new one.  */

Andreas Schwab's avatar
Andreas Schwab committed
213 214
static void
copy_file (int old, int new, int size)
215 216 217 218 219 220 221 222
{
  int len;
  int buffer[8192];  /* word aligned will be faster */

  for (; size > 0; size -= len)
    {
      len = min (size, sizeof (buffer));
      if (read (old, buffer, len) != len)
223
	unexec_error ("Read failure on a.out file");
224
      if (write (new, buffer, len) != len)
225
	unexec_error ("Write failure in a.out file");
226 227 228 229 230
    }
}

/* Copy the rest of the file, up to EOF.  */

Andreas Schwab's avatar
Andreas Schwab committed
231 232
static void
copy_rest (int old, int new)
233 234 235 236 237 238 239 240 241
{
  int buffer[4096];
  int len;

  /* Copy bytes until end of file or error */
  while ((len = read (old, buffer, sizeof (buffer))) > 0)
    if (write (new, buffer, len) != len) break;

  if (len != 0)
242
    unexec_error ("Unable to copy the rest of the file");
243 244 245
}

#ifdef	DEBUG
Andreas Schwab's avatar
Andreas Schwab committed
246 247
static void
display_header (struct header *hdr, struct som_exec_auxhdr *auxhdr)
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
{
  /* Display the header information (debug) */
  printf ("\n\nFILE HEADER\n");
  printf ("magic number %d \n", hdr->a_magic);
  printf ("text loc %.8x   size %d \n", auxhdr->exec_tmem, auxhdr->exec_tsize);
  printf ("data loc %.8x   size %d \n", auxhdr->exec_dmem, auxhdr->exec_dsize);
  printf ("entry     %x \n",   auxhdr->exec_entry);
  printf ("Bss  segment size %u\n", auxhdr->exec_bsize);
  printf ("\n");
  printf ("data file loc %d    size %d\n",
	  auxhdr->exec_dfile, auxhdr->exec_dsize);
  printf ("som_length %d\n", hdr->som_length);
  printf ("unloadable sploc %d    size %d\n",
	  hdr->unloadable_sp_location, hdr->unloadable_sp_size);
}
#endif /* DEBUG */
Andreas Schwab's avatar
Andreas Schwab committed
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283


/* Create a new a.out file, same as old but with current data space */
void
unexec (const char *new_name,      /* name of the new a.out file to be created */
	const char *old_name)       /* name of the old a.out file */
{
  int old, new;
  int old_size, new_size;
  struct header hdr;
  struct som_exec_auxhdr auxhdr;
  long i;

  /* For the greatest flexibility, should create a temporary file in
     the same directory as the new file.  When everything is complete,
     rename the temp file to the new name.
     This way, a program could update its own a.out file even while
     it is still executing.  If problems occur, everything is still
     intact.  NOT implemented.  */

284
  /* Open the input and output a.out files.  */
285
  old = emacs_open (old_name, O_RDONLY, 0);
Andreas Schwab's avatar
Andreas Schwab committed
286
  if (old < 0)
287
    unexec_error (old_name);
288
  new = emacs_open (new_name, O_CREAT | O_RDWR | O_TRUNC, 0777);
Andreas Schwab's avatar
Andreas Schwab committed
289
  if (new < 0)
290
    unexec_error (new_name);
Andreas Schwab's avatar
Andreas Schwab committed
291

292
  /* Read the old headers.  */
Andreas Schwab's avatar
Andreas Schwab committed
293 294 295 296
  read_header (old, &hdr, &auxhdr);

  brk_on_dump = (long) sbrk (0);

297
  /* Decide how large the new and old data areas are.  */
Andreas Schwab's avatar
Andreas Schwab committed
298 299 300 301 302 303
  old_size = auxhdr.exec_dsize;
  /* I suspect these two statements are separate
     to avoid a compiler bug in hpux version 8.  */
  i = (long) sbrk (0);
  new_size = i - auxhdr.exec_dmem;

304 305
  /* Copy the old file to the new, up to the data space.  */
  check_lseek (old, 0, 0);
Andreas Schwab's avatar
Andreas Schwab committed
306 307
  copy_file (old, new, auxhdr.exec_dfile);

308 309
  /* Skip the old data segment and write a new one.  */
  check_lseek (old, old_size, 1);
Andreas Schwab's avatar
Andreas Schwab committed
310 311
  save_data_space (new, &hdr, &auxhdr, new_size);

312
  /* Copy the rest of the file.  */
Andreas Schwab's avatar
Andreas Schwab committed
313 314
  copy_rest (old, new);

315
  /* Update file pointers since we probably changed size of data area.  */
Andreas Schwab's avatar
Andreas Schwab committed
316 317
  update_file_ptrs (new, &hdr, &auxhdr, auxhdr.exec_dfile, new_size-old_size);

318
  /* Save the modified header.  */
Andreas Schwab's avatar
Andreas Schwab committed
319 320
  write_header (new, &hdr, &auxhdr);

321
  /* Close the binary file.  */
322 323
  emacs_close (old);
  emacs_close (new);
Andreas Schwab's avatar
Andreas Schwab committed
324
}