1 /* Contributed by Viktor Dukhovni. */
3 * Unexec for Berkeley a.out format + SUNOS shared libraries
4 * The unexeced executable contains the __DYNAMIC area from the
5 * original text file, and then the rest of data + bss + malloced area of
6 * the current process. (The __DYNAMIC area is at the top of the process
7 * data segment, we use "data_start" defined externally to mark the start
8 * of the "real" data segment.)
10 * For programs that want to remap some of the data segment read only
11 * a run_time_remap is provided. This attempts to remap largest area starting
12 * and ending on page boundaries between "data_start" and "bndry"
13 * For this it to figure out where the text file is located. A path search
14 * is attempted after trying argv[0] and if all fails we simply do not remap
16 * One feature of run_time_remap () is mandatory: reseting the break.
18 * Note that we can no longer map data into the text segment, as this causes
19 * the __DYNAMIC struct to become read only, breaking the runtime loader.
20 * Thus we no longer need to mess with a private crt0.c, the standard one
21 * will do just fine, since environ can live in the writable area between
22 * __DYNAMIC and data_start, just make sure that pre-crt0.o (the name
23 * is somewhat abused here) is loaded first!
26 #include <sys/param.h>
34 /* Do this after the above #include's in case a configuration file wants
35 to define things for this file based on what <a.out.h> defines. */
44 /* NetBSD needs this bit, but SunOS does not have it. */
51 * for programs other than emacs
52 * define data_start + initialized here, and make sure
53 * this object is loaded first!
54 * emacs will define these elsewhere, and load the object containing
55 * data_start (pre-crt0.o or firstfile.o?) first!
56 * The custom crt0.o *must not* be loaded!
59 static int data_start
= 0;
60 static int initialized
= 0;
62 extern int initialized
;
63 extern unsigned data_start
;
67 extern char *getenv ();
69 static struct exec nhdr
;
70 static int rd_only_len
;
74 unexec (new_name
, a_name
, bndry
, bss_start
, entry
)
75 char *new_name
, *a_name
;
76 unsigned bndry
, bss_start
, entry
;
80 struct exec ohdr
; /* Allocate on the stack, not needed in the next life */
84 fprintf (stderr
, "Used %d bytes of Pure Storage\n", pureptr
);
87 if ((fd
= open (a_name
, O_RDONLY
)) < 0)
89 fprintf (stderr
, "%s: open: ", a_name
);
93 if ((new = open (new_name
, O_WRONLY
| O_CREAT
, 0666)) == -1)
95 fprintf (stderr
, "%s: open: ", a_name
);
100 if ((fstat (fd
, &stat
) == -1))
102 fprintf (stderr
, "%s: ", a_name
);
107 old
= (char *)mmap (0, stat
.st_size
, PROT_READ
, MAP_FILE
|MAP_SHARED
, fd
, 0);
108 if (old
== (char *)-1)
110 fprintf (stderr
, "%s: ", a_name
);
116 nhdr
= ohdr
= (*(struct exec
*)old
);
120 * Remember a magic cookie so we know we've got the right binary
125 Brk
= sbrk (0); /* Save the break, it is reset to &_end (by ld.so?) */
128 * Round up data start to a page boundary (Lose if not a 2 power!)
130 data_start
= ((((int)&data_start
) - 1) & ~(N_PAGSIZ (nhdr
) - 1)) + N_PAGSIZ (nhdr
);
133 * Round down read only pages to a multiple of the page size
136 rd_only_len
= ((int)bndry
& ~(N_PAGSIZ (nhdr
) - 1)) - data_start
;
139 /* Have to do this some time before dumping the data */
144 * Handle new data and bss sizes and optional new entry point.
145 * No one actually uses bss_start and entry, but tradition compels
146 * one to support them.
147 * Could complain if bss_start > Brk, but the caller is *supposed* to know
150 nhdr
.a_data
= (bss_start
? bss_start
: Brk
) - N_DATADDR (nhdr
);
151 nhdr
.a_bss
= bss_start
? Brk
- bss_start
: 0;
153 nhdr
.a_entry
= entry
;
156 * Write out the text segment with new header
157 * Dynamic executables are ZMAGIC with N_TXTOFF==0 and the header
158 * part of the text segment, but no need to rely on this.
159 * So write the TEXT first, then go back replace the header.
160 * Doing it in the other order is less general!
162 lseek (new, N_TXTOFF (nhdr
), L_SET
);
163 write (new, old
+ N_TXTOFF (ohdr
), N_TXTOFF (ohdr
) + ohdr
.a_text
);
164 lseek (new, 0L, L_SET
);
165 write (new, &nhdr
, sizeof (nhdr
));
168 * Write out the head of the old data segment from the file not
169 * from core, this has the unresolved __DYNAMIC relocation data
172 lseek (new, N_DATOFF (nhdr
), L_SET
);
173 write (new, old
+ N_DATOFF (ohdr
), (int)&data_start
- N_DATADDR (ohdr
));
176 * Copy the rest of the data from core
178 write (new, &data_start
, N_BSSADDR (nhdr
) - (int)&data_start
);
181 * Copy the symbol table and line numbers
183 lseek (new, N_TRELOFF (nhdr
), L_SET
);
184 write (new, old
+ N_TRELOFF (ohdr
), stat
.st_size
- N_TRELOFF (ohdr
));
190 run_time_remap (progname
)
193 char aout
[MAXPATHLEN
];
194 register char *path
, *p
;
200 /* Restore the break */
203 /* If nothing to remap: we are done! */
204 if (rd_only_len
== 0)
208 * Attempt to find the executable
209 * First try argv[0], will almost always succeed as shells tend to give
210 * the full path from the hash list rather than using execvp ()
212 if (is_it (progname
))
216 * If argv[0] is a full path and does not exist, not much sense in
219 if (strchr (progname
, '/'))
223 * Try to search for argv[0] on the PATH
225 path
= getenv ("PATH");
231 /* copy through ':' or end */
232 for (p
= aout
; *p
= *path
; ++p
, ++path
)
235 ++path
; /* move past ':' */
239 strcpy (p
, progname
);
241 * aout is a candidate full path name
256 * Open an executable and check for a valid header!
257 * Can't bcmp() the header with what we had, it may have been stripped!
258 * so we may save looking at non executables with the same name, mostly
261 fd
= open (path
, O_RDONLY
);
264 if (read (fd
, &hdr
, sizeof (hdr
)) == sizeof (hdr
)
265 && !N_BADMAG (hdr
) && N_DATOFF (hdr
) == N_DATOFF (nhdr
)
266 && N_TRELOFF (hdr
) == N_TRELOFF (nhdr
))
268 /* compare cookies */
269 lseek (fd
, N_DATOFF (hdr
) + (int)&cookie
- N_DATADDR (hdr
), L_SET
);
270 read (fd
, &paths_cookie
, sizeof (paths_cookie
));
271 if (paths_cookie
== cookie
)
276 * The PROT_EXEC may not be needed, but it is safer this way.
277 * should the shared library decide to indirect through
278 * addresses in the data segment not part of __DYNAMIC
280 mmap (data_start
, rd_only_len
, PROT_READ
| PROT_EXEC
,
281 MAP_FILE
| MAP_SHARED
| MAP_FIXED
, fd
,
282 N_DATOFF (hdr
) + data_start
- N_DATADDR (hdr
));