unexcoff.c 14.8 KB
Newer Older
Paul Eggert's avatar
Paul Eggert committed
1
/* Copyright (C) 1985-1988, 1992-1994, 2001-2019 Free Software
2
 * Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
3 4 5

This file is part of GNU Emacs.

6
GNU Emacs is free software: you can redistribute it and/or modify
Richard M. Stallman's avatar
Richard M. Stallman committed
7
it under the terms of the GNU General Public License as published by
8 9
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
10 11 12 13 14 15 16

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
17
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
18 19 20


/*
21 22 23 24 25 26 27
 * unexcoff.c - Convert a running program into an a.out or COFF file.
 *
 * ==================================================================
 * Note: This file is currently used only by the MSDOS (a.k.a. DJGPP)
 * build of Emacs.  If you are not interested in the MSDOS build, you
 * are looking at the wrong version of unexec!
 * ==================================================================
Richard M. Stallman's avatar
Richard M. Stallman committed
28 29 30 31 32
 *
 * Author:	Spencer W. Thomas
 * 		Computer Science Dept.
 * 		University of Utah
 * Date:	Tue Mar  2 1982
33
 * Originally under the name unexec.c.
Richard M. Stallman's avatar
Richard M. Stallman committed
34 35 36
 * Modified heavily since then.
 *
 * Synopsis:
37
 *	unexec (const char *new_name, const char *old_name);
Richard M. Stallman's avatar
Richard M. Stallman committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
 *
 * Takes a snapshot of the program and makes an a.out format file in the
 * file named by the string argument new_name.
 * If a_name is non-NULL, the symbol table will be taken from the given file.
 * On some machines, an existing a_name file is required.
 *
 * If you make improvements I'd like to get them too.
 * harpo!utah-cs!thomas, thomas@Utah-20
 *
 */

/* Modified to support SysVr3 shared libraries by James Van Artsdalen
 * of Dell Computer Corporation.  james@bigtex.cactus.org.
 */

53
#include <config.h>
54
#include "unexec.h"
55
#include "lisp.h"
56

Richard M. Stallman's avatar
Richard M. Stallman committed
57 58
#define PERROR(file) report_error (file, new)

59
#ifdef HAVE_UNEXEC  /* all rest of file!  */
Richard M. Stallman's avatar
Richard M. Stallman committed
60

61
#ifdef HAVE_COFF_H
62
#include <coff.h>
63
#ifdef MSDOS
64
#include <fcntl.h>  /* for O_RDONLY, O_RDWR */
65
#include <crt0.h>   /* for _crt0_startup_flags and its bits */
Eli Zaretskii's avatar
Eli Zaretskii committed
66
#include <sys/exceptn.h>
67
static int save_djgpp_startup_flags;
Eli Zaretskii's avatar
Eli Zaretskii committed
68 69
#include <libc/atexit.h>
static struct __atexit *save_atexit_ptr;
70 71 72 73 74 75 76 77
#define filehdr external_filehdr
#define scnhdr external_scnhdr
#define syment external_syment
#define auxent external_auxent
#define n_numaux e_numaux
#define n_type e_type
struct aouthdr
{
78 79 80 81 82 83 84 85
  unsigned short	magic;	/* type of file				*/
  unsigned short	vstamp;	/* version stamp			*/
  unsigned long		tsize;	/* text size in bytes, padded to FW bdry*/
  unsigned long		dsize;	/* initialized data "  "		*/
  unsigned long		bsize;	/* uninitialized data "   "		*/
  unsigned long		entry;	/* entry pt.				*/
  unsigned long	 	text_start;/* base of text used for this file */
  unsigned long	 	data_start;/* base of data used for this file */
86
};
Eli Zaretskii's avatar
Eli Zaretskii committed
87
#endif /* MSDOS */
88
#else  /* not HAVE_COFF_H */
89
#include <a.out.h>
90
#endif /* not HAVE_COFF_H */
Jim Blandy's avatar
Jim Blandy committed
91

Richard M. Stallman's avatar
Richard M. Stallman committed
92 93
/* Define getpagesize if the system does not.
   Note that this may depend on symbols defined in a.out.h.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
94 95 96 97
#include "getpagesize.h"

#ifndef makedev			/* Try to detect types.h already loaded */
#include <sys/types.h>
Jim Blandy's avatar
Jim Blandy committed
98
#endif /* makedev */
Richard M. Stallman's avatar
Richard M. Stallman committed
99 100 101
#include <stdio.h>
#include <errno.h>

102
#include <sys/file.h>
103

104
extern int etext;
Richard M. Stallman's avatar
Richard M. Stallman committed
105 106 107 108 109 110 111 112 113 114 115

static long block_copy_start;		/* Old executable start point */
static struct filehdr f_hdr;		/* File header */
static struct aouthdr f_ohdr;		/* Optional file header (a.out) */
long bias;			/* Bias to add for growth */
long lnnoptr;			/* Pointer to line-number info within file */
#define SYMS_START block_copy_start

static long text_scnptr;
static long data_scnptr;

116 117
static long coff_offset;

Richard M. Stallman's avatar
Richard M. Stallman committed
118 119 120 121 122 123
static int pagemask;

/* Correct an int which is the bit pattern of a pointer to a byte
   into an int which is the number of a byte.
   This is a no-op on ordinary machines, but not on all.  */

124
#define ADDR_CORRECT(x) ((char *) (x) - (char *) 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
125

126 127
#include "lisp.h"

Eli Zaretskii's avatar
Eli Zaretskii committed
128 129
static void
report_error (const char *file, int fd)
Richard M. Stallman's avatar
Richard M. Stallman committed
130
{
131
  int err = errno;
Richard M. Stallman's avatar
Richard M. Stallman committed
132
  if (fd)
133
    emacs_close (fd);
134
  report_file_errno ("Cannot unexec", build_string (file), err);
Richard M. Stallman's avatar
Richard M. Stallman committed
135 136 137 138 139 140
}

#define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
#define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
#define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1

Eli Zaretskii's avatar
Eli Zaretskii committed
141 142
static void
report_error_1 (int fd, const char *msg, int a1, int a2)
Richard M. Stallman's avatar
Richard M. Stallman committed
143
{
144
  emacs_close (fd);
Richard M. Stallman's avatar
Richard M. Stallman committed
145 146 147
  error (msg, a1, a2);
}

148
static int make_hdr (int, int, const char *, const char *);
Eli Zaretskii's avatar
Eli Zaretskii committed
149 150 151
static int copy_text_and_data (int, int);
static int copy_sym (int, int, const char *, const char *);
static void mark_x (const char *);
Richard M. Stallman's avatar
Richard M. Stallman committed
152 153 154 155 156 157 158 159

/* ****************************************************************
 * make_hdr
 *
 * Make the header in the new a.out from the header in core.
 * Modify the text and data sizes.
 */
static int
160 161
make_hdr (int new, int a_out,
	  const char *a_name, const char *new_name)
Richard M. Stallman's avatar
Richard M. Stallman committed
162 163 164 165 166 167
{
  auto struct scnhdr f_thdr;		/* Text section header */
  auto struct scnhdr f_dhdr;		/* Data section header */
  auto struct scnhdr f_bhdr;		/* Bss section header */
  auto struct scnhdr scntemp;		/* Temporary section header */
  register int scns;
168 169
  unsigned int bss_start;
  unsigned int data_start;
Richard M. Stallman's avatar
Richard M. Stallman committed
170 171 172 173

  pagemask = getpagesize () - 1;

  /* Adjust text/data boundary. */
174
  data_start = (int) DATA_START;
Richard M. Stallman's avatar
Richard M. Stallman committed
175 176 177
  data_start = ADDR_CORRECT (data_start);
  data_start = data_start & ~pagemask; /* (Down) to page boundary. */

178 179
  bss_start = ADDR_CORRECT (sbrk (0)) + pagemask;
  bss_start &= ~ pagemask;
Richard M. Stallman's avatar
Richard M. Stallman committed
180 181 182 183 184 185 186

  if (data_start > bss_start)	/* Can't have negative data size. */
    {
      ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
	      data_start, bss_start);
    }

187 188
  coff_offset = 0L;		/* stays zero, except in DJGPP */

Richard M. Stallman's avatar
Richard M. Stallman committed
189 190 191
  /* Salvage as much info from the existing file as possible */
  if (a_out >= 0)
    {
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
#ifdef MSDOS
      /* Support the coff-go32-exe format with a prepended stub, since
	 this is what GCC 2.8.0 and later generates by default in DJGPP.  */
      unsigned short mz_header[3];

      if (read (a_out, &mz_header, sizeof (mz_header)) != sizeof (mz_header))
	{
	  PERROR (a_name);
	}
      if (mz_header[0] == 0x5a4d || mz_header[0] == 0x4d5a) /* "MZ" or "ZM" */
	{
	  coff_offset = (long)mz_header[2] * 512L;
	  if (mz_header[1])
	    coff_offset += (long)mz_header[1] - 512L;
	  lseek (a_out, coff_offset, 0);
	}
      else
	lseek (a_out, 0L, 0);
#endif /* MSDOS */
Richard M. Stallman's avatar
Richard M. Stallman committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224
      if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
	{
	  PERROR (a_name);
	}
      block_copy_start += sizeof (f_hdr);
      if (f_hdr.f_opthdr > 0)
	{
	  if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
	    {
	      PERROR (a_name);
	    }
	  block_copy_start += sizeof (f_ohdr);
	}
      /* Loop through section headers, copying them in */
225
      lseek (a_out, coff_offset + sizeof (f_hdr) + f_hdr.f_opthdr, 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
      for (scns = f_hdr.f_nscns; scns > 0; scns--) {
	if (read (a_out, &scntemp, sizeof (scntemp)) != sizeof (scntemp))
	  {
	    PERROR (a_name);
	  }
	if (scntemp.s_scnptr > 0L)
	  {
            if (block_copy_start < scntemp.s_scnptr + scntemp.s_size)
	      block_copy_start = scntemp.s_scnptr + scntemp.s_size;
	  }
	if (strcmp (scntemp.s_name, ".text") == 0)
	  {
	    f_thdr = scntemp;
	  }
	else if (strcmp (scntemp.s_name, ".data") == 0)
	  {
	    f_dhdr = scntemp;
	  }
	else if (strcmp (scntemp.s_name, ".bss") == 0)
	  {
	    f_bhdr = scntemp;
	  }
      }
    }
  else
    {
      ERROR0 ("can't build a COFF file from scratch yet");
    }

  /* Now we alter the contents of all the f_*hdr variables
     to correspond to what we want to dump.  */

  f_hdr.f_flags |= (F_RELFLG | F_EXEC);
  f_ohdr.dsize = bss_start - f_ohdr.data_start;
260
  f_ohdr.bsize = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
  f_thdr.s_size = f_ohdr.tsize;
  f_thdr.s_scnptr = sizeof (f_hdr) + sizeof (f_ohdr);
  f_thdr.s_scnptr += (f_hdr.f_nscns) * (sizeof (f_thdr));
  lnnoptr = f_thdr.s_lnnoptr;
  text_scnptr = f_thdr.s_scnptr;
  f_dhdr.s_paddr = f_ohdr.data_start;
  f_dhdr.s_vaddr = f_ohdr.data_start;
  f_dhdr.s_size = f_ohdr.dsize;
  f_dhdr.s_scnptr = f_thdr.s_scnptr + f_thdr.s_size;
  data_scnptr = f_dhdr.s_scnptr;
  f_bhdr.s_paddr = f_ohdr.data_start + f_ohdr.dsize;
  f_bhdr.s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
  f_bhdr.s_size = f_ohdr.bsize;
  f_bhdr.s_scnptr = 0L;
  bias = f_dhdr.s_scnptr + f_dhdr.s_size - block_copy_start;

  if (f_hdr.f_symptr > 0L)
    {
      f_hdr.f_symptr += bias;
    }

  if (f_thdr.s_lnnoptr > 0L)
    {
      f_thdr.s_lnnoptr += bias;
    }

  if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
    {
      PERROR (new_name);
    }

  if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
    {
      PERROR (new_name);
    }

  if (write (new, &f_thdr, sizeof (f_thdr)) != sizeof (f_thdr))
    {
      PERROR (new_name);
    }

  if (write (new, &f_dhdr, sizeof (f_dhdr)) != sizeof (f_dhdr))
    {
      PERROR (new_name);
    }

  if (write (new, &f_bhdr, sizeof (f_bhdr)) != sizeof (f_bhdr))
    {
      PERROR (new_name);
    }

  return (0);

}

Eli Zaretskii's avatar
Eli Zaretskii committed
316 317
void
write_segment (int new, const char *ptr, const char *end)
318 319 320 321 322 323 324 325
{
  register int i, nwrite, ret;
  /* This is the normal amount to write at once.
     It is the size of block that NFS uses.  */
  int writesize = 1 << 13;
  int pagesize = getpagesize ();
  char zeros[1 << 13];

326
  memset (zeros, 0, sizeof (zeros));
327 328 329 330 331 332 333 334 335 336 337 338

  for (i = 0; ptr < end;)
    {
      /* Distance to next multiple of writesize.  */
      nwrite = (((int) ptr + writesize) & -writesize) - (int) ptr;
      /* But not beyond specified end.  */
      if (nwrite > end - ptr) nwrite = end - ptr;
      ret = write (new, ptr, nwrite);
      /* If write gets a page fault, it means we reached
	 a gap between the old text segment and the old data segment.
	 This gap has probably been remapped into part of the text segment.
	 So write zeros for it.  */
339
      if (ret == -1 && errno == EFAULT)
340 341
	{
	  /* Write only a page of zeros at once,
Juanma Barranquero's avatar
Juanma Barranquero committed
342
	     so that we don't overshoot the start
343 344 345 346 347 348 349 350 351
	     of the valid memory in the old data segment.  */
	  if (nwrite > pagesize)
	    nwrite = pagesize;
	  write (new, zeros, nwrite);
	}
      i += nwrite;
      ptr += nwrite;
    }
}
Richard M. Stallman's avatar
Richard M. Stallman committed
352 353 354 355 356 357
/* ****************************************************************
 * copy_text_and_data
 *
 * Copy the text and data segments from memory to the new a.out
 */
static int
Eli Zaretskii's avatar
Eli Zaretskii committed
358
copy_text_and_data (int new, int a_out)
Richard M. Stallman's avatar
Richard M. Stallman committed
359 360 361 362
{
  register char *end;
  register char *ptr;

363 364 365 366
#ifdef MSDOS
  /* Dump the original table of exception handlers, not the one
     where our exception hooks are registered.  */
  __djgpp_exception_toggle ();
367 368 369 370 371

  /* Switch off startup flags that might have been set at runtime
     and which might change the way that dumped Emacs works.  */
  save_djgpp_startup_flags = _crt0_startup_flags;
  _crt0_startup_flags &= ~(_CRT0_FLAG_NO_LFN | _CRT0_FLAG_NEARPTR);
Eli Zaretskii's avatar
Eli Zaretskii committed
372 373 374 375 376 377

  /* Zero out the 'atexit' chain in the dumped executable, to avoid
     calling the atexit functions twice.  (emacs.c:main installs an
     atexit function.)  */
  save_atexit_ptr = __atexit_ptr;
  __atexit_ptr = NULL;
378 379
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
380 381 382 383 384 385 386 387 388 389
  lseek (new, (long) text_scnptr, 0);
  ptr = (char *) f_ohdr.text_start;
  end = ptr + f_ohdr.tsize;
  write_segment (new, ptr, end);

  lseek (new, (long) data_scnptr, 0);
  ptr = (char *) f_ohdr.data_start;
  end = ptr + f_ohdr.dsize;
  write_segment (new, ptr, end);

390 391 392
#ifdef MSDOS
  /* Restore our exception hooks.  */
  __djgpp_exception_toggle ();
393 394 395

  /* Restore the startup flags.  */
  _crt0_startup_flags = save_djgpp_startup_flags;
Eli Zaretskii's avatar
Eli Zaretskii committed
396 397 398

  /* Restore the atexit chain.  */
  __atexit_ptr = save_atexit_ptr;
399 400
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
401 402 403 404 405 406 407 408 409 410

  return 0;
}

/* ****************************************************************
 * copy_sym
 *
 * Copy the relocation information and symbol table from the a.out to the new
 */
static int
Eli Zaretskii's avatar
Eli Zaretskii committed
411
copy_sym (int new, int a_out, const char *a_name, const char *new_name)
Richard M. Stallman's avatar
Richard M. Stallman committed
412 413 414 415 416 417 418 419 420 421 422
{
  char page[1024];
  int n;

  if (a_out < 0)
    return 0;

  if (SYMS_START == 0L)
    return 0;

  if (lnnoptr)			/* if there is line number info */
423
    lseek (a_out, coff_offset + lnnoptr, 0);	/* start copying from there */
Richard M. Stallman's avatar
Richard M. Stallman committed
424
  else
425
    lseek (a_out, coff_offset + SYMS_START, 0);	/* Position a.out to symtab. */
Richard M. Stallman's avatar
Richard M. Stallman committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

  while ((n = read (a_out, page, sizeof page)) > 0)
    {
      if (write (new, page, n) != n)
	{
	  PERROR (new_name);
	}
    }
  if (n < 0)
    {
      PERROR (a_name);
    }
  return 0;
}


/*
 *	If the COFF file contains a symbol table and a line number section,
 *	then any auxiliary entries that have values for x_lnnoptr must
 *	be adjusted by the amount that the line number section has moved
 *	in the file (bias computed in make_hdr).  The #@$%&* designers of
 *	the auxiliary entry structures used the absolute file offsets for
 *	the line number entry rather than an offset from the start of the
 *	line number section!
 *
 *	When I figure out how to scan through the symbol table and pick out
 *	the auxiliary entries that need adjustment, this routine will
 *	be fixed.  As it is now, all such entries are wrong and sdb
 *	will complain.   Fred Fish, UniSoft Systems Inc.
 */

/* This function is probably very slow.  Instead of reopening the new
   file for input and output it should copy from the old to the new
   using the two descriptors already open (WRITEDESC and READDESC).
   Instead of reading one small structure at a time it should use
   a reasonable size buffer.  But I don't have time to work on such
   things, so I am installing it as submitted to me.  -- RMS.  */

Eli Zaretskii's avatar
Eli Zaretskii committed
464 465
int
adjust_lnnoptrs (int writedesc, int readdesc, const char *new_name)
Richard M. Stallman's avatar
Richard M. Stallman committed
466 467 468 469 470 471 472 473 474
{
  register int nsyms;
  register int new;
  struct syment symentry;
  union auxent auxentry;

  if (!lnnoptr || !f_hdr.f_symptr)
    return 0;

475 476 477
#ifdef MSDOS
  if ((new = writedesc) < 0)
#else
478
  if ((new = emacs_open (new_name, O_RDWR, 0)) < 0)
479
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
480 481 482 483 484 485 486 487 488 489 490 491 492
    {
      PERROR (new_name);
      return -1;
    }

  lseek (new, f_hdr.f_symptr, 0);
  for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
    {
      read (new, &symentry, SYMESZ);
      if (symentry.n_numaux)
	{
	  read (new, &auxentry, AUXESZ);
	  nsyms++;
493 494 495 496 497 498
	  if (ISFCN (symentry.n_type) || symentry.n_type == 0x2400)
	    {
	      auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
	      lseek (new, -AUXESZ, 1);
	      write (new, &auxentry, AUXESZ);
	    }
Richard M. Stallman's avatar
Richard M. Stallman committed
499 500
	}
    }
501
#ifndef MSDOS
502
  emacs_close (new);
503 504
#endif
  return 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
505 506
}

507 508 509 510 511
/* ****************************************************************
 * unexec
 *
 * driving logic.
 */
512
void
513
unexec (const char *new_name, const char *a_name)
514
{
Eli Zaretskii's avatar
Eli Zaretskii committed
515
  int new = -1, a_out = -1;
516

517
  if (a_name && (a_out = emacs_open (a_name, O_RDONLY, 0)) < 0)
518 519 520
    {
      PERROR (a_name);
    }
521
  if ((new = emacs_open (new_name, O_WRONLY | O_CREAT | O_TRUNC, 0777)) < 0)
522 523 524 525
    {
      PERROR (new_name);
    }

526
  if (make_hdr (new, a_out, a_name, new_name) < 0
527 528 529 530 531
      || copy_text_and_data (new, a_out) < 0
      || copy_sym (new, a_out, a_name, new_name) < 0
      || adjust_lnnoptrs (new, a_out, new_name) < 0
      )
    {
532
      emacs_close (new);
533
      return;
534 535
    }

536
  emacs_close (new);
537
  if (a_out >= 0)
538
    emacs_close (a_out);
539 540
}

541
#endif /* HAVE_UNEXEC */