1/* Unexec for Sunos 4 using shared libraries.
2   Copyright (C) 1990, 1994, 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
22/* Contributed by Viktor Dukhovni.  */
23/*
24 * Unexec for Berkeley a.out format + SUNOS shared libraries
25 * The unexeced executable contains the __DYNAMIC area from the
26 * original text file,  and then the rest of data + bss + malloced area of
27 * the current process.  (The __DYNAMIC area is at the top of the process
28 * data segment,  we use "data_start" defined externally to mark the start
29 * of the "real" data segment.)
30 *
31 * For programs that want to remap some of the data segment read only
32 * a run_time_remap is provided.  This attempts to remap largest area starting
33 * and ending on page boundaries between "data_start" and "bndry"
34 * For this it to figure out where the text file is located.  A path search
35 * is attempted after trying argv[0] and if all fails we simply do not remap
36 *
37 * One feature of run_time_remap () is mandatory:  reseting the break.
38 *
39 *  Note that we can no longer map data into the text segment,  as this causes
40 *  the __DYNAMIC struct to become read only,  breaking the runtime loader.
41 *  Thus we no longer need to mess with a private crt0.c,  the standard one
42 *  will do just fine,  since environ can live in the writable area between
43 *  __DYNAMIC and data_start,  just make sure that pre-crt0.o (the name
44 *  is somewhat abused here) is loaded first!
45 *
46 */
47
48#ifdef emacs
49#include <config.h>
50#endif
51
52#include <sys/param.h>
53#include <sys/mman.h>
54#include <sys/file.h>
55#include <sys/stat.h>
56#include <string.h>
57#include <stdio.h>
58#include <a.out.h>
59
60#if defined (SUNOS4) || defined (__FreeBSD__) || defined (__NetBSD__)
61#define UNDO_RELOCATION
62#endif
63
64#ifdef UNDO_RELOCATION
65#include <link.h>
66#endif
67
68#ifdef HAVE_UNISTD_H
69#include <unistd.h>
70#endif
71
72/* NetBSD needs this bit, but SunOS does not have it.  */
73#ifndef MAP_FILE
74#define MAP_FILE 0
75#endif
76
77
78/*
79 * for programs other than emacs
80 * define data_start + initialized here,  and make sure
81 * this object is loaded first!
82 * emacs will define these elsewhere,  and load the object containing
83 * data_start (pre-crt0.o or firstfile.o?) first!
84 * The custom crt0.o *must not* be loaded!
85 */
86#ifndef emacs
87  static int data_start = 0;
88  static int initialized = 0;
89#else
90  extern int initialized;
91  extern unsigned data_start;
92  extern int pureptr;
93#endif
94
95extern char *getenv ();
96static unsigned brk_value;
97static struct exec nhdr;
98static int rd_only_len;
99static long cookie;
100
101
102unexec (new_name, a_name, bndry, bss_start, entry)
103     char *new_name, *a_name;
104     unsigned bndry, bss_start, entry;
105{
106  int fd, new;
107  char *old;
108  struct exec ohdr;		/* Allocate on the stack,  not needed in the next life */
109  struct stat stat;
110
111  if ((fd = open (a_name, O_RDONLY)) < 0)
112    {
113      fprintf (stderr, "%s: open: ", a_name);
114      perror (a_name);
115      exit (1);
116    }
117  if ((new = open (new_name, O_WRONLY | O_CREAT, 0666)) == -1)
118    {
119      fprintf (stderr, "%s: open: ", a_name);
120      perror (new_name);
121      exit (1);
122    }
123
124  if ((fstat (fd, &stat) == -1))
125    {
126      fprintf (stderr, "%s: ", a_name);
127      perror ("fstat");
128      exit (1);
129    }
130
131  old = (char *)mmap (0, stat.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0);
132  if (old == (char *)-1)
133    {
134      fprintf (stderr, "%s: ", a_name);
135      perror ("mmap");
136      exit (1);
137    }
138  close (fd);
139
140  nhdr = ohdr = (*(struct exec *)old);
141
142
143  /*
144   * Remember a magic cookie so we know we've got the right binary
145   * when remapping.
146   */
147  cookie = time (0);
148
149  /* Save the break, it is reset to &_end (by ld.so?).  */
150  brk_value = (unsigned) sbrk (0);
151
152  /*
153   * Round up data start to a page boundary (Lose if not a 2 power!)
154   */
155  data_start = ((((int)&data_start) - 1) & ~(N_PAGSIZ (nhdr) - 1)) + N_PAGSIZ (nhdr);
156
157  /*
158   * Round down read only pages to a multiple of the page size
159   */
160  if (bndry)
161    rd_only_len = ((int)bndry & ~(N_PAGSIZ (nhdr) - 1)) - data_start;
162
163#ifndef emacs
164  /* Have to do this some time before dumping the data */
165  initialized = 1;
166#endif
167
168  /* Handle new data and bss sizes and optional new entry point.
169     No one actually uses bss_start and entry,  but tradition compels
170     one to support them.
171     Could complain if bss_start > brk_value,
172     but the caller is *supposed* to know what she is doing.  */
173  nhdr.a_data = (bss_start ? bss_start : brk_value) - N_DATADDR (nhdr);
174  nhdr.a_bss  = bss_start ? brk_value - bss_start : 0;
175  if (entry)
176    nhdr.a_entry = entry;
177
178  /*
179   * Write out the text segment with new header
180   * Dynamic executables are ZMAGIC with N_TXTOFF==0 and the header
181   * part of the text segment, but no need to rely on this.
182   * So write the TEXT first,  then go back replace the header.
183   * Doing it in the other order is less general!
184   */
185  lseek (new, N_TXTOFF (nhdr), L_SET);
186  write (new, old + N_TXTOFF (ohdr), N_TXTOFF (ohdr) + ohdr.a_text);
187  lseek (new, 0L, L_SET);
188  write (new, &nhdr, sizeof (nhdr));
189
190  /*
191   * Write out the head of the old data segment from the file not
192   * from core, this has the unresolved __DYNAMIC relocation data
193   * we need to reload
194   */
195  lseek (new, N_DATOFF (nhdr), L_SET);
196  write (new, old + N_DATOFF (ohdr), (int)&data_start - N_DATADDR (ohdr));
197
198  /*
199   * Copy the rest of the data from core
200   */
201  write (new, &data_start, N_BSSADDR (nhdr) - (int)&data_start);
202
203  /*
204   * Copy the symbol table and line numbers
205   */
206  lseek (new, N_TRELOFF (nhdr), L_SET);
207  write (new, old + N_TRELOFF (ohdr), stat.st_size - N_TRELOFF (ohdr));
208
209  /* Some other BSD systems use this file.
210     We don't know whether this change is right for them.  */
211#ifdef UNDO_RELOCATION
212  /* Undo the relocations done at startup by ld.so.
213     It will do these relocations again when we start the dumped Emacs.
214     Doing them twice gives incorrect results.  */
215  {
216    unsigned long daddr = N_DATADDR (ohdr);
217    unsigned long rel, erel;
218#ifdef SUNOS4
219#ifdef SUNOS4_SHARED_LIBRARIES
220    extern struct link_dynamic _DYNAMIC;
221
222    /*  SunOS4.x's ld_rel is relative to N_TXTADDR. */
223    if (!ohdr.a_dynamic)
224      /* This was statically linked.  */
225      rel = erel = 0;
226    else if (_DYNAMIC.ld_version < 2)
227      {
228	rel = _DYNAMIC.ld_un.ld_1->ld_rel + N_TXTADDR (ohdr);
229	erel = _DYNAMIC.ld_un.ld_1->ld_hash + N_TXTADDR (ohdr);
230      }
231    else
232      {
233	rel = _DYNAMIC.ld_un.ld_2->ld_rel + N_TXTADDR (ohdr);
234	erel = _DYNAMIC.ld_un.ld_2->ld_hash + N_TXTADDR (ohdr);
235      }
236#else /* not SUNOS4_SHARED_LIBRARIES */
237    rel = erel = 0;
238#endif /* not SUNOS4_SHARED_LIBRARIES */
239#ifdef sparc
240#define REL_INFO_TYPE		struct reloc_info_sparc
241#else
242#define REL_INFO_TYPE		struct relocation_info
243#endif /* sparc */
244#define REL_TARGET_ADDRESS(r)	(((REL_INFO_TYPE *)(r))->r_address)
245#endif /* SUNOS4 */
246#if defined (__FreeBSD__) || defined (__NetBSD__)
247    extern struct _dynamic _DYNAMIC;
248
249    /*  FreeBSD's LD_REL is a virtual address itself. */
250    rel = LD_REL (&_DYNAMIC);
251    erel = rel + LD_RELSZ (&_DYNAMIC);
252#define REL_INFO_TYPE		struct relocation_info
253#define REL_TARGET_ADDRESS(r)	(((REL_INFO_TYPE *)(r))->r_address)
254#endif
255
256    for (; rel < erel; rel += sizeof (REL_INFO_TYPE))
257      {
258	/*  This is the virtual address where ld.so will do relocation.  */
259	unsigned long target = REL_TARGET_ADDRESS (rel);
260	/*  This is the offset in the data segment.  */
261	unsigned long segoffset = target - daddr;
262
263	/*  If it is located below data_start, we have to do nothing here,
264	    because the old data has been already written to the location. */
265	if (target < (unsigned long)&data_start)
266	    continue;
267
268	lseek (new, N_DATOFF (nhdr) + segoffset, L_SET);
269	write (new, old + N_DATOFF (ohdr) + segoffset, sizeof (unsigned long));
270      }
271  }
272#endif /* UNDO_RELOCATION */
273
274  fchmod (new, 0755);
275}
276
277void
278run_time_remap (progname)
279     char *progname;
280{
281  char aout[MAXPATHLEN];
282  register char *path, *p;
283
284  /* Just in case */
285  if (!initialized)
286    return;
287
288  /* Restore the break */
289  brk ((char *) brk_value);
290
291  /*  If nothing to remap:  we are done! */
292  if (rd_only_len == 0)
293    return;
294
295  /*
296   * Attempt to find the executable
297   * First try argv[0],  will almost always succeed as shells tend to give
298   * the full path from the hash list rather than using execvp ()
299   */
300  if (is_it (progname))
301    return;
302
303  /*
304   * If argv[0] is a full path and does not exist,  not much sense in
305   * searching further
306   */
307  if (strchr (progname, '/'))
308    return;
309
310  /*
311   * Try to search for  argv[0] on the PATH
312   */
313  path = getenv ("PATH");
314  if (path == NULL)
315    return;
316
317  while (*path)
318    {
319      /* copy through ':' or end */
320      for (p = aout; *p = *path; ++p, ++path)
321	if (*p == ':')
322	  {
323	    ++path;		/* move past ':' */
324	    break;
325	  }
326      *p++ = '/';
327      strcpy (p, progname);
328      /*
329       * aout is a candidate full path name
330       */
331      if (is_it (aout))
332	return;
333    }
334}
335
336is_it (filename)
337  char *filename;
338{
339  int fd;
340  long filenames_cookie;
341  struct exec hdr;
342
343  /*
344   * Open an executable  and check for a valid header!
345   * Can't bcmp the header with what we had,  it may have been stripped!
346   * so we may save looking at non executables with the same name, mostly
347   * directories.
348   */
349  fd = open (filename, O_RDONLY);
350  if (fd != -1)
351    {
352      if (read (fd, &hdr, sizeof (hdr)) == sizeof (hdr)
353	  && !N_BADMAG (hdr) && N_DATOFF (hdr) == N_DATOFF (nhdr)
354	  && N_TRELOFF (hdr) == N_TRELOFF (nhdr))
355	{
356	  /* compare cookies */
357	  lseek (fd, N_DATOFF (hdr) + (int)&cookie - N_DATADDR (hdr), L_SET);
358	  read (fd, &filenames_cookie, sizeof (filenames_cookie));
359	  if (filenames_cookie == cookie)
360	    {			/* Eureka */
361
362	      /*
363	       * Do the mapping
364	       * The PROT_EXEC may not be needed,  but it is safer this way.
365	       * should the shared library decide to indirect through
366	       * addresses in the data segment not part of __DYNAMIC
367	       */
368	      mmap ((char *) data_start, rd_only_len, PROT_READ | PROT_EXEC,
369		    MAP_FILE | MAP_SHARED | MAP_FIXED, fd,
370		    N_DATOFF (hdr) + data_start - N_DATADDR (hdr));
371	      close (fd);
372	      return 1;
373	    }
374	}
375      close (fd);
376    }
377  return 0;
378}
379
380/* arch-tag: 30227420-2c6f-4700-a4f8-9e45e52f53b1
381   (do not change this comment) */
382