1 /* Unexec for Sunos 4 using shared libraries.
2 Copyright (C) 1990, 1994 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
20 /* Contributed by Viktor Dukhovni. */
22 * Unexec for Berkeley a.out format + SUNOS shared libraries
23 * The unexeced executable contains the __DYNAMIC area from the
24 * original text file, and then the rest of data + bss + malloced area of
25 * the current process. (The __DYNAMIC area is at the top of the process
26 * data segment, we use "data_start" defined externally to mark the start
27 * of the "real" data segment.)
29 * For programs that want to remap some of the data segment read only
30 * a run_time_remap is provided. This attempts to remap largest area starting
31 * and ending on page boundaries between "data_start" and "bndry"
32 * For this it to figure out where the text file is located. A path search
33 * is attempted after trying argv[0] and if all fails we simply do not remap
35 * One feature of run_time_remap () is mandatory: reseting the break.
37 * Note that we can no longer map data into the text segment, as this causes
38 * the __DYNAMIC struct to become read only, breaking the runtime loader.
39 * Thus we no longer need to mess with a private crt0.c, the standard one
40 * will do just fine, since environ can live in the writable area between
41 * __DYNAMIC and data_start, just make sure that pre-crt0.o (the name
42 * is somewhat abused here) is loaded first!
45 #include <sys/param.h>
53 /* Do this after the above #include's in case a configuration file wants
54 to define things for this file based on what <a.out.h> defines. */
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 extern struct link_dynamic _DYNAMIC
;
220 /* SunOS4.x's ld_rel is relative to N_TXTADDR. */
221 if (_DYNAMIC
.ld_version
< 2)
223 rel
= _DYNAMIC
.ld_un
.ld_1
->ld_rel
+ N_TXTADDR (ohdr
);
224 erel
= _DYNAMIC
.ld_un
.ld_1
->ld_hash
+ N_TXTADDR (ohdr
);
228 rel
= _DYNAMIC
.ld_un
.ld_2
->ld_rel
+ N_TXTADDR (ohdr
);
229 erel
= _DYNAMIC
.ld_un
.ld_2
->ld_hash
+ N_TXTADDR (ohdr
);
232 #define REL_INFO_TYPE struct reloc_info_sparc
234 #define REL_INFO_TYPE struct relocation_info
236 #define REL_TARGET_ADDRESS(r) (((REL_INFO_TYPE *)(r))->r_address)
238 #if defined (__FreeBSD__) || defined (__NetBSD__)
239 extern struct _dynamic _DYNAMIC
;
241 /* FreeBSD's LD_REL is a virtual address itself. */
242 rel
= LD_REL (&_DYNAMIC
);
243 erel
= rel
+ LD_RELSZ (&_DYNAMIC
);
244 #define REL_INFO_TYPE struct relocation_info
245 #define REL_TARGET_ADDRESS(r) (((REL_INFO_TYPE *)(r))->r_address)
248 for (; rel
< erel
; rel
+= sizeof (REL_INFO_TYPE
))
250 /* This is the virtual address where ld.so will do relocation. */
251 unsigned long target
= REL_TARGET_ADDRESS (rel
);
252 /* This is the offset in the data segment. */
253 unsigned long segoffset
= target
- daddr
;
255 /* If it is located below data_start, we have to do nothing here,
256 because the old data has been already written to the location. */
257 if (target
< (unsigned long)&data_start
)
260 lseek (new, N_DATOFF (nhdr
) + segoffset
, L_SET
);
261 write (new, old
+ N_DATOFF (ohdr
) + segoffset
, sizeof (unsigned long));
264 #endif /* UNDO_RELOCATION */
270 run_time_remap (progname
)
273 char aout
[MAXPATHLEN
];
274 register char *path
, *p
;
280 /* Restore the break */
281 brk ((char *) brk_value
);
283 /* If nothing to remap: we are done! */
284 if (rd_only_len
== 0)
288 * Attempt to find the executable
289 * First try argv[0], will almost always succeed as shells tend to give
290 * the full path from the hash list rather than using execvp ()
292 if (is_it (progname
))
296 * If argv[0] is a full path and does not exist, not much sense in
299 if (strchr (progname
, '/'))
303 * Try to search for argv[0] on the PATH
305 path
= getenv ("PATH");
311 /* copy through ':' or end */
312 for (p
= aout
; *p
= *path
; ++p
, ++path
)
315 ++path
; /* move past ':' */
319 strcpy (p
, progname
);
321 * aout is a candidate full path name
332 long filenames_cookie
;
336 * Open an executable and check for a valid header!
337 * Can't bcmp the header with what we had, it may have been stripped!
338 * so we may save looking at non executables with the same name, mostly
341 fd
= open (filename
, O_RDONLY
);
344 if (read (fd
, &hdr
, sizeof (hdr
)) == sizeof (hdr
)
345 && !N_BADMAG (hdr
) && N_DATOFF (hdr
) == N_DATOFF (nhdr
)
346 && N_TRELOFF (hdr
) == N_TRELOFF (nhdr
))
348 /* compare cookies */
349 lseek (fd
, N_DATOFF (hdr
) + (int)&cookie
- N_DATADDR (hdr
), L_SET
);
350 read (fd
, &filenames_cookie
, sizeof (filenames_cookie
));
351 if (filenames_cookie
== cookie
)
356 * The PROT_EXEC may not be needed, but it is safer this way.
357 * should the shared library decide to indirect through
358 * addresses in the data segment not part of __DYNAMIC
360 mmap ((char *) data_start
, rd_only_len
, PROT_READ
| PROT_EXEC
,
361 MAP_FILE
| MAP_SHARED
| MAP_FIXED
, fd
,
362 N_DATOFF (hdr
) + data_start
- N_DATADDR (hdr
));