2 * Unexec for Berkeley a.out format + SUNOS shared libraries
3 * The unexeced executable contains the __DYNAMIC area from the
4 * original text file, and then the rest of data + bss + malloced area of
5 * the current process. (The __DYNAMIC area is at the top of the process
6 * data segment, we use "data_start" defined externally to mark the start
7 * of the "real" data segment.)
9 * For programs that want to remap some of the data segment read only
10 * a run_time_remap is provided. This attempts to remap largest area starting
11 * and ending on page boundaries between "data_start" and "bndry"
12 * For this it to figure out where the text file is located. A path search
13 * is attempted after trying argv[0] and if all fails we simply do not remap
15 * One feature of run_time_remap () is mandatory: reseting the break.
17 * Note that we can no longer map data into the text segment, as this causes
18 * the __DYNAMIC struct to become read only, breaking the runtime loader.
19 * Thus we no longer need to mess with a private crt0.c, the standard one
20 * will do just fine, since environ can live in the writable area between
21 * __DYNAMIC and data_start, just make sure that pre-crt0.o (the name
22 * is somewhat abused here) is loaded first!
24 * $Log: unexsunos4.c,v $
25 * Revision 1.3 90/02/15 04:27:40 root
26 * Now it actually works.
28 * Revision 1.2 90/02/15 02:02:01 root
29 * Many comments, fixes, works not only with emacs.
31 * Revision 1.1 90/01/29 19:43:46 root
39 #include <sys/param.h>
48 * for programs other than emacs
49 * define data_start + initialized here, and make sure
50 * this object is loaded first!
51 * emacs will define these elsewhere, and load the object containing
52 * data_start (pre-crt0.o or firstfile.o?) first!
53 * The custom crt0.o *must not* be loaded!
56 static int data_start
= 0;
57 static int initialized
= 0;
59 extern int initialized
;
60 extern unsigned data_start
;
64 extern char *getenv ();
66 static struct exec nhdr
;
67 static int rd_only_len
;
71 unexec (new_name
, a_name
, bndry
, bss_start
, entry
)
72 char *new_name
, *a_name
;
73 unsigned bndry
, bss_start
, entry
;
78 struct exec ohdr
; /* Allocate on the stack, not needed in the next life */
82 fprintf (stderr
, "Used %d bytes of Pure Storage\n", pureptr
);
85 if ((fd
= open (a_name
, O_RDONLY
)) < 0)
87 fprintf (stderr
, "%s: open: ", a_name
);
91 if ((new = open (new_name
, O_WRONLY
| O_CREAT
, 0666)) == -1)
93 fprintf (stderr
, "%s: open: ", a_name
);
98 if ((fstat (fd
, &stat
) == -1))
100 fprintf (stderr
, "%s: ", a_name
);
105 old
= (char *)mmap (0, stat
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
106 if (old
== (char *)-1)
108 fprintf (stderr
, "%s: ", a_name
);
114 nhdr
= ohdr
= (*(struct exec
*)old
);
118 * Remeber a magic cookie so we know we've got the right binary
123 Brk
= sbrk (0); /* Save the break, it is reset to &_end (by ld.so?) */
126 * Round up data start to a page boundary (Lose if not a 2 power!)
128 data_start
= ((((int)&data_start
) - 1) & ~(N_PAGSIZ (nhdr
) - 1)) + N_PAGSIZ (nhdr
);
131 * Round down read only pages to a multiple of the page size
134 rd_only_len
= ((int)bndry
& ~(N_PAGSIZ (nhdr
) - 1)) - data_start
;
137 /* Have to do this some time before dumping the data */
142 * Handle new data and bss sizes and optional new entry point.
143 * No one actually uses bss_start and entry, but tradition compels
144 * one to support them.
145 * Could complain if bss_start > Brk, but the caller is *supposed* to know
148 nhdr
.a_data
= (bss_start
? bss_start
: Brk
) - N_DATADDR (nhdr
);
149 nhdr
.a_bss
= bss_start
? Brk
- bss_start
: 0;
151 nhdr
.a_entry
= entry
;
154 * Write out the text segment with new header
155 * Dynamic executables are ZMAGIC with N_TXTOFF==0 and the header
156 * part of the text segment, but no need to rely on this.
157 * So write the TEXT first, then go back replace the header.
158 * Doing it in the other order is less general!
160 lseek (new, N_TXTOFF (nhdr
), L_SET
);
161 write (new, old
+ N_TXTOFF (ohdr
), N_TXTOFF (ohdr
) + ohdr
.a_text
);
162 lseek (new, 0L, L_SET
);
163 write (new, &nhdr
, sizeof (nhdr
));
166 * Write out the head of the old data segment from the file not
167 * from core, this has the unresolved __DYNAMIC relocation data
170 lseek (new, N_DATOFF (nhdr
), L_SET
);
171 write (new, old
+ N_DATOFF (ohdr
), (int)&data_start
- N_DATADDR (ohdr
));
174 * Copy the rest of the data from core
176 write (new, &data_start
, N_BSSADDR (nhdr
) - (int)&data_start
);
179 * Copy the symbol table and line numbers
181 lseek (new, N_TRELOFF (nhdr
), L_SET
);
182 write (new, old
+ N_TRELOFF (ohdr
), stat
.st_size
- N_TRELOFF (ohdr
));
188 run_time_remap (progname
)
191 char aout
[MAXPATHLEN
];
192 register char *path
, *p
;
198 /* Restore the break */
201 /* If nothing to remap: we are done! */
202 if (rd_only_len
== 0)
206 * Attempt to find the executable
207 * First try argv[0], will almost always succeed as shells tend to give
208 * the full path from the hash list rather than using execvp ()
210 if (is_it (progname
))
214 * If argv[0] is a full path and does not exist, not much sense in
217 if (strchr (progname
, '/'))
221 * Try to search for argv[0] on the PATH
223 path
= getenv ("PATH");
229 /* copy through ':' or end */
230 for (p
= aout
; *p
= *path
; ++p
, ++path
)
233 ++path
; /* move past ':' */
237 strcpy (p
, progname
);
239 * aout is a candidate full path name
254 * Open an executable and check for a valid header!
255 * Can't bcmp() the header with what we had, it may have been stripped!
256 * so we may save looking at non executables with the same name, mostly
259 fd
= open (path
, O_RDONLY
);
262 if (read (fd
, &hdr
, sizeof (hdr
)) == sizeof (hdr
)
263 && !N_BADMAG (hdr
) && N_DATOFF (hdr
) == N_DATOFF (nhdr
)
264 && N_TRELOFF (hdr
) == N_TRELOFF (nhdr
))
266 /* compare cookies */
267 lseek (fd
, N_DATOFF (hdr
) + (int)&cookie
- N_DATADDR (hdr
), L_SET
);
268 read (fd
, &paths_cookie
, sizeof (paths_cookie
));
269 if (paths_cookie
== cookie
)
274 * The PROT_EXEC may not be needed, but it is safer this way.
275 * should the shared library decide to indirect through
276 * addresses in the data segment not part of __DYNAMIC
278 mmap (data_start
, rd_only_len
, PROT_READ
| PROT_EXEC
,
279 MAP_SHARED
| MAP_FIXED
, fd
,
280 N_DATOFF (hdr
) + data_start
- N_DATADDR (hdr
));