1/* Unexec for DEC alpha.  schoepf@sc.ZIB-Berlin.DE (Rainer Schoepf).
2
3   Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004,
4                 2005, 2006, 2007  Free Software Foundation, Inc.
5
6This file is part of GNU Emacs.
7
8GNU Emacs is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Emacs; see the file COPYING.  If not, write to
20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21Boston, MA 02110-1301, USA.  */
22
23
24#include <config.h>
25#include <sys/types.h>
26#include <sys/file.h>
27#include <sys/stat.h>
28#include <sys/mman.h>
29#include <stdio.h>
30#include <errno.h>
31#ifdef HAVE_STRING_H
32#include <string.h>
33#endif
34#if !defined (__NetBSD__) && !defined (__OpenBSD__)
35#include <filehdr.h>
36#include <aouthdr.h>
37#include <scnhdr.h>
38#include <syms.h>
39#ifndef __linux__
40# include <reloc.h>
41# include <elf_abi.h>
42#endif
43#else /* __NetBSD__ or __OpenBSD__ */
44/*
45 * NetBSD/Alpha does not have 'normal' user-land ECOFF support because
46 * there's no desire to support ECOFF as the executable format in the
47 * long term.
48 */
49#include <sys/exec_ecoff.h>
50
51/* Structures, constants, etc., that NetBSD defines strangely. */
52#define	filehdr		ecoff_filehdr
53#define	aouthdr		ecoff_aouthdr
54#define	scnhdr		ecoff_scnhdr
55#define	HDRR		struct ecoff_symhdr
56#define	pHDRR		HDRR *
57#define	cbHDRR		sizeof(HDRR)
58#ifdef __OpenBSD__
59#define	ALPHAMAGIC	ECOFF_MAGIC_NATIVE_ALPHA
60#else
61#define	ALPHAMAGIC	ECOFF_MAGIC_NETBSD_ALPHA
62#endif
63#define	ZMAGIC		ECOFF_ZMAGIC
64
65/* Misc. constants that NetBSD doesn't define at all. */
66#define	ALPHAUMAGIC	0617
67#define	_MIPS_NSCNS_MAX	35
68#define	STYP_TEXT	0x00000020
69#define	STYP_DATA	0x00000040
70#define	STYP_BSS	0x00000080
71#define	STYP_RDATA	0x00000100
72#define	STYP_SDATA	0x00000200
73#define	STYP_SBSS	0x00000400
74#define	STYP_INIT	0x80000000
75#define	_TEXT		".text"
76#define	_DATA		".data"
77#define	_BSS		".bss"
78#define	_INIT		".init"
79#define	_RDATA		".rdata"
80#define	_SDATA		".sdata"
81#define	_SBSS		".sbss"
82#endif /* __NetBSD__ || __OpenBSD__ */
83
84static void fatal_unexec __P ((char *, char *));
85static void mark_x __P ((char *));
86
87static void update_dynamic_symbols __P ((char *, char *, int, struct aouthdr));
88
89#define READ(_fd, _buffer, _size, _error_message, _error_arg) \
90	errno = EEOF; \
91	if (read (_fd, _buffer, _size) != _size) \
92	  fatal_unexec (_error_message, _error_arg);
93
94#define WRITE(_fd, _buffer, _size, _error_message, _error_arg) \
95	if (write (_fd, _buffer, _size) != _size) \
96	  fatal_unexec (_error_message, _error_arg);
97
98#define SEEK(_fd, _position, _error_message, _error_arg) \
99	errno = EEOF; \
100	if (lseek (_fd, _position, L_SET) != _position) \
101	  fatal_unexec (_error_message, _error_arg);
102
103#ifdef HAVE_UNISTD_H
104#include <unistd.h>
105#else
106void *sbrk ();
107#endif
108
109#define EEOF -1
110
111static struct scnhdr *text_section;
112static struct scnhdr *rel_dyn_section;
113static struct scnhdr *dynstr_section;
114static struct scnhdr *dynsym_section;
115static struct scnhdr *init_section;
116static struct scnhdr *finit_section;
117static struct scnhdr *rdata_section;
118static struct scnhdr *rconst_section;
119static struct scnhdr *data_section;
120static struct scnhdr *pdata_section;
121static struct scnhdr *xdata_section;
122static struct scnhdr *got_section;
123static struct scnhdr *lit8_section;
124static struct scnhdr *lit4_section;
125static struct scnhdr *sdata_section;
126static struct scnhdr *sbss_section;
127static struct scnhdr *bss_section;
128
129static struct scnhdr old_data_scnhdr;
130
131static unsigned long Brk;
132
133struct headers {
134    struct filehdr fhdr;
135    struct aouthdr aout;
136    struct scnhdr section[_MIPS_NSCNS_MAX];
137};
138
139
140
141/* Define name of label for entry point for the dumped executable.  */
142
143#ifndef DEFAULT_ENTRY_ADDRESS
144#define DEFAULT_ENTRY_ADDRESS __start
145#endif
146
147void
148unexec (new_name, a_name, data_start, bss_start, entry_address)
149     char *new_name, *a_name;
150     unsigned long data_start, bss_start, entry_address;
151{
152  int new, old;
153  char * oldptr;
154  struct headers ohdr, nhdr;
155  struct stat stat;
156  long pagesize, brk;
157  long newsyms, symrel;
158  int nread;
159  int i;
160  long vaddr, scnptr;
161#define BUFSIZE 8192
162  char buffer[BUFSIZE];
163
164  if ((old = open (a_name, O_RDONLY)) < 0)
165    fatal_unexec ("opening %s", a_name);
166
167  new = creat (new_name, 0666);
168  if (new < 0) fatal_unexec ("creating %s", new_name);
169
170  if ((fstat (old, &stat) == -1))
171    fatal_unexec ("fstat %s", a_name);
172
173  oldptr = (char *)mmap (0, stat.st_size, PROT_READ, MAP_FILE|MAP_SHARED, old, 0);
174
175  if (oldptr == (char *)-1)
176    fatal_unexec ("mmap %s", a_name);
177
178  close (old);
179
180  /* This is a copy of the a.out header of the original executable */
181
182  ohdr = (*(struct headers *)oldptr);
183
184  /* This is where we build the new header from the in-memory copy */
185
186  nhdr = *((struct headers *)TEXT_START);
187
188  /* First do some consistency checks */
189
190  if (nhdr.fhdr.f_magic != ALPHAMAGIC
191      && nhdr.fhdr.f_magic != ALPHAUMAGIC)
192    {
193      fprintf (stderr, "unexec: input file magic number is %x, not %x or %x.\n",
194	       nhdr.fhdr.f_magic, ALPHAMAGIC, ALPHAUMAGIC);
195      exit (1);
196    }
197
198  if (nhdr.fhdr.f_opthdr != sizeof (nhdr.aout))
199    {
200      fprintf (stderr, "unexec: input a.out header is %d bytes, not %d.\n",
201	       nhdr.fhdr.f_opthdr, (int)sizeof (nhdr.aout));
202      exit (1);
203    }
204  if (nhdr.aout.magic != ZMAGIC)
205    {
206      fprintf (stderr, "unexec: input file a.out magic number is %o, not %o.\n",
207	       nhdr.aout.magic, ZMAGIC);
208      exit (1);
209    }
210
211
212  /* Now check the existence of certain header section and grab
213     their addresses. */
214
215#define CHECK_SCNHDR(ptr, name, flags)					\
216  ptr = NULL;								\
217  for (i = 0; i < nhdr.fhdr.f_nscns && !ptr; i++)			\
218    if (strncmp (nhdr.section[i].s_name, name, 8) == 0)			\
219      {									\
220	if (nhdr.section[i].s_flags != flags)				\
221	  fprintf (stderr, "unexec: %x flags (%x expected) in %s section.\n", \
222		   nhdr.section[i].s_flags, flags, name);		\
223	ptr = nhdr.section + i;						\
224      }									\
225
226  CHECK_SCNHDR (text_section,  _TEXT,  STYP_TEXT);
227  CHECK_SCNHDR (init_section,  _INIT,  STYP_INIT);
228#ifdef _REL_DYN
229  CHECK_SCNHDR (rel_dyn_section, _REL_DYN,  STYP_REL_DYN);
230#endif /* _REL_DYN */
231#ifdef _DYNSYM
232  CHECK_SCNHDR (dynsym_section, _DYNSYM,  STYP_DYNSYM);
233#endif /* _REL_DYN */
234#ifdef _DYNSTR
235  CHECK_SCNHDR (dynstr_section, _DYNSTR,  STYP_DYNSTR);
236#endif /* _REL_DYN */
237#ifdef _FINI
238  CHECK_SCNHDR (finit_section, _FINI,  STYP_FINI);
239#endif /* _FINI */
240  CHECK_SCNHDR (rdata_section, _RDATA, STYP_RDATA);
241#ifdef _RCONST
242  CHECK_SCNHDR (rconst_section, _RCONST, STYP_RCONST);
243#endif
244#ifdef _PDATA
245  CHECK_SCNHDR (pdata_section, _PDATA, STYP_PDATA);
246#endif /* _PDATA */
247#ifdef _GOT
248  CHECK_SCNHDR (got_section,   _GOT,   STYP_GOT);
249#endif /* _GOT */
250  CHECK_SCNHDR (data_section,  _DATA,  STYP_DATA);
251#ifdef _XDATA
252  CHECK_SCNHDR (xdata_section, _XDATA, STYP_XDATA);
253#endif /* _XDATA */
254#ifdef _LIT8
255  CHECK_SCNHDR (lit8_section,  _LIT8,  STYP_LIT8);
256  CHECK_SCNHDR (lit4_section,  _LIT4,  STYP_LIT4);
257#endif /* _LIT8 */
258  CHECK_SCNHDR (sdata_section, _SDATA, STYP_SDATA);
259  CHECK_SCNHDR (sbss_section,  _SBSS,  STYP_SBSS);
260  CHECK_SCNHDR (bss_section,   _BSS,   STYP_BSS);
261
262
263  pagesize = getpagesize ();
264  brk = (((long) (sbrk (0))) + pagesize - 1) & (-pagesize);
265
266  /* Remember the current break */
267
268  Brk = brk;
269
270  bcopy (data_section, &old_data_scnhdr, sizeof (old_data_scnhdr));
271
272  nhdr.aout.dsize = brk - DATA_START;
273  nhdr.aout.bsize = 0;
274  if (entry_address == 0)
275    {
276      extern DEFAULT_ENTRY_ADDRESS ();
277      nhdr.aout.entry = (unsigned long)DEFAULT_ENTRY_ADDRESS;
278    }
279  else
280    nhdr.aout.entry = entry_address;
281
282  nhdr.aout.bss_start = nhdr.aout.data_start + nhdr.aout.dsize;
283
284  if (rdata_section != NULL)
285    {
286      rdata_section->s_size = data_start - DATA_START;
287
288      /* Adjust start and virtual addresses of rdata_section, too.  */
289      rdata_section->s_vaddr = DATA_START;
290      rdata_section->s_paddr = DATA_START;
291      rdata_section->s_scnptr = text_section->s_scnptr + nhdr.aout.tsize;
292    }
293
294  data_section->s_vaddr = data_start;
295  data_section->s_paddr = data_start;
296  data_section->s_size = brk - data_start;
297
298  if (rdata_section != NULL)
299    {
300      data_section->s_scnptr = rdata_section->s_scnptr + rdata_section->s_size;
301    }
302
303  vaddr = data_section->s_vaddr + data_section->s_size;
304  scnptr = data_section->s_scnptr + data_section->s_size;
305  if (lit8_section != NULL)
306    {
307      lit8_section->s_vaddr = vaddr;
308      lit8_section->s_paddr = vaddr;
309      lit8_section->s_size = 0;
310      lit8_section->s_scnptr = scnptr;
311    }
312  if (lit4_section != NULL)
313    {
314      lit4_section->s_vaddr = vaddr;
315      lit4_section->s_paddr = vaddr;
316      lit4_section->s_size = 0;
317      lit4_section->s_scnptr = scnptr;
318    }
319  if (sdata_section != NULL)
320    {
321      sdata_section->s_vaddr = vaddr;
322      sdata_section->s_paddr = vaddr;
323      sdata_section->s_size = 0;
324      sdata_section->s_scnptr = scnptr;
325    }
326#ifdef _XDATA
327  if (xdata_section != NULL)
328    {
329      xdata_section->s_vaddr = vaddr;
330      xdata_section->s_paddr = vaddr;
331      xdata_section->s_size = 0;
332      xdata_section->s_scnptr = scnptr;
333    }
334#endif
335#ifdef _GOT
336  if (got_section != NULL)
337    {
338      bcopy (got_section, buffer, sizeof (struct scnhdr));
339
340      got_section->s_vaddr = vaddr;
341      got_section->s_paddr = vaddr;
342      got_section->s_size = 0;
343      got_section->s_scnptr = scnptr;
344    }
345#endif /*_GOT */
346  if (sbss_section != NULL)
347    {
348      sbss_section->s_vaddr = vaddr;
349      sbss_section->s_paddr = vaddr;
350      sbss_section->s_size = 0;
351      sbss_section->s_scnptr = scnptr;
352    }
353  if (bss_section != NULL)
354    {
355      bss_section->s_vaddr = vaddr;
356      bss_section->s_paddr = vaddr;
357      bss_section->s_size = 0;
358      bss_section->s_scnptr = scnptr;
359    }
360
361  WRITE (new, (char *)TEXT_START, nhdr.aout.tsize,
362	 "writing text section to %s", new_name);
363  WRITE (new, (char *)DATA_START, nhdr.aout.dsize,
364	 "writing data section to %s", new_name);
365
366#ifdef _GOT
367#define old_got_section ((struct scnhdr *)buffer)
368
369  if (got_section != NULL)
370    {
371      SEEK (new, old_got_section->s_scnptr,
372	    "seeking to start of got_section in %s", new_name);
373      WRITE (new, oldptr + old_got_section->s_scnptr, old_got_section->s_size,
374	     "writing new got_section of %s", new_name);
375      SEEK (new, nhdr.aout.tsize + nhdr.aout.dsize,
376	    "seeking to end of data section of %s", new_name);
377    }
378
379#undef old_got_section
380#endif
381
382  /*
383   * Construct new symbol table header
384   */
385
386  bcopy (oldptr + nhdr.fhdr.f_symptr, buffer, cbHDRR);
387
388#define symhdr ((pHDRR)buffer)
389  newsyms = nhdr.aout.tsize + nhdr.aout.dsize;
390  symrel = newsyms - nhdr.fhdr.f_symptr;
391  nhdr.fhdr.f_symptr = newsyms;
392  symhdr->cbLineOffset += symrel;
393  symhdr->cbDnOffset += symrel;
394  symhdr->cbPdOffset += symrel;
395  symhdr->cbSymOffset += symrel;
396  symhdr->cbOptOffset += symrel;
397  symhdr->cbAuxOffset += symrel;
398  symhdr->cbSsOffset += symrel;
399  symhdr->cbSsExtOffset += symrel;
400  symhdr->cbFdOffset += symrel;
401  symhdr->cbRfdOffset += symrel;
402  symhdr->cbExtOffset += symrel;
403
404  WRITE (new, buffer, cbHDRR, "writing symbol table header of %s", new_name);
405
406  /*
407   * Copy the symbol table and line numbers
408   */
409  WRITE (new, oldptr + ohdr.fhdr.f_symptr + cbHDRR,
410	 stat.st_size - ohdr.fhdr.f_symptr - cbHDRR,
411	 "writing symbol table of %s", new_name);
412
413#ifdef _REL_DYN
414  if (rel_dyn_section)
415    update_dynamic_symbols (oldptr, new_name, new, nhdr.aout);
416#endif
417
418#undef symhdr
419
420  SEEK (new, 0, "seeking to start of header in %s", new_name);
421  WRITE (new, &nhdr, sizeof (nhdr),
422	 "writing header of %s", new_name);
423
424  close (old);
425  close (new);
426  mark_x (new_name);
427}
428
429
430static void
431update_dynamic_symbols (old, new_name, new, aout)
432     char *old;			/* Pointer to old executable */
433     char *new_name;            /* Name of new executable */
434     int new;			/* File descriptor for new executable */
435     struct aouthdr aout;	/* a.out info from the file header */
436{
437#if !defined (__linux__) && !defined (__NetBSD__) && !defined (__OpenBSD__)
438
439  typedef struct dynrel_info {
440    char * addr;
441    unsigned type:8;
442    unsigned index:24;
443    unsigned info:8;
444    unsigned pad:8;
445  } dr_info;
446
447  int nsyms = rel_dyn_section->s_size / sizeof (struct dynrel_info);
448  int i;
449  dr_info * rd_base = (dr_info *) (old + rel_dyn_section->s_scnptr);
450  Elf32_Sym * ds_base = (Elf32_Sym *) (old + dynsym_section->s_scnptr);
451
452  for (i = 0; i < nsyms; i++) {
453    register Elf32_Sym x;
454
455    if (rd_base[i].index == 0)
456      continue;
457
458    x = ds_base[rd_base[i].index];
459
460#if 0
461      fprintf (stderr, "Object inspected: %s, addr = %lx, shndx = %x",
462	       old + dynstr_section->s_scnptr + x.st_name, rd_base[i].addr, x.st_shndx);
463#endif
464
465
466    if ((ELF32_ST_BIND (x.st_info) == STB_GLOBAL)
467	&& (x.st_shndx == 0)
468	/* && (x.st_value == NULL) */
469	) {
470      /* OK, this is probably a reference to an object in a shared
471	 library, so copy the old value. This is done in several steps:
472	 1. reladdr is the address of the location in question relative to
473            the start of the data section,
474         2. oldref is the addr is the mapped in temacs executable,
475         3. newref is the address of the location in question in the
476            undumped executable,
477         4. len is the size of the object reference in bytes --
478            currently only 4 (long) and 8 (quad) are supported.
479	    */
480      register unsigned long reladdr = (long)rd_base[i].addr - old_data_scnhdr.s_vaddr;
481      char * oldref = old + old_data_scnhdr.s_scnptr + reladdr;
482      unsigned long newref = aout.tsize + reladdr;
483      int len;
484
485#if 0
486      fprintf (stderr, "...relocated\n");
487#endif
488
489      if (rd_base[i].type == R_REFLONG)
490	len = 4;
491      else if (rd_base[i].type == R_REFQUAD)
492	len = 8;
493      else
494	fatal_unexec ("unrecognized relocation type in .dyn.rel section (symbol #%d)", (char *) i);
495
496      SEEK (new, newref, "seeking to dynamic symbol in %s", new_name);
497      WRITE (new, oldref, len, "writing old dynrel info in %s", new_name);
498    }
499
500#if 0
501    else
502      fprintf (stderr, "...not relocated\n");
503#endif
504
505  }
506
507#endif /* not __linux__ and not __NetBSD__ and not __OpenBSD__ */
508}
509
510
511/*
512 * mark_x
513 *
514 * After successfully building the new a.out, mark it executable
515 */
516
517static void
518mark_x (name)
519     char *name;
520{
521  struct stat sbuf;
522  int um = umask (777);
523  umask (um);
524  if (stat (name, &sbuf) < 0)
525    fatal_unexec ("getting protection on %s", name);
526  sbuf.st_mode |= 0111 & ~um;
527  if (chmod (name, sbuf.st_mode) < 0)
528    fatal_unexec ("setting protection on %s", name);
529}
530
531static void
532fatal_unexec (s, arg)
533     char *s;
534     char *arg;
535{
536  if (errno == EEOF)
537    fputs ("unexec: unexpected end of file, ", stderr);
538  else
539    fprintf (stderr, "unexec: %s, ", strerror (errno));
540  fprintf (stderr, s, arg);
541  fputs (".\n", stderr);
542  exit (1);
543}
544
545/* arch-tag: 46316c49-ee08-4aa3-942b-00798902f5bd
546   (do not change this comment) */
547