1 /* Unexec for Sunos 4 using shared libraries.
2 Copyright (C) 1990, 1994, 1999 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* Contributed by Viktor Dukhovni. */
23 * Unexec for Berkeley a.out format + SUNOS shared libraries
24 * The unexeced executable contains the __DYNAMIC area from the
25 * original text file, and then the rest of data + bss + malloced area of
26 * the current process. (The __DYNAMIC area is at the top of the process
27 * data segment, we use "data_start" defined externally to mark the start
28 * of the "real" data segment.)
30 * For programs that want to remap some of the data segment read only
31 * a run_time_remap is provided. This attempts to remap largest area starting
32 * and ending on page boundaries between "data_start" and "bndry"
33 * For this it to figure out where the text file is located. A path search
34 * is attempted after trying argv[0] and if all fails we simply do not remap
36 * One feature of run_time_remap () is mandatory: reseting the break.
38 * Note that we can no longer map data into the text segment, as this causes
39 * the __DYNAMIC struct to become read only, breaking the runtime loader.
40 * Thus we no longer need to mess with a private crt0.c, the standard one
41 * will do just fine, since environ can live in the writable area between
42 * __DYNAMIC and data_start, just make sure that pre-crt0.o (the name
43 * is somewhat abused here) is loaded first!
51 #include <sys/param.h>
59 #if defined (SUNOS4) || defined (__FreeBSD__) || defined (__NetBSD__)
60 #define UNDO_RELOCATION
63 #ifdef UNDO_RELOCATION
71 /* NetBSD needs this bit, but SunOS does not have it. */
78 * for programs other than emacs
79 * define data_start + initialized here, and make sure
80 * this object is loaded first!
81 * emacs will define these elsewhere, and load the object containing
82 * data_start (pre-crt0.o or firstfile.o?) first!
83 * The custom crt0.o *must not* be loaded!
86 static int data_start
= 0;
87 static int initialized
= 0;
89 extern int initialized
;
90 extern unsigned data_start
;
94 extern char *getenv ();
95 static unsigned brk_value
;
96 static struct exec nhdr
;
97 static int rd_only_len
;
101 unexec (new_name
, a_name
, bndry
, bss_start
, entry
)
102 char *new_name
, *a_name
;
103 unsigned bndry
, bss_start
, entry
;
107 struct exec ohdr
; /* Allocate on the stack, not needed in the next life */
110 if ((fd
= open (a_name
, O_RDONLY
)) < 0)
112 fprintf (stderr
, "%s: open: ", a_name
);
116 if ((new = open (new_name
, O_WRONLY
| O_CREAT
, 0666)) == -1)
118 fprintf (stderr
, "%s: open: ", a_name
);
123 if ((fstat (fd
, &stat
) == -1))
125 fprintf (stderr
, "%s: ", a_name
);
130 old
= (char *)mmap (0, stat
.st_size
, PROT_READ
, MAP_FILE
|MAP_SHARED
, fd
, 0);
131 if (old
== (char *)-1)
133 fprintf (stderr
, "%s: ", a_name
);
139 nhdr
= ohdr
= (*(struct exec
*)old
);
143 * Remember a magic cookie so we know we've got the right binary
148 /* Save the break, it is reset to &_end (by ld.so?). */
149 brk_value
= (unsigned) sbrk (0);
152 * Round up data start to a page boundary (Lose if not a 2 power!)
154 data_start
= ((((int)&data_start
) - 1) & ~(N_PAGSIZ (nhdr
) - 1)) + N_PAGSIZ (nhdr
);
157 * Round down read only pages to a multiple of the page size
160 rd_only_len
= ((int)bndry
& ~(N_PAGSIZ (nhdr
) - 1)) - data_start
;
163 /* Have to do this some time before dumping the data */
167 /* Handle new data and bss sizes and optional new entry point.
168 No one actually uses bss_start and entry, but tradition compels
170 Could complain if bss_start > brk_value,
171 but the caller is *supposed* to know what she is doing. */
172 nhdr
.a_data
= (bss_start
? bss_start
: brk_value
) - N_DATADDR (nhdr
);
173 nhdr
.a_bss
= bss_start
? brk_value
- bss_start
: 0;
175 nhdr
.a_entry
= entry
;
178 * Write out the text segment with new header
179 * Dynamic executables are ZMAGIC with N_TXTOFF==0 and the header
180 * part of the text segment, but no need to rely on this.
181 * So write the TEXT first, then go back replace the header.
182 * Doing it in the other order is less general!
184 lseek (new, N_TXTOFF (nhdr
), L_SET
);
185 write (new, old
+ N_TXTOFF (ohdr
), N_TXTOFF (ohdr
) + ohdr
.a_text
);
186 lseek (new, 0L, L_SET
);
187 write (new, &nhdr
, sizeof (nhdr
));
190 * Write out the head of the old data segment from the file not
191 * from core, this has the unresolved __DYNAMIC relocation data
194 lseek (new, N_DATOFF (nhdr
), L_SET
);
195 write (new, old
+ N_DATOFF (ohdr
), (int)&data_start
- N_DATADDR (ohdr
));
198 * Copy the rest of the data from core
200 write (new, &data_start
, N_BSSADDR (nhdr
) - (int)&data_start
);
203 * Copy the symbol table and line numbers
205 lseek (new, N_TRELOFF (nhdr
), L_SET
);
206 write (new, old
+ N_TRELOFF (ohdr
), stat
.st_size
- N_TRELOFF (ohdr
));
208 /* Some other BSD systems use this file.
209 We don't know whether this change is right for them. */
210 #ifdef UNDO_RELOCATION
211 /* Undo the relocations done at startup by ld.so.
212 It will do these relocations again when we start the dumped Emacs.
213 Doing them twice gives incorrect results. */
215 unsigned long daddr
= N_DATADDR (ohdr
);
216 unsigned long rel
, erel
;
218 #ifdef SUNOS4_SHARED_LIBRARIES
219 extern struct link_dynamic _DYNAMIC
;
221 /* SunOS4.x's ld_rel is relative to N_TXTADDR. */
223 /* This was statically linked. */
225 else if (_DYNAMIC
.ld_version
< 2)
227 rel
= _DYNAMIC
.ld_un
.ld_1
->ld_rel
+ N_TXTADDR (ohdr
);
228 erel
= _DYNAMIC
.ld_un
.ld_1
->ld_hash
+ N_TXTADDR (ohdr
);
232 rel
= _DYNAMIC
.ld_un
.ld_2
->ld_rel
+ N_TXTADDR (ohdr
);
233 erel
= _DYNAMIC
.ld_un
.ld_2
->ld_hash
+ N_TXTADDR (ohdr
);
235 #else /* not SUNOS4_SHARED_LIBRARIES */
237 #endif /* not SUNOS4_SHARED_LIBRARIES */
239 #define REL_INFO_TYPE struct reloc_info_sparc
241 #define REL_INFO_TYPE struct relocation_info
243 #define REL_TARGET_ADDRESS(r) (((REL_INFO_TYPE *)(r))->r_address)
245 #if defined (__FreeBSD__) || defined (__NetBSD__)
246 extern struct _dynamic _DYNAMIC
;
248 /* FreeBSD's LD_REL is a virtual address itself. */
249 rel
= LD_REL (&_DYNAMIC
);
250 erel
= rel
+ LD_RELSZ (&_DYNAMIC
);
251 #define REL_INFO_TYPE struct relocation_info
252 #define REL_TARGET_ADDRESS(r) (((REL_INFO_TYPE *)(r))->r_address)
255 for (; rel
< erel
; rel
+= sizeof (REL_INFO_TYPE
))
257 /* This is the virtual address where ld.so will do relocation. */
258 unsigned long target
= REL_TARGET_ADDRESS (rel
);
259 /* This is the offset in the data segment. */
260 unsigned long segoffset
= target
- daddr
;
262 /* If it is located below data_start, we have to do nothing here,
263 because the old data has been already written to the location. */
264 if (target
< (unsigned long)&data_start
)
267 lseek (new, N_DATOFF (nhdr
) + segoffset
, L_SET
);
268 write (new, old
+ N_DATOFF (ohdr
) + segoffset
, sizeof (unsigned long));
271 #endif /* UNDO_RELOCATION */
277 run_time_remap (progname
)
280 char aout
[MAXPATHLEN
];
281 register char *path
, *p
;
287 /* Restore the break */
288 brk ((char *) brk_value
);
290 /* If nothing to remap: we are done! */
291 if (rd_only_len
== 0)
295 * Attempt to find the executable
296 * First try argv[0], will almost always succeed as shells tend to give
297 * the full path from the hash list rather than using execvp ()
299 if (is_it (progname
))
303 * If argv[0] is a full path and does not exist, not much sense in
306 if (strchr (progname
, '/'))
310 * Try to search for argv[0] on the PATH
312 path
= getenv ("PATH");
318 /* copy through ':' or end */
319 for (p
= aout
; *p
= *path
; ++p
, ++path
)
322 ++path
; /* move past ':' */
326 strcpy (p
, progname
);
328 * aout is a candidate full path name
339 long filenames_cookie
;
343 * Open an executable and check for a valid header!
344 * Can't bcmp the header with what we had, it may have been stripped!
345 * so we may save looking at non executables with the same name, mostly
348 fd
= open (filename
, O_RDONLY
);
351 if (read (fd
, &hdr
, sizeof (hdr
)) == sizeof (hdr
)
352 && !N_BADMAG (hdr
) && N_DATOFF (hdr
) == N_DATOFF (nhdr
)
353 && N_TRELOFF (hdr
) == N_TRELOFF (nhdr
))
355 /* compare cookies */
356 lseek (fd
, N_DATOFF (hdr
) + (int)&cookie
- N_DATADDR (hdr
), L_SET
);
357 read (fd
, &filenames_cookie
, sizeof (filenames_cookie
));
358 if (filenames_cookie
== cookie
)
363 * The PROT_EXEC may not be needed, but it is safer this way.
364 * should the shared library decide to indirect through
365 * addresses in the data segment not part of __DYNAMIC
367 mmap ((char *) data_start
, rd_only_len
, PROT_READ
| PROT_EXEC
,
368 MAP_FILE
| MAP_SHARED
| MAP_FIXED
, fd
,
369 N_DATOFF (hdr
) + data_start
- N_DATADDR (hdr
));