1 /* Unexec for Sunos 4 using shared libraries.
2 Copyright (C) 1990, 1994, 1999, 2002, 2003, 2004,
3 2005, 2006 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
22 /* Contributed by Viktor Dukhovni. */
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.)
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
37 * One feature of run_time_remap () is mandatory: reseting the break.
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!
52 #include <sys/param.h>
60 #if defined (SUNOS4) || defined (__FreeBSD__) || defined (__NetBSD__)
61 #define UNDO_RELOCATION
64 #ifdef UNDO_RELOCATION
72 /* NetBSD needs this bit, but SunOS does not have it. */
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!
87 static int data_start
= 0;
88 static int initialized
= 0;
90 extern int initialized
;
91 extern unsigned data_start
;
95 extern char *getenv ();
96 static unsigned brk_value
;
97 static struct exec nhdr
;
98 static int rd_only_len
;
102 unexec (new_name
, a_name
, bndry
, bss_start
, entry
)
103 char *new_name
, *a_name
;
104 unsigned bndry
, bss_start
, entry
;
108 struct exec ohdr
; /* Allocate on the stack, not needed in the next life */
111 if ((fd
= open (a_name
, O_RDONLY
)) < 0)
113 fprintf (stderr
, "%s: open: ", a_name
);
117 if ((new = open (new_name
, O_WRONLY
| O_CREAT
, 0666)) == -1)
119 fprintf (stderr
, "%s: open: ", a_name
);
124 if ((fstat (fd
, &stat
) == -1))
126 fprintf (stderr
, "%s: ", a_name
);
131 old
= (char *)mmap (0, stat
.st_size
, PROT_READ
, MAP_FILE
|MAP_SHARED
, fd
, 0);
132 if (old
== (char *)-1)
134 fprintf (stderr
, "%s: ", a_name
);
140 nhdr
= ohdr
= (*(struct exec
*)old
);
144 * Remember a magic cookie so we know we've got the right binary
149 /* Save the break, it is reset to &_end (by ld.so?). */
150 brk_value
= (unsigned) sbrk (0);
153 * Round up data start to a page boundary (Lose if not a 2 power!)
155 data_start
= ((((int)&data_start
) - 1) & ~(N_PAGSIZ (nhdr
) - 1)) + N_PAGSIZ (nhdr
);
158 * Round down read only pages to a multiple of the page size
161 rd_only_len
= ((int)bndry
& ~(N_PAGSIZ (nhdr
) - 1)) - data_start
;
164 /* Have to do this some time before dumping the data */
168 /* Handle new data and bss sizes and optional new entry point.
169 No one actually uses bss_start and entry, but tradition compels
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;
176 nhdr
.a_entry
= entry
;
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!
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
));
191 * Write out the head of the old data segment from the file not
192 * from core, this has the unresolved __DYNAMIC relocation data
195 lseek (new, N_DATOFF (nhdr
), L_SET
);
196 write (new, old
+ N_DATOFF (ohdr
), (int)&data_start
- N_DATADDR (ohdr
));
199 * Copy the rest of the data from core
201 write (new, &data_start
, N_BSSADDR (nhdr
) - (int)&data_start
);
204 * Copy the symbol table and line numbers
206 lseek (new, N_TRELOFF (nhdr
), L_SET
);
207 write (new, old
+ N_TRELOFF (ohdr
), stat
.st_size
- N_TRELOFF (ohdr
));
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. */
216 unsigned long daddr
= N_DATADDR (ohdr
);
217 unsigned long rel
, erel
;
219 #ifdef SUNOS4_SHARED_LIBRARIES
220 extern struct link_dynamic _DYNAMIC
;
222 /* SunOS4.x's ld_rel is relative to N_TXTADDR. */
224 /* This was statically linked. */
226 else if (_DYNAMIC
.ld_version
< 2)
228 rel
= _DYNAMIC
.ld_un
.ld_1
->ld_rel
+ N_TXTADDR (ohdr
);
229 erel
= _DYNAMIC
.ld_un
.ld_1
->ld_hash
+ N_TXTADDR (ohdr
);
233 rel
= _DYNAMIC
.ld_un
.ld_2
->ld_rel
+ N_TXTADDR (ohdr
);
234 erel
= _DYNAMIC
.ld_un
.ld_2
->ld_hash
+ N_TXTADDR (ohdr
);
236 #else /* not SUNOS4_SHARED_LIBRARIES */
238 #endif /* not SUNOS4_SHARED_LIBRARIES */
240 #define REL_INFO_TYPE struct reloc_info_sparc
242 #define REL_INFO_TYPE struct relocation_info
244 #define REL_TARGET_ADDRESS(r) (((REL_INFO_TYPE *)(r))->r_address)
246 #if defined (__FreeBSD__) || defined (__NetBSD__)
247 extern struct _dynamic _DYNAMIC
;
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)
256 for (; rel
< erel
; rel
+= sizeof (REL_INFO_TYPE
))
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
;
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
)
268 lseek (new, N_DATOFF (nhdr
) + segoffset
, L_SET
);
269 write (new, old
+ N_DATOFF (ohdr
) + segoffset
, sizeof (unsigned long));
272 #endif /* UNDO_RELOCATION */
278 run_time_remap (progname
)
281 char aout
[MAXPATHLEN
];
282 register char *path
, *p
;
288 /* Restore the break */
289 brk ((char *) brk_value
);
291 /* If nothing to remap: we are done! */
292 if (rd_only_len
== 0)
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 ()
300 if (is_it (progname
))
304 * If argv[0] is a full path and does not exist, not much sense in
307 if (strchr (progname
, '/'))
311 * Try to search for argv[0] on the PATH
313 path
= getenv ("PATH");
319 /* copy through ':' or end */
320 for (p
= aout
; *p
= *path
; ++p
, ++path
)
323 ++path
; /* move past ':' */
327 strcpy (p
, progname
);
329 * aout is a candidate full path name
340 long filenames_cookie
;
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
349 fd
= open (filename
, O_RDONLY
);
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
))
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
)
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
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
));
380 /* arch-tag: 30227420-2c6f-4700-a4f8-9e45e52f53b1
381 (do not change this comment) */