1/* Dump an executable image.
2   Copyright (C) 1985, 1986, 1987, 1988, 1999, 2001, 2002, 2003, 2004,
3                 2005, 2006, 2007  Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING.  If not, write to
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA.
21
22In other words, you are welcome to use, share and improve this program.
23You are forbidden to forbid anyone else to use, share and improve
24what you give them.   Help stamp out software-hoarding!  */
25
26
27/* Originally based on the COFF unexec.c by Spencer W. Thomas.
28 *
29 * Subsequently hacked on by
30 * Bill Mann <Bill_Man@praxisint.com>
31 * Andrew Vignaux <Andrew.Vignaux@comp.vuw.ac.nz>
32 * Mike Sperber <sperber@informatik.uni-tuebingen.de>
33 *
34 * Synopsis:
35 *	unexec (new_name, a_name, data_start, bss_start, entry_address)
36 *	char *new_name, *a_name;
37 *	unsigned data_start, bss_start, entry_address;
38 *
39 * Takes a snapshot of the program and makes an a.out format file in the
40 * file named by the string argument new_name.
41 * If a_name is non-NULL, the symbol table will be taken from the given file.
42 * On some machines, an existing a_name file is required.
43 *
44 * data_start and entry_address are ignored.
45 *
46 * bss_start indicates how much of the data segment is to be saved in the
47 * a.out file and restored when the program is executed.  It gives the lowest
48 * unsaved address, and is rounded up to a page boundary.  The default when 0
49 * is given assumes that the entire data segment is to be stored, including
50 * the previous data and bss as well as any additional storage allocated with
51 * sbrk(2).
52 *
53 */
54
55#ifndef emacs
56#define PERROR(arg) perror (arg); return -1
57#else
58#include <config.h>
59#define PERROR(file) report_error (file, new)
60#endif
61
62#include <a.out.h>
63/* Define getpagesize () if the system does not.
64   Note that this may depend on symbols defined in a.out.h
65 */
66#include "getpagesize.h"
67
68#include <sys/types.h>
69#include <stdio.h>
70#include <sys/stat.h>
71#include <errno.h>
72#include <unistd.h>
73#include <fcntl.h>
74
75extern char *start_of_text (void);		/* Start of text */
76extern char *start_of_data (void);		/* Start of initialized data */
77
78extern int _data;
79extern int _text;
80
81#include <filehdr.h>
82#include <aouthdr.h>
83#include <scnhdr.h>
84#include <syms.h>
85
86static struct filehdr f_hdr;		/* File header */
87static struct aouthdr f_ohdr;		/* Optional file header (a.out) */
88static long bias;			/* Bias to add for growth */
89static long lnnoptr;			/* Pointer to line-number info within file */
90
91static long text_scnptr;
92static long data_scnptr;
93#define ALIGN(val, pwr) (((val) + ((1L<<(pwr))-1)) & ~((1L<<(pwr))-1))
94static long load_scnptr;
95static long orig_load_scnptr;
96static long orig_data_scnptr;
97static int unrelocate_symbols (int, int, char *, char *);
98
99#ifndef MAX_SECTIONS
100#define MAX_SECTIONS	10
101#endif
102
103static int adjust_lnnoptrs (int, int, char *);
104
105static int pagemask;
106
107/* Correct an int which is the bit pattern of a pointer to a byte
108   into an int which is the number of a byte.
109   This is a no-op on ordinary machines, but not on all.  */
110
111#ifndef ADDR_CORRECT   /* Let m-*.h files override this definition */
112#define ADDR_CORRECT(x) ((char *)(x) - (char*)0)
113#endif
114
115#ifdef emacs
116#include "lisp.h"
117
118static void
119report_error (char *file, int fd)
120{
121  if (fd)
122    close (fd);
123  report_file_error ("Cannot unexec", Fcons (build_string (file), Qnil));
124}
125#endif /* emacs */
126
127#define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
128#define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
129#define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1
130
131static void
132report_error_1 (int fd, char *msg, int a1, int a2)
133{
134  close (fd);
135#ifdef emacs
136  error (msg, a1, a2);
137#else
138  fprintf (stderr, msg, a1, a2);
139  fprintf (stderr, "\n");
140#endif
141}
142
143static int make_hdr (int, int, unsigned, unsigned, unsigned, char *, char *);
144static void mark_x (char *);
145static int copy_text_and_data (int);
146static int copy_sym (int, int, char *, char *);
147static void write_segment (int, char *, char *);
148
149/* ****************************************************************
150 * unexec
151 *
152 * driving logic.
153 */
154int unexec (char *new_name, char *a_name,
155	    unsigned data_start,
156	    unsigned bss_start,
157	    unsigned entry_address)
158{
159  int new = -1, a_out = -1;
160
161  if (a_name && (a_out = open (a_name, O_RDONLY)) < 0)
162    {
163      PERROR (a_name);
164    }
165  if ((new = creat (new_name, 0666)) < 0)
166    {
167      PERROR (new_name);
168    }
169  if (make_hdr (new, a_out,
170		data_start, bss_start,
171		entry_address,
172		a_name, new_name) < 0
173      || copy_text_and_data (new) < 0
174      || copy_sym (new, a_out, a_name, new_name) < 0
175      || adjust_lnnoptrs (new, a_out, new_name) < 0
176      || unrelocate_symbols (new, a_out, a_name, new_name) < 0)
177    {
178      close (new);
179      return -1;
180    }
181
182  close (new);
183  if (a_out >= 0)
184    close (a_out);
185  mark_x (new_name);
186  return 0;
187}
188
189/* ****************************************************************
190 * make_hdr
191 *
192 * Make the header in the new a.out from the header in core.
193 * Modify the text and data sizes.
194 */
195static int
196make_hdr (int new, int a_out,
197	  unsigned data_start, unsigned bss_start,
198	  unsigned entry_address,
199	  char *a_name, char *new_name)
200{
201  int scns;
202  unsigned int bss_end;
203
204  struct scnhdr section[MAX_SECTIONS];
205  struct scnhdr * f_thdr;		/* Text section header */
206  struct scnhdr * f_dhdr;		/* Data section header */
207  struct scnhdr * f_bhdr;		/* Bss section header */
208  struct scnhdr * f_lhdr;		/* Loader section header */
209  struct scnhdr * f_tchdr;		/* Typechk section header */
210  struct scnhdr * f_dbhdr;		/* Debug section header */
211  struct scnhdr * f_xhdr;		/* Except section header */
212
213  load_scnptr = orig_load_scnptr = lnnoptr = 0;
214  pagemask = getpagesize () - 1;
215
216  /* Adjust text/data boundary. */
217  data_start = (long) start_of_data ();
218  data_start = ADDR_CORRECT (data_start);
219
220  data_start = data_start & ~pagemask; /* (Down) to page boundary. */
221
222  bss_end = ADDR_CORRECT (sbrk (0)) + pagemask;
223  bss_end &= ~ pagemask;
224  /* Adjust data/bss boundary. */
225  if (bss_start != 0)
226    {
227      bss_start = (ADDR_CORRECT (bss_start) + pagemask);
228      /* (Up) to page bdry. */
229      bss_start &= ~ pagemask;
230      if (bss_start > bss_end)
231	{
232	  ERROR1 ("unexec: Specified bss_start (%u) is past end of program",
233		  bss_start);
234	}
235    }
236  else
237    bss_start = bss_end;
238
239  if (data_start > bss_start)	/* Can't have negative data size. */
240    {
241      ERROR2 ("unexec: data_start (%u) can't be greater than bss_start (%u)",
242	      data_start, bss_start);
243    }
244
245  /* Salvage as much info from the existing file as possible */
246  f_thdr = NULL; f_dhdr = NULL; f_bhdr = NULL;
247  f_lhdr = NULL; f_tchdr = NULL; f_dbhdr = NULL; f_xhdr = NULL;
248  if (a_out >= 0)
249    {
250      if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
251	{
252	  PERROR (a_name);
253	}
254      if (f_hdr.f_opthdr > 0)
255	{
256	  if (read (a_out, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
257	    {
258	      PERROR (a_name);
259	    }
260	}
261      if (f_hdr.f_nscns > MAX_SECTIONS)
262	{
263	  ERROR0 ("unexec: too many section headers -- increase MAX_SECTIONS");
264	}
265      /* Loop through section headers */
266      for (scns = 0; scns < f_hdr.f_nscns; scns++) {
267	struct scnhdr *s = &section[scns];
268	if (read (a_out, s, sizeof (*s)) != sizeof (*s))
269	  {
270	    PERROR (a_name);
271	  }
272
273#define CHECK_SCNHDR(ptr, name, flags) \
274  if (strcmp(s->s_name, name) == 0) { \
275    if (s->s_flags != flags) { \
276      fprintf(stderr, "unexec: %lx flags where %x expected in %s section.\n", \
277	      (unsigned long)s->s_flags, flags, name); \
278    } \
279    if (ptr) { \
280      fprintf(stderr, "unexec: duplicate section header for section %s.\n", \
281	      name); \
282    } \
283    ptr = s; \
284  }
285	CHECK_SCNHDR(f_thdr, _TEXT, STYP_TEXT);
286	CHECK_SCNHDR(f_dhdr, _DATA, STYP_DATA);
287	CHECK_SCNHDR(f_bhdr, _BSS, STYP_BSS);
288	CHECK_SCNHDR(f_lhdr, _LOADER, STYP_LOADER);
289	CHECK_SCNHDR(f_dbhdr, _DEBUG,  STYP_DEBUG);
290	CHECK_SCNHDR(f_tchdr, _TYPCHK,  STYP_TYPCHK);
291	CHECK_SCNHDR(f_xhdr, _EXCEPT,  STYP_EXCEPT);
292      }
293
294      if (f_thdr == 0)
295	{
296	  ERROR1 ("unexec: couldn't find \"%s\" section", (int) _TEXT);
297	}
298      if (f_dhdr == 0)
299	{
300	  ERROR1 ("unexec: couldn't find \"%s\" section", (int) _DATA);
301	}
302      if (f_bhdr == 0)
303	{
304	  ERROR1 ("unexec: couldn't find \"%s\" section", (int) _BSS);
305	}
306    }
307  else
308    {
309      ERROR0 ("can't build a COFF file from scratch yet");
310    }
311  orig_data_scnptr = f_dhdr->s_scnptr;
312  orig_load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
313
314  /* Now we alter the contents of all the f_*hdr variables
315     to correspond to what we want to dump.  */
316
317  /* Indicate that the reloc information is no longer valid for ld (bind);
318     we only update it enough to fake out the exec-time loader.  */
319  f_hdr.f_flags |= (F_RELFLG | F_EXEC);
320
321  f_ohdr.dsize = bss_start - f_ohdr.data_start;
322  f_ohdr.bsize = bss_end - bss_start;
323
324  f_dhdr->s_size = f_ohdr.dsize;
325  f_bhdr->s_size = f_ohdr.bsize;
326  f_bhdr->s_paddr = f_ohdr.data_start + f_ohdr.dsize;
327  f_bhdr->s_vaddr = f_ohdr.data_start + f_ohdr.dsize;
328
329  /* fix scnptr's */
330  {
331    ulong ptr = section[0].s_scnptr;
332
333    bias = -1;
334    for (scns = 0; scns < f_hdr.f_nscns; scns++)
335      {
336	struct scnhdr *s = &section[scns];
337
338	if (s->s_flags & STYP_PAD)        /* .pad sections omitted in AIX 4.1 */
339	  {
340	    /*
341	     * the text_start should probably be o_algntext but that doesn't
342	     * seem to change
343	     */
344	    if (f_ohdr.text_start != 0) /* && scns != 0 */
345	      {
346		s->s_size = 512 - (ptr % 512);
347		if (s->s_size == 512)
348		  s->s_size = 0;
349	      }
350	    s->s_scnptr = ptr;
351	  }
352	else if (s->s_flags & STYP_DATA)
353	  s->s_scnptr = ptr;
354	else if (!(s->s_flags & (STYP_TEXT | STYP_BSS)))
355	  {
356	    if (bias == -1)                /* if first section after bss */
357	      bias = ptr - s->s_scnptr;
358
359	    s->s_scnptr += bias;
360	    ptr = s->s_scnptr;
361	  }
362
363	ptr = ptr + s->s_size;
364      }
365  }
366
367  /* fix other pointers */
368  for (scns = 0; scns < f_hdr.f_nscns; scns++)
369    {
370      struct scnhdr *s = &section[scns];
371
372      if (s->s_relptr != 0)
373	{
374	  s->s_relptr += bias;
375	}
376      if (s->s_lnnoptr != 0)
377	{
378	  if (lnnoptr == 0) lnnoptr = s->s_lnnoptr;
379	  s->s_lnnoptr += bias;
380	}
381    }
382
383  if (f_hdr.f_symptr > 0L)
384    {
385      f_hdr.f_symptr += bias;
386    }
387
388  text_scnptr = f_thdr->s_scnptr;
389  data_scnptr = f_dhdr->s_scnptr;
390  load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
391
392  if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
393    {
394      PERROR (new_name);
395    }
396
397  if (f_hdr.f_opthdr > 0)
398    {
399      if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
400	{
401	  PERROR (new_name);
402	}
403    }
404
405  for (scns = 0; scns < f_hdr.f_nscns; scns++) {
406    struct scnhdr *s = &section[scns];
407    if (write (new, s, sizeof (*s)) != sizeof (*s))
408      {
409	PERROR (new_name);
410      }
411  }
412
413  return (0);
414}
415
416/* ****************************************************************
417
418 *
419 * Copy the text and data segments from memory to the new a.out
420 */
421static int
422copy_text_and_data (int new)
423{
424  char *end;
425  char *ptr;
426
427  lseek (new, (long) text_scnptr, SEEK_SET);
428  ptr = start_of_text () + text_scnptr;
429  end = ptr + f_ohdr.tsize;
430  write_segment (new, ptr, end);
431
432  lseek (new, (long) data_scnptr, SEEK_SET);
433  ptr = (char *) f_ohdr.data_start;
434  end = ptr + f_ohdr.dsize;
435  write_segment (new, ptr, end);
436
437  return 0;
438}
439
440#define UnexBlockSz (1<<12)			/* read/write block size */
441static void
442write_segment (int new, char *ptr, char *end)
443{
444  int i, nwrite, ret;
445  char buf[80];
446  extern int errno;
447  char zeros[UnexBlockSz];
448
449  for (i = 0; ptr < end;)
450    {
451      /* distance to next block.  */
452      nwrite = (((int) ptr + UnexBlockSz) & -UnexBlockSz) - (int) ptr;
453      /* But not beyond specified end.  */
454      if (nwrite > end - ptr) nwrite = end - ptr;
455      ret = write (new, ptr, nwrite);
456      /* If write gets a page fault, it means we reached
457	 a gap between the old text segment and the old data segment.
458	 This gap has probably been remapped into part of the text segment.
459	 So write zeros for it.  */
460      if (ret == -1 && errno == EFAULT)
461	{
462	  memset (zeros, 0, nwrite);
463	  write (new, zeros, nwrite);
464	}
465      else if (nwrite != ret)
466	{
467	  sprintf (buf,
468		   "unexec write failure: addr 0x%lx, fileno %d, size 0x%x, wrote 0x%x, errno %d",
469		   (unsigned long)ptr, new, nwrite, ret, errno);
470	  PERROR (buf);
471	}
472      i += nwrite;
473      ptr += nwrite;
474    }
475}
476
477/* ****************************************************************
478 * copy_sym
479 *
480 * Copy the relocation information and symbol table from the a.out to the new
481 */
482static int
483copy_sym (int new, int a_out, char *a_name, char *new_name)
484{
485  char page[UnexBlockSz];
486  int n;
487
488  if (a_out < 0)
489    return 0;
490
491  if (orig_load_scnptr == 0L)
492    return 0;
493
494  if (lnnoptr && lnnoptr < orig_load_scnptr) /* if there is line number info  */
495    lseek (a_out, lnnoptr, SEEK_SET);  /* start copying from there */
496  else
497    lseek (a_out, orig_load_scnptr, SEEK_SET); /* Position a.out to symtab. */
498
499  while ((n = read (a_out, page, sizeof page)) > 0)
500    {
501      if (write (new, page, n) != n)
502	{
503	  PERROR (new_name);
504	}
505    }
506  if (n < 0)
507    {
508      PERROR (a_name);
509    }
510  return 0;
511}
512
513/* ****************************************************************
514 * mark_x
515 *
516 * After successfully building the new a.out, mark it executable
517 */
518static void
519mark_x (char *name)
520{
521  struct stat sbuf;
522  int um;
523  int new = 0;  /* for PERROR */
524
525  um = umask (777);
526  umask (um);
527  if (stat (name, &sbuf) == -1)
528    {
529      PERROR (name);
530    }
531  sbuf.st_mode |= 0111 & ~um;
532  if (chmod (name, sbuf.st_mode) == -1)
533    PERROR (name);
534}
535
536static int
537adjust_lnnoptrs (int writedesc, int readdesc, char *new_name)
538{
539  int nsyms;
540  int naux;
541  int new;
542  struct syment symentry;
543  union auxent auxentry;
544
545  if (!lnnoptr || !f_hdr.f_symptr)
546    return 0;
547
548  if ((new = open (new_name, O_RDWR)) < 0)
549    {
550      PERROR (new_name);
551      return -1;
552    }
553
554  lseek (new, f_hdr.f_symptr, SEEK_SET);
555  for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
556    {
557      read (new, &symentry, SYMESZ);
558      if (symentry.n_sclass == C_BINCL || symentry.n_sclass == C_EINCL)
559	{
560	  symentry.n_value += bias;
561	  lseek (new, -SYMESZ, SEEK_CUR);
562	  write (new, &symentry, SYMESZ);
563	}
564
565      for (naux = symentry.n_numaux; naux-- != 0; )
566	{
567	  read (new, &auxentry, AUXESZ);
568	  nsyms++;
569	  if (naux != 0              /* skip csect auxentry (last entry) */
570              && (symentry.n_sclass == C_EXT || symentry.n_sclass == C_HIDEXT))
571            {
572              auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
573              lseek (new, -AUXESZ, SEEK_CUR);
574              write (new, &auxentry, AUXESZ);
575            }
576	}
577    }
578  close (new);
579
580  return 0;
581}
582
583static int
584unrelocate_symbols (int new, int a_out, char *a_name, char *new_name)
585{
586  int i;
587  LDHDR ldhdr;
588  LDREL ldrel;
589  ulong t_reloc = (ulong) &_text - f_ohdr.text_start;
590#ifndef ALIGN_DATA_RELOC
591  ulong d_reloc = (ulong) &_data - f_ohdr.data_start;
592#else
593  /* This worked (and was needed) before AIX 4.2.
594     I have no idea why. -- Mike */
595  ulong d_reloc = (ulong) &_data - ALIGN(f_ohdr.data_start, 2);
596#endif
597  int * p;
598
599  if (load_scnptr == 0)
600    return 0;
601
602  lseek (a_out, orig_load_scnptr, SEEK_SET);
603  if (read (a_out, &ldhdr, sizeof (ldhdr)) != sizeof (ldhdr))
604    {
605      PERROR (new_name);
606    }
607
608#define SYMNDX_TEXT	0
609#define SYMNDX_DATA	1
610#define SYMNDX_BSS	2
611
612  for (i = 0; i < ldhdr.l_nreloc; i++)
613    {
614      lseek (a_out,
615	     orig_load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
616	     SEEK_SET);
617
618      if (read (a_out, &ldrel, LDRELSZ) != LDRELSZ)
619	{
620	  PERROR (a_name);
621	}
622
623      /* move the BSS loader symbols to the DATA segment */
624      if (ldrel.l_symndx == SYMNDX_BSS)
625	{
626	  ldrel.l_symndx = SYMNDX_DATA;
627
628	  lseek (new,
629		 load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
630		 SEEK_SET);
631
632	  if (write (new, &ldrel, LDRELSZ) != LDRELSZ)
633	    {
634	      PERROR (new_name);
635	    }
636	}
637
638      if (ldrel.l_rsecnm == f_ohdr.o_sndata)
639	{
640	  int orig_int;
641
642	  lseek (a_out,
643                 orig_data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
644		 SEEK_SET);
645
646	  if (read (a_out, (void *) &orig_int, sizeof (orig_int))
647	      != sizeof (orig_int))
648	    {
649	      PERROR (a_name);
650	    }
651
652          p = (int *) (ldrel.l_vaddr + d_reloc);
653
654	  switch (ldrel.l_symndx) {
655	  case SYMNDX_TEXT:
656	    orig_int = * p - t_reloc;
657	    break;
658
659	  case SYMNDX_DATA:
660	  case SYMNDX_BSS:
661	    orig_int = * p - d_reloc;
662	    break;
663	  }
664
665          if (orig_int != * p)
666            {
667              lseek (new,
668                     data_scnptr + (ldrel.l_vaddr - f_ohdr.data_start),
669		     SEEK_SET);
670              if (write (new, (void *) &orig_int, sizeof (orig_int))
671                  != sizeof (orig_int))
672                {
673                  PERROR (new_name);
674                }
675            }
676	}
677    }
678  return 0;
679}
680
681/* arch-tag: 0783857a-7c2d-456f-a426-58b722d69fd0
682   (do not change this comment) */
683