1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
|
/* Unexec for Sunos 4 using shared libraries.
Copyright (C) 1990, 1994 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with GNU Emacs. If not, see
<http://www.gnu.org/licenses/>. */
/* Contributed by Viktor Dukhovni. */
/*
* Unexec for Berkeley a.out format + SUNOS shared libraries
* The unexeced executable contains the __DYNAMIC area from the
* original text file, and then the rest of data + bss + malloced area of
* the current process. (The __DYNAMIC area is at the top of the process
* data segment, we use "data_start" defined externally to mark the start
* of the "real" data segment.)
*
* For programs that want to remap some of the data segment read only
* a run_time_remap is provided. This attempts to remap largest area starting
* and ending on page boundaries between "data_start" and "bndry"
* For this it to figure out where the text file is located. A path search
* is attempted after trying argv[0] and if all fails we simply do not remap
*
* One feature of run_time_remap () is mandatory: reseting the break.
*
* Note that we can no longer map data into the text segment, as this causes
* the __DYNAMIC struct to become read only, breaking the runtime loader.
* Thus we no longer need to mess with a private crt0.c, the standard one
* will do just fine, since environ can live in the writable area between
* __DYNAMIC and data_start, just make sure that pre-crt0.o (the name
* is somewhat abused here) is loaded first!
*
*/
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <a.out.h>
/* Do this after the above #include's in case a configuration file wants
to define things for this file based on what <a.out.h> defines. */
#ifdef emacs
#include <config.h>
#endif
#if defined (SUNOS4) || defined (__FreeBSD__) || defined (__NetBSD__)
#define UNDO_RELOCATION
#endif
#ifdef UNDO_RELOCATION
#include <link.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/* NetBSD needs this bit, but SunOS does not have it. */
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
/*
* for programs other than emacs
* define data_start + initialized here, and make sure
* this object is loaded first!
* emacs will define these elsewhere, and load the object containing
* data_start (pre-crt0.o or firstfile.o?) first!
* The custom crt0.o *must not* be loaded!
*/
#ifndef emacs
static int data_start = 0;
static int initialized = 0;
#else
extern int initialized;
extern unsigned data_start;
extern int pureptr;
#endif
extern char *getenv ();
static unsigned brk_value;
static struct exec nhdr;
static int rd_only_len;
static long cookie;
unexec (new_name, a_name, bndry, bss_start, entry)
char *new_name, *a_name;
unsigned bndry, bss_start, entry;
{
int fd, new;
char *old;
struct exec ohdr; /* Allocate on the stack, not needed in the next life */
struct stat stat;
if ((fd = open (a_name, O_RDONLY)) < 0)
{
fprintf (stderr, "%s: open: ", a_name);
perror (a_name);
exit (1);
}
if ((new = open (new_name, O_WRONLY | O_CREAT, 0666)) == -1)
{
fprintf (stderr, "%s: open: ", a_name);
perror (new_name);
exit (1);
}
if ((fstat (fd, &stat) == -1))
{
fprintf (stderr, "%s: ", a_name);
perror ("fstat");
exit (1);
}
old = (char *)mmap (0, stat.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0);
if (old == (char *)-1)
{
fprintf (stderr, "%s: ", a_name);
perror ("mmap");
exit (1);
}
close (fd);
nhdr = ohdr = (*(struct exec *)old);
/*
* Remember a magic cookie so we know we've got the right binary
* when remapping.
*/
cookie = time (0);
/* Save the break, it is reset to &_end (by ld.so?). */
brk_value = (unsigned) sbrk (0);
/*
* Round up data start to a page boundary (Lose if not a 2 power!)
*/
data_start = ((((int)&data_start) - 1) & ~(N_PAGSIZ (nhdr) - 1)) + N_PAGSIZ (nhdr);
/*
* Round down read only pages to a multiple of the page size
*/
if (bndry)
rd_only_len = ((int)bndry & ~(N_PAGSIZ (nhdr) - 1)) - data_start;
#ifndef emacs
/* Have to do this some time before dumping the data */
initialized = 1;
#endif
/* Handle new data and bss sizes and optional new entry point.
No one actually uses bss_start and entry, but tradition compels
one to support them.
Could complain if bss_start > brk_value,
but the caller is *supposed* to know what she is doing. */
nhdr.a_data = (bss_start ? bss_start : brk_value) - N_DATADDR (nhdr);
nhdr.a_bss = bss_start ? brk_value - bss_start : 0;
if (entry)
nhdr.a_entry = entry;
/*
* Write out the text segment with new header
* Dynamic executables are ZMAGIC with N_TXTOFF==0 and the header
* part of the text segment, but no need to rely on this.
* So write the TEXT first, then go back replace the header.
* Doing it in the other order is less general!
*/
lseek (new, N_TXTOFF (nhdr), L_SET);
write (new, old + N_TXTOFF (ohdr), N_TXTOFF (ohdr) + ohdr.a_text);
lseek (new, 0L, L_SET);
write (new, &nhdr, sizeof (nhdr));
/*
* Write out the head of the old data segment from the file not
* from core, this has the unresolved __DYNAMIC relocation data
* we need to reload
*/
lseek (new, N_DATOFF (nhdr), L_SET);
write (new, old + N_DATOFF (ohdr), (int)&data_start - N_DATADDR (ohdr));
/*
* Copy the rest of the data from core
*/
write (new, &data_start, N_BSSADDR (nhdr) - (int)&data_start);
/*
* Copy the symbol table and line numbers
*/
lseek (new, N_TRELOFF (nhdr), L_SET);
write (new, old + N_TRELOFF (ohdr), stat.st_size - N_TRELOFF (ohdr));
/* Some other BSD systems use this file.
We don't know whether this change is right for them. */
#ifdef UNDO_RELOCATION
/* Undo the relocations done at startup by ld.so.
It will do these relocations again when we start the dumped Emacs.
Doing them twice gives incorrect results. */
{
unsigned long daddr = N_DATADDR (ohdr);
unsigned long rel, erel;
#ifdef SUNOS4
#ifdef SUNOS4_SHARED_LIBRARIES
extern struct link_dynamic _DYNAMIC;
/* SunOS4.x's ld_rel is relative to N_TXTADDR. */
if (!ohdr.a_dynamic)
/* This was statically linked. */
rel = erel = 0;
else if (_DYNAMIC.ld_version < 2)
{
rel = _DYNAMIC.ld_un.ld_1->ld_rel + N_TXTADDR (ohdr);
erel = _DYNAMIC.ld_un.ld_1->ld_hash + N_TXTADDR (ohdr);
}
else
{
rel = _DYNAMIC.ld_un.ld_2->ld_rel + N_TXTADDR (ohdr);
erel = _DYNAMIC.ld_un.ld_2->ld_hash + N_TXTADDR (ohdr);
}
#else /* not SUNOS4_SHARED_LIBRARIES */
rel = erel = 0;
#endif /* not SUNOS4_SHARED_LIBRARIES */
#ifdef sparc
#define REL_INFO_TYPE struct reloc_info_sparc
#else
#define REL_INFO_TYPE struct relocation_info
#endif /* sparc */
#define REL_TARGET_ADDRESS(r) (((REL_INFO_TYPE *)(r))->r_address)
#endif /* SUNOS4 */
#if defined (__FreeBSD__) || defined (__NetBSD__)
extern struct _dynamic _DYNAMIC;
/* FreeBSD's LD_REL is a virtual address itself. */
rel = LD_REL (&_DYNAMIC);
erel = rel + LD_RELSZ (&_DYNAMIC);
#define REL_INFO_TYPE struct relocation_info
#define REL_TARGET_ADDRESS(r) (((REL_INFO_TYPE *)(r))->r_address)
#endif
for (; rel < erel; rel += sizeof (REL_INFO_TYPE))
{
/* This is the virtual address where ld.so will do relocation. */
unsigned long target = REL_TARGET_ADDRESS (rel);
/* This is the offset in the data segment. */
unsigned long segoffset = target - daddr;
/* If it is located below data_start, we have to do nothing here,
because the old data has been already written to the location. */
if (target < (unsigned long)&data_start)
continue;
lseek (new, N_DATOFF (nhdr) + segoffset, L_SET);
write (new, old + N_DATOFF (ohdr) + segoffset, sizeof (unsigned long));
}
}
#endif /* UNDO_RELOCATION */
fchmod (new, 0755);
}
void
run_time_remap (progname)
char *progname;
{
char aout[MAXPATHLEN];
register char *path, *p;
/* Just in case */
if (!initialized)
return;
/* Restore the break */
brk ((char *) brk_value);
/* If nothing to remap: we are done! */
if (rd_only_len == 0)
return;
/*
* Attempt to find the executable
* First try argv[0], will almost always succeed as shells tend to give
* the full path from the hash list rather than using execvp ()
*/
if (is_it (progname))
return;
/*
* If argv[0] is a full path and does not exist, not much sense in
* searching further
*/
if (strchr (progname, '/'))
return;
/*
* Try to search for argv[0] on the PATH
*/
path = getenv ("PATH");
if (path == NULL)
return;
while (*path)
{
/* copy through ':' or end */
for (p = aout; *p = *path; ++p, ++path)
if (*p == ':')
{
++path; /* move past ':' */
break;
}
*p++ = '/';
strcpy (p, progname);
/*
* aout is a candidate full path name
*/
if (is_it (aout))
return;
}
}
is_it (filename)
char *filename;
{
int fd;
long filenames_cookie;
struct exec hdr;
/*
* Open an executable and check for a valid header!
* Can't bcmp the header with what we had, it may have been stripped!
* so we may save looking at non executables with the same name, mostly
* directories.
*/
fd = open (filename, O_RDONLY);
if (fd != -1)
{
if (read (fd, &hdr, sizeof (hdr)) == sizeof (hdr)
&& !N_BADMAG (hdr) && N_DATOFF (hdr) == N_DATOFF (nhdr)
&& N_TRELOFF (hdr) == N_TRELOFF (nhdr))
{
/* compare cookies */
lseek (fd, N_DATOFF (hdr) + (int)&cookie - N_DATADDR (hdr), L_SET);
read (fd, &filenames_cookie, sizeof (filenames_cookie));
if (filenames_cookie == cookie)
{ /* Eureka */
/*
* Do the mapping
* The PROT_EXEC may not be needed, but it is safer this way.
* should the shared library decide to indirect through
* addresses in the data segment not part of __DYNAMIC
*/
mmap ((char *) data_start, rd_only_len, PROT_READ | PROT_EXEC,
MAP_FILE | MAP_SHARED | MAP_FIXED, fd,
N_DATOFF (hdr) + data_start - N_DATADDR (hdr));
close (fd);
return 1;
}
}
close (fd);
}
return 0;
}
|