1995 lines
52 KiB
C
1995 lines
52 KiB
C
/* @(#)rtld.c 1.1 94/10/31 SMI */
|
|
|
|
/*
|
|
* Run-time link editor.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1989, 1990, 1991, 1992 by Sun Microsystems, Inc.
|
|
*/
|
|
#include <sys/param.h>
|
|
#include <sys/file.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/errno.h>
|
|
#include <a.out.h>
|
|
#include <link.h>
|
|
#include "dynamic.h"
|
|
#include "reloc_info.h"
|
|
#include "rtld.h"
|
|
#include <strings.h>
|
|
#include "cache.h"
|
|
#include <sysexits.h>
|
|
|
|
#define VERSION2 2
|
|
|
|
/*
|
|
* Code collapsing macros.
|
|
*/
|
|
#define JMPOFF(x) (x)->v2->ld_plt
|
|
#define RELOCOFF(x) (x)->v2->ld_rel
|
|
#define HASHOFF(x) (x)->v2->ld_hash
|
|
#define SYMOFF(x) (x)->v2->ld_stab
|
|
#define STROFF(x) (x)->v2->ld_symbols
|
|
|
|
#define DONTAPPEND 0
|
|
#define APPEND 1
|
|
|
|
#if TARGET==SUN4
|
|
# define MASK(n) ((1<<(n))-1)
|
|
# define IN_RANGE(v,n) ((-(1<<((n)-1))) <=(v) && (v) < (1<<((n)-1)))
|
|
#define isitpcrel(rp) (rp->r_type == RELOC_DISP8 || rp->r_type == RELOC_DISP16\
|
|
|| rp->r_type == RELOC_DISP32 || rp->r_type == RELOC_WDISP30 \
|
|
|| rp->r_type == RELOC_WDISP22)
|
|
|
|
#endif
|
|
|
|
/*
|
|
* What we do:
|
|
* - crt0 will pass in the pointer to dynamic structure of main prog.
|
|
* - relocate ourselves.
|
|
* - version checking: get the libraries,
|
|
* check the version and mmap them in.
|
|
* - relocate the _GOT_ for both user and shared libraries.
|
|
* - relocate _PLT_ to the binder.
|
|
* - go back to crt0.
|
|
*/
|
|
|
|
/*
|
|
* Interface between crt0 & ld.so.
|
|
*/
|
|
struct crt_i1 {
|
|
int crt_baseaddr; /* Address ld.so is at */
|
|
int crt_dzfd; /* /dev/zero file descriptor */
|
|
int crt_rlfd; /* ld.so file descriptor */
|
|
struct link_dynamic *crt_udp; /* "main_" dynamic */
|
|
char **crt_ep; /* environment strings */
|
|
caddr_t crt_breakp; /* place to put initial breakpoint */
|
|
};
|
|
|
|
/*
|
|
* Global declarations.
|
|
*/
|
|
int devzero_fd; /* cache for file descriptor */
|
|
struct rtc_symb *rtcp; /* allocated commons */
|
|
struct link_map *hlmp = NULL; /* head of link_map chain */
|
|
struct link_map *plmp = NULL; /* first public link_map */
|
|
struct link_map *mlmp; /* link map for "main" */
|
|
struct link_map *ld_lmp; /* link map for runtime linker */
|
|
struct link_object ld_lo; /* dummy link object for linker */
|
|
struct link_map **plmpp; /* insertion point for new lm's */
|
|
struct link_map *flmp = NULL; /* head of link_map free chain */
|
|
struct dl_object *hdlp = NULL; /* head of dl_object chain */
|
|
struct dl_object **pdlpp; /* insertion point for new dl's */
|
|
struct dl_object *fdlp = NULL; /* head of dl_object free chain */
|
|
caddr_t top_of_stack; /* top of stack (+1) */
|
|
char *dl_error; /* last dynamic linking error */
|
|
char *tracing; /* tracing or running? */
|
|
char *preload; /* other shared object to preload */
|
|
char *profile; /* activate profiling */
|
|
char *symbols_public; /* show ld.so symbols to debuggers */
|
|
char *library_path; /* alternate path for library search */
|
|
char **environ; /* root of environment strings */
|
|
char *main_program; /* string identifying lmp of "main" */
|
|
char *ldso_program; /* string identifying lmp of "ld.so" */
|
|
char *cached_symbol = NULL; /* last string cached by lookup */
|
|
int use_cache = 1; /* tell lo_lookup ok to use cache */
|
|
int version_no; /* link dynamic version number */
|
|
|
|
caddr_t rtmalloc();
|
|
static struct nlist *lookup();
|
|
static struct nlist *findsb();
|
|
static struct nlist *ldsofindsb();
|
|
static struct link_map *have_we_got_it();
|
|
static struct link_map *new_lmp();
|
|
static struct link_map *map_so();
|
|
static void map_error();
|
|
static struct dl_object *new_dlp();
|
|
static void relocate();
|
|
static void upd_reloc();
|
|
static struct link_map *pc_to_lmp();
|
|
static void free_dlp();
|
|
static struct dl_object *dlopen_worker();
|
|
|
|
extern int errno;
|
|
extern void rtbinder(); /* declare the binding function */
|
|
extern caddr_t caller(); /* function to return our caller's pc */
|
|
|
|
#if TARGET==SUN4
|
|
extern void iflush(); /* flush instruction cache */
|
|
static void upd_reloc_flushptr();
|
|
#endif
|
|
|
|
/*
|
|
* Option storage.
|
|
*/
|
|
#ifdef PROF
|
|
int prof_fd;
|
|
char *prof_buf;
|
|
#endif
|
|
|
|
#ifdef WHOAMI
|
|
char *progname;
|
|
#endif
|
|
|
|
#ifdef HRC_TIME
|
|
extern void hrc_init();
|
|
extern int hrc_time();
|
|
|
|
int ftime;
|
|
int stime;
|
|
int etime;
|
|
int time1;
|
|
int time2;
|
|
int time3;
|
|
int time4;
|
|
int time5;
|
|
#endif
|
|
|
|
/*
|
|
* Dummies for LD_PRELOAD.
|
|
*/
|
|
static struct link_dynamic_2 pld_ld2; /* dummy structure for preloads */
|
|
static struct link_dynamic pld_ld = /* dummy __DYNAMIC */
|
|
{2, (struct ld_debug *)0};
|
|
|
|
/*
|
|
* Run-time link editor bootstrap entry point. Called by program requiring
|
|
* initial link editing at startup (generally, crt0).
|
|
*/
|
|
rtld(version, iptr, dp)
|
|
int version; /* interface version */
|
|
caddr_t iptr; /* interface passed from program */
|
|
register struct link_dynamic *dp; /* ld.so dynamic pointer */
|
|
{
|
|
register struct crt_i1 *ip; /* crt0 version 1 interface structure */
|
|
register int reloc; /* count of internal relocations */
|
|
register /* scratch relocation entry pointer */
|
|
struct relocation_info *rp;
|
|
int i; /* integer temporaries */
|
|
char *bp, *cp; /* character pointer temporaries */
|
|
char *buf; /* temporary storage pointers */
|
|
struct link_object *lop; /* work pointer */
|
|
struct link_object **lopp; /* preloaded object insertion point */
|
|
struct link_map *lmp; /* work pointer */
|
|
struct link_map *nlmp; /* newly allocated link_map */
|
|
|
|
#if TARGET==SUN4
|
|
void (*iflush_addr)();
|
|
#endif
|
|
|
|
/*
|
|
* Determine interface format. A prototype interface once existed
|
|
* that passed an address as the first argument. We assume this
|
|
* address is greater than the size of a page, and thus use this
|
|
* to test for it. Eventually, support for this will be deleted.
|
|
*/
|
|
if (version < PAGSIZ) { /* version 1 of new interface */
|
|
ip = (struct crt_i1 *) iptr;
|
|
if (version != 1)
|
|
panic(
|
|
"ld.so: unsupported crt interface version of %d\n",
|
|
version);
|
|
} else /* old interface */
|
|
panic("ld.so: obsolete crt interface\n");
|
|
|
|
/*
|
|
* ld.so must itself be relocated, take care of this now.
|
|
* We can not refer to global data before this step is
|
|
* complete. Perform the relocation by stepping over all
|
|
* entries in the relocation table and turn them into
|
|
* absolute addresses.
|
|
*
|
|
* N.B. We have the assumption here that there are no
|
|
* symbolic relocations which need to be performed.
|
|
*/
|
|
(int) dp->v2 += ip->crt_baseaddr;
|
|
|
|
#if TARGET==SUN4
|
|
/*
|
|
* SPARC does not have a divide instruction, but we can not
|
|
* call support routine until we have finished relocating the
|
|
* loader.
|
|
*/
|
|
#define RELOC_SIZE sizeof (struct relocation_info)
|
|
reloc = 0;
|
|
i = GETRELSZ(dp);
|
|
while (i != 0) {
|
|
i -= RELOC_SIZE;
|
|
reloc++;
|
|
}
|
|
|
|
/*
|
|
* We need to do iflush instructions from upd_reloc(). Unfortunately,
|
|
* the iflush routine is in another module, so we need to relocate the
|
|
* references to it before we can call it, but we can't relocate the
|
|
* references without iflush! Ick. To kludge around this, we produce
|
|
* a pointer to the routine and indirect through it to make the call.
|
|
* Then, we do our self-relocation with a magic version of upd_reloc
|
|
* which takes said pointer as an argument.
|
|
*
|
|
* The right way to do this is probably to inline the iflush calls,
|
|
* but the assembler chokes trying to optimize them.
|
|
*/
|
|
|
|
|
|
*(int *) &iflush_addr = ((int) iflush) + ip->crt_baseaddr;
|
|
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
reloc = GETRELSZ(dp) / sizeof (struct relocation_info);
|
|
#endif
|
|
|
|
rp = (struct relocation_info *) (RELOCOFF(dp) +
|
|
(dp->ld_version < VERSION2 ? (int) dp : ip->crt_baseaddr));
|
|
for (i = 0; i < reloc; i++) {
|
|
#if TARGET==SUN4
|
|
upd_reloc_flushptr(rp,
|
|
(long *)(rp->r_address + ip->crt_baseaddr),
|
|
ip->crt_baseaddr, iflush_addr);
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
*(long *)(rp->r_address + ip->crt_baseaddr) =
|
|
*(long *)(rp->r_address + ip->crt_baseaddr) + ip->crt_baseaddr;
|
|
#endif
|
|
rp++;
|
|
}
|
|
|
|
/*
|
|
* Relocation is completed. Update any global data necessary
|
|
* to continue.
|
|
*/
|
|
environ = ip->crt_ep;
|
|
rtgetenv();
|
|
devzero_fd = ip->crt_dzfd;
|
|
pld_ld.ld_un.ld_2 = &pld_ld2; /* C bug with initializing unions */
|
|
|
|
/*
|
|
* Initialize link maps. Create a link map for ld.so -- however
|
|
* do not put it on the "public list" unless we are making its
|
|
* symbols available to the debugger.
|
|
*/
|
|
pdlpp = &hdlp;
|
|
plmpp = &hlmp;
|
|
ldso_program = "/usr/lib/ld.so";
|
|
ld_lo.lo_name = (long)ldso_program - ip->crt_baseaddr;
|
|
ld_lmp = new_lmp(ldso_program, &ld_lo, ip->crt_baseaddr,
|
|
ip->crt_baseaddr, dp, findsb);
|
|
if (!symbols_public)
|
|
hlmp = NULL;
|
|
plmpp = &hlmp;
|
|
db_malloc = heap_malloc = rtmalloc;
|
|
is_secure = secure_objects;
|
|
main_program = "main_$main_";
|
|
|
|
#ifdef PROF
|
|
/*
|
|
* If profiling features are turned on, then see if the profiling
|
|
* file exists. If so, map in the file and use it as our profile
|
|
* buffer.
|
|
*/
|
|
if ((prof_fd = open("/tmp/ld.so.profile_buffer", O_RDWR)) != -1) {
|
|
if ((prof_buf = mmap(0, 0x2000, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, prof_fd, 0)) == (caddr_t)-1)
|
|
panic("ld.so: mmap failure (%d) for profile buffer\n",
|
|
errno);
|
|
profil(prof_buf, 0x2000, ip->crt_baseaddr, 0x4000);
|
|
close(prof_fd);
|
|
}
|
|
#endif PROF
|
|
#ifdef HRC_TIME
|
|
hrc_init();
|
|
ftime = stime = hrc_time();
|
|
#endif
|
|
|
|
/*
|
|
* Free descriptor on ld.so.
|
|
*/
|
|
close(ip->crt_rlfd);
|
|
|
|
/*
|
|
* If we're letting the user freelance, then see if the user has
|
|
* specified any files to be dynamically loaded. If so, build
|
|
* link-object data structures for them and get them.
|
|
* N.B. THIS IS FOR DEBUGGING ONLY. IT IS NOT SUPPORTED NOR
|
|
* IS IT TO BE DOCUMENTED!
|
|
*/
|
|
if ((cp = preload))
|
|
if (!secure_objects()) {
|
|
int f = 0; /* flag for scanning */
|
|
|
|
lopp = &(struct link_object *)pld_ld2.ld_need;
|
|
bp = buf = rtmalloc(strlen(cp) + 1);
|
|
do {
|
|
*bp = *cp;
|
|
if (isspace(*bp) || (*bp == '\0')) {
|
|
*bp = '\0';
|
|
if (f) {
|
|
f = 0;
|
|
lop = (struct link_object *)
|
|
rtmalloc(sizeof
|
|
(struct link_object));
|
|
lop->lo_name = (long)buf;
|
|
lop->lo_library = 0;
|
|
lop->lo_next = NULL;
|
|
*lopp = lop;
|
|
lopp = &(struct link_object *)
|
|
lop->lo_next;
|
|
}
|
|
} else
|
|
if (!f) {
|
|
f = 1;
|
|
buf = bp;
|
|
}
|
|
bp++;
|
|
} while (*cp++);
|
|
|
|
/*
|
|
* Have list of objects. Go add them.
|
|
*/
|
|
(void) new_lmp("ld_$preload_", NULL, (caddr_t)0,
|
|
NULL, &pld_ld, findsb);
|
|
}
|
|
|
|
/*
|
|
* Insert link_map for "main".
|
|
*/
|
|
mlmp = new_lmp(main_program, NULL, MAIN_BASE, (caddr_t)0, ip->crt_udp,
|
|
findsb);
|
|
|
|
#ifdef HRC_TIME
|
|
etime = hrc_time();
|
|
time1 = etime - stime;
|
|
stime = etime;
|
|
#endif
|
|
|
|
/*
|
|
* Walk map of loaded objects, loading their requisite objects as
|
|
* necessary. Note that the first one we load in this loop is the
|
|
* first one we hand back to the debugger (see use of plmp).
|
|
*/
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next)
|
|
if (lmp->lm_ld) {
|
|
for (lop = (struct link_object *)
|
|
&TEXTBASE(lmp)[lmp->lm_ld->v2->ld_need];
|
|
lop != (struct link_object *)&TEXTBASE(lmp)[0];
|
|
lop = (struct link_object *)
|
|
&TEXTBASE(lmp)[lop->lo_next])
|
|
if (!have_we_got_it(lop, TEXTBASE(lmp)))
|
|
if (nlmp = map_so(lmp, lop))
|
|
if (!plmp)
|
|
plmp = nlmp;
|
|
}
|
|
|
|
/*
|
|
* If we've just been asked to describe what we would load,
|
|
* describe what we did load, and terminate the process.
|
|
*/
|
|
if (tracing) {
|
|
for (lmp = plmp; lmp; lmp = lmp->lm_next)
|
|
if (lmp->lm_lop->lo_library)
|
|
printf("\t-l%s.%d => %s\n",
|
|
&lmp->lm_lob[lmp->lm_lop->lo_name],
|
|
lmp->lm_lop->lo_major,
|
|
lmp->lm_name ? lmp->lm_name :
|
|
"not found");
|
|
else
|
|
printf("\t%s%s\n",
|
|
&lmp->lm_lob[lmp->lm_lop->lo_name],
|
|
lmp->lm_name ? "" : " (not found)");
|
|
_exit(0);
|
|
}
|
|
|
|
#ifdef HRC_TIME
|
|
etime = hrc_time();
|
|
time2 = etime - stime;
|
|
stime = etime;
|
|
/* +++ time in mmap loop */
|
|
#endif
|
|
/*
|
|
* Relocate all loaded objects.
|
|
*/
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next)
|
|
if (lmp != ld_lmp)
|
|
relocate(lmp, mlmp);
|
|
|
|
#ifdef HRC_TIME
|
|
etime = hrc_time();
|
|
time3 = etime - stime;
|
|
stime = etime;
|
|
/* +++ time in relocate loop */
|
|
#endif
|
|
|
|
/*
|
|
* Store in the "main" link_dynamic the list of objects we
|
|
* loaded. Also return any commons that were allocated.
|
|
*/
|
|
ip->crt_udp->v2->ld_loaded = plmp;
|
|
if (rtcp)
|
|
ip->crt_udp->ldd->ldd_cp = rtcp;
|
|
|
|
/*
|
|
* If we're running under a debugger, let it know we've
|
|
* done our loader thing.
|
|
*/
|
|
if (ip->crt_udp->ldd->ldd_in_debugger) {
|
|
int psize;
|
|
int pmask;
|
|
|
|
ip->crt_udp->ldd->ldd_bp_addr = ip->crt_breakp;
|
|
psize = getpagesize();
|
|
pmask = ~(psize - 1);
|
|
if (!mlmp->lm_rwt)
|
|
if (mprotect(pmask &
|
|
(int)ip->crt_breakp, psize,
|
|
PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
|
|
map_error("text write-enable", "main");
|
|
ip->crt_udp->ldd->ldd_sym_loaded = 1;
|
|
ip->crt_udp->ldd->ldd_bp_inst = *(int *)(ip->crt_breakp);
|
|
#if TARGET==SUN4
|
|
*(long *)(ip->crt_breakp) = TRAP;
|
|
iflush((long *)(ip->crt_breakp));
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
*(short *)(ip->crt_breakp) = TRAP;
|
|
#endif
|
|
if (!mlmp->lm_rwt)
|
|
if (mprotect(pmask &
|
|
(int)ip->crt_breakp, psize,
|
|
PROT_READ | PROT_EXEC) == -1)
|
|
panic(
|
|
"ld.so: error %d on main text write-protect\n",
|
|
errno);
|
|
}
|
|
|
|
/*
|
|
* Turn write-protect back on for text segments.
|
|
*/
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next)
|
|
if (lmp->lm_rwt) {
|
|
if (mprotect(TEXTBASE(lmp),
|
|
lmp->lm_ld->v2->ld_text,
|
|
PROT_READ | PROT_EXEC) == -1)
|
|
panic(
|
|
"ld.so: write protect error %d\n", errno);
|
|
lmp->lm_rwt = 0;
|
|
}
|
|
|
|
/*
|
|
* Get rid of /dev/zero
|
|
*/
|
|
(void) close(devzero_fd);
|
|
devzero_fd = -1;
|
|
|
|
#ifdef HRC_TIME
|
|
etime = hrc_time();
|
|
time4 = etime - stime;
|
|
stime = etime;
|
|
/* +++ time in misc stuff after relocate loop */
|
|
fprintf(stderr, "before mainloop %x main loop %x relocate %x misc %x\n",
|
|
time1, time2, time3, time4);
|
|
fprintf(stderr, " total %x\n", etime - ftime);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Indicate whether a given link_object has already been mapped into the
|
|
* address space.
|
|
*/
|
|
static struct link_map *
|
|
have_we_got_it(lop, ba)
|
|
struct link_object *lop;
|
|
caddr_t ba;
|
|
{
|
|
struct link_map *lmp;
|
|
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next)
|
|
if (lmp->lm_lop)
|
|
if (!strcmp(&ba[lop->lo_name],
|
|
&lmp->lm_lob[lmp->lm_lop->lo_name]))
|
|
return (lmp);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Handle mapping error.
|
|
*/
|
|
static void
|
|
map_error(s, cp)
|
|
char *s; /* type of mapping */
|
|
char *cp; /* object being mapped */
|
|
{
|
|
|
|
if (errno == ENOMEM) {
|
|
fprintf(stderr,
|
|
"ld.so: swap space exhausted for %s of %s\n", s, cp);
|
|
_exit(EX_TEMPFAIL);
|
|
} else
|
|
panic("ld.so: %s error (%d) for %s\n", s, errno, cp);
|
|
}
|
|
|
|
static struct link_map *
|
|
mapit(s, lop, lob, intp)
|
|
char *s; /* object name */
|
|
struct link_object *lop; /* link object that derived file */
|
|
caddr_t lob; /* base address for link object */
|
|
struct nlist *(*intp)(); /* interpreter for link map */
|
|
{
|
|
struct exec exec; /* working area for object headers */
|
|
int fd; /* file descriptor temporary */
|
|
caddr_t addr; /* mmap result temporary */
|
|
struct link_dynamic *dp; /* dynamic pointer of object mapped */
|
|
int size; /* size of object */
|
|
|
|
if ((fd = open(s, O_RDONLY)) == -1)
|
|
return(0);
|
|
|
|
/*
|
|
* Verify the object's header.
|
|
*/
|
|
if (read(fd, (char *)&exec, sizeof (struct exec)) !=
|
|
sizeof (struct exec))
|
|
panic("ld.so: can't read struct exec for %s\n", s);
|
|
if
|
|
#if TARGET == SUN2
|
|
(exec.a_machtype != M_68010)
|
|
#endif
|
|
#if TARGET == SUN3
|
|
((exec.a_machtype != M_68010) && (exec.a_machtype != M_68020))
|
|
#endif
|
|
#if TARGET == SUN4
|
|
(exec.a_machtype != M_SPARC)
|
|
#endif
|
|
panic("ld.so: %s is not for this machine type\n", s);
|
|
|
|
/*
|
|
* Map text and allocate enough address space to fit the whole
|
|
* library. Note that we map enough to catch the first symbol
|
|
* in the symbol table and thereby avoid an "lseek" & "read"
|
|
* pair to pick it up.
|
|
*/
|
|
/* XXX need to fail in ways besides "panic". */
|
|
size = max(SIZE(exec), N_SYMOFF(exec) + sizeof (struct nlist));
|
|
if ((addr = mmap(0, size, PROT_READ | PROT_EXEC, MAP_PRIVATE,
|
|
fd, 0)) == (caddr_t)-1)
|
|
map_error("mmap", s);
|
|
|
|
/*
|
|
* Grab the first symbol entry while we've got it mapped aligned
|
|
* to file addresses. We assume that this symbol describes the
|
|
* object's link_dynamic.
|
|
*/
|
|
dp = (struct link_dynamic *)&addr[
|
|
((struct nlist *)&addr[N_SYMOFF(exec)])->n_value];
|
|
|
|
/*
|
|
* Map the initialized data portion of the file to the correct
|
|
* point in the range of allocated addresses. This will leave
|
|
* some portion of the data segment "doubly mapped" on machines
|
|
* where the text/data relocation alignment is not on a page
|
|
* boundaries. However, leaving the file mapped has the double
|
|
* advantage of both saving the munmap system call and of leaving
|
|
* us a contiguous chunk of address space devoted to the object --
|
|
* in case we need to unmap it all later.
|
|
*/
|
|
if (mmap((caddr_t)(addr+SROUND(exec.a_text)), (int) exec.a_data,
|
|
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE,
|
|
fd, (off_t) exec.a_text) == (caddr_t)-1)
|
|
map_error("mmap data", s);
|
|
|
|
/*
|
|
* Allocate pages for the object's bss, if necessary.
|
|
*/
|
|
if (exec.a_bss != 0) {
|
|
if (!get_zero_object())
|
|
map_error("mmap bss", s);
|
|
if (mmap(addr + SROUND(exec.a_text) + exec.a_data,
|
|
(int) exec.a_bss,
|
|
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE,
|
|
devzero_fd, (off_t) 0) == (caddr_t)-1)
|
|
map_error("mmap bss", s);
|
|
}
|
|
|
|
/*
|
|
* Finished with shared object mapping, get rid of descriptor.
|
|
*/
|
|
dp->v2 = (struct link_dynamic_2 *)((int)dp->v2 + (int)addr);
|
|
(void) close(fd);
|
|
return(new_lmp(s, lop, lob, addr, dp, intp));
|
|
}
|
|
|
|
/*
|
|
* Map in a link object, return its link_map.
|
|
*/
|
|
static struct link_map *
|
|
map_so(lmp, lop)
|
|
struct link_map *lmp; /* link_map from which lop came */
|
|
struct link_object *lop; /* object to be mapped */
|
|
{
|
|
caddr_t cp; /* work pointer */
|
|
static int flushed = 0; /* only flush cache once */
|
|
struct link_map *nlmp; /* new link map for mapped object */
|
|
|
|
/*
|
|
* Determine the absolute name of the object to be mapped.
|
|
*/
|
|
for (;;) {
|
|
cp = lop->lo_library ? lo_lookup(lop, lmp) :
|
|
&TEXTBASE(lmp)[lop->lo_name];
|
|
if (!cp) {
|
|
if (!tracing)
|
|
panic("ld.so: lib%s.so.%d: not found\n",
|
|
&TEXTBASE(lmp)[lop->lo_name],
|
|
lop->lo_major);
|
|
return (new_lmp(NULL, lop, TEXTBASE(lmp), 0, 0, findsb));
|
|
}
|
|
|
|
/*
|
|
* Open object. If we can not, see if flushing the cache
|
|
* and retrying helps. There is special handling for the
|
|
* "programmer's interface", embodied in libdl -- which
|
|
* is really just an alias for some of our own symbols.
|
|
* Note that this allows for special handling ONLY for
|
|
* a libdl installed with /usr/lib/ld.so -- i.e., the
|
|
* one *in* /usr/lib.
|
|
*/
|
|
if ((nlmp = mapit(cp, lop, TEXTBASE(lmp),
|
|
(!strcmp(cp, "/usr/lib/libdl.so.1.0") ?
|
|
ldsofindsb : findsb))) == 0) {
|
|
if (flushed) {
|
|
if (!tracing)
|
|
panic("ld.so: open error %d for %s\n",
|
|
errno, cp);
|
|
return (new_lmp(NULL, lop, TEXTBASE(lmp),
|
|
0, 0, findsb));
|
|
} else {
|
|
lo_flush();
|
|
flushed++;
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Process dynamic interface we just loaded, including
|
|
* list of objects on which this one depends.
|
|
*/
|
|
return (nlmp);
|
|
}
|
|
|
|
/*
|
|
* Allocate a new link map for file "f", loaded at "addr" from link_object
|
|
* "lop", with a dynamic at "dp". Manage a free list of "link maps" -- as
|
|
* we have no way to reclaim one into a "heap" when one is made free.
|
|
*/
|
|
static struct link_map *
|
|
new_lmp(f, lop, lob, addr, dp, intp)
|
|
char *f; /* file name */
|
|
struct link_object *lop; /* link object that derived file */
|
|
caddr_t lob; /* base address for link object */
|
|
caddr_t addr; /* address where mapped */
|
|
struct link_dynamic *dp; /* link_dynamic for file */
|
|
struct nlist *(*intp)(); /* interpreter for link map */
|
|
{
|
|
caddr_t offset; /* hack for "main" */
|
|
struct link_map *lmp; /* link map we built */
|
|
|
|
/*
|
|
* For the "main" program, addresses in the link_dynamic structure
|
|
* are "broken" (text relocated to "0" rather than MAIN_BASE).
|
|
* We "know" which these are, and deal with them especially here.
|
|
* XXX
|
|
*/
|
|
offset = f == main_program ? (caddr_t)MAIN_BASE : addr;
|
|
|
|
/*
|
|
* Allocate link_map structure, and private data structure.
|
|
* Add the new link_map to the list of link_map's.
|
|
*/
|
|
if (lmp = flmp)
|
|
flmp = lmp->lm_next;
|
|
else {
|
|
lmp = (struct link_map *)rtmalloc(sizeof (struct link_map));
|
|
lmp->lm_lpd = rtmalloc(sizeof (struct ld_private));
|
|
}
|
|
lmp->lm_next = *plmpp;
|
|
*plmpp = lmp;
|
|
plmpp = &lmp->lm_next;
|
|
|
|
/*
|
|
* Fill in the fields in the new link_map. Load the private
|
|
* data cache with pre-relocated information from the object's
|
|
* unrelocated link_dynamic.
|
|
*/
|
|
LM2LP(lmp)->lp_symbol_base = lmp->lm_addr = addr;
|
|
if (f) {
|
|
lmp->lm_name = rtmalloc(strlen(f) + 1);
|
|
strcpy(lmp->lm_name, f);
|
|
} else
|
|
lmp->lm_name = f;
|
|
lmp->lm_lop = lop;
|
|
lmp->lm_lob = lob;
|
|
lmp->lm_ld = dp;
|
|
|
|
/*
|
|
* Fill in fields from target's dynamic structure -- if there is
|
|
* one. (There might not be because we are "tracing.")
|
|
*/
|
|
if (dp) {
|
|
if (dp->ld_version < VERSION2)
|
|
panic("ld.so: __DYNAMIC version %d not supported\n",
|
|
dp->ld_version);
|
|
LM2LP(lmp)->lp_plt = (struct jbind *)(&addr[JMPOFF(dp)]);
|
|
LM2LP(lmp)->lp_rp =
|
|
(struct relocation_info *)(&offset[RELOCOFF(dp)]);
|
|
LM2LP(lmp)->lp_hash = (struct fshash *)(&offset[HASHOFF(dp)]);
|
|
LM2LP(lmp)->lp_symtab = (struct nlist *)(&offset[SYMOFF(dp)]);
|
|
LM2LP(lmp)->lp_symstr = &offset[STROFF(dp)];
|
|
LM2LP(lmp)->lp_textbase = offset;
|
|
LM2LP(lmp)->lp_interp = intp;
|
|
LM2LP(lmp)->lp_refcnt++;
|
|
LM2LP(lmp)->lp_dlh = LM2LP(lmp)->lp_dlp = NULL;
|
|
}
|
|
return (lmp);
|
|
}
|
|
|
|
static void
|
|
free_lmp(lmp)
|
|
struct link_map *lmp;
|
|
{
|
|
struct link_map *tlmp; /* temporary */
|
|
struct link_map **plmp; /* previous pointer */
|
|
|
|
tlmp = hlmp, plmp = &hlmp;
|
|
while (tlmp) {
|
|
if (tlmp == lmp) {
|
|
cached_symbol = NULL;
|
|
(void) munmap(lmp->lm_addr,
|
|
max(SIZE(*(struct exec *)lmp->lm_addr),
|
|
N_SYMOFF((*(struct exec *)lmp->lm_addr)) +
|
|
sizeof (struct nlist)));
|
|
*plmp = lmp->lm_next;
|
|
if (plmpp == &lmp->lm_next)
|
|
plmpp = plmp;
|
|
lmp->lm_next = flmp;
|
|
flmp = lmp;
|
|
return;
|
|
}
|
|
plmp = &tlmp->lm_next;
|
|
tlmp = tlmp->lm_next;
|
|
}
|
|
panic("ld.so: mangled lm object list.\n");
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* Relocate an object.
|
|
*/
|
|
static void
|
|
relocate(lmp, clmp)
|
|
register struct link_map *lmp; /* object to be relocated */
|
|
register struct link_map *clmp; /* calling link map */
|
|
{
|
|
int k; /* loop temporary */
|
|
int nr; /* number of relocations */
|
|
char *symbol; /* symbol being searched for */
|
|
caddr_t et; /* cached _etext of object */
|
|
register long j; /* relocation temporary */
|
|
register caddr_t ra; /* cached relocation address */
|
|
register struct /* current relocation */
|
|
relocation_info *rp;
|
|
struct nlist *sp; /* symbol table of "symbol" */
|
|
struct link_map *llmp; /* lmp of source of "symbol" */
|
|
|
|
/*
|
|
* Cache some invariants.
|
|
*/
|
|
rp = LM2LP(lmp)->lp_rp;
|
|
et = &TEXTBASE(lmp)[lmp->lm_ld->v2->ld_text];
|
|
nr = GETRELSZ(lmp->lm_ld) / sizeof (struct relocation_info);
|
|
|
|
/*
|
|
* Initialize _PLT_, if any.
|
|
*/
|
|
if (lmp->lm_ld->v2->ld_plt_sz) {
|
|
#if TARGET==SUN4
|
|
int *inst = LM2LP(lmp)->lp_plt->jb_inst;
|
|
int tmp = (int) rtbinder;
|
|
|
|
*inst |= ((tmp >> (32-22)) & MASK(22));
|
|
iflush(inst);
|
|
inst++;
|
|
*inst |= (tmp & MASK(10));
|
|
iflush(inst);
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
LM2LP(lmp)->lp_plt->cl_hi = (int) rtbinder >> 16;
|
|
LM2LP(lmp)->lp_plt->cl_low = (int) rtbinder & 0xffff;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Loop over all relocations.
|
|
*/
|
|
for (k = 0; k < nr; k++, rp++) {
|
|
/*
|
|
* Check to see if we're relocating in the text segment
|
|
* and turn off the write protect if necessary.
|
|
*/
|
|
if ((ra = &lmp->lm_addr[rp->r_address]) < et)
|
|
if (lmp->lm_rwt == 0) {
|
|
if (mprotect(TEXTBASE(lmp),
|
|
(long)(et - TEXTBASE(lmp)),
|
|
PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
|
|
map_error("text write-enable",
|
|
lmp->lm_name);
|
|
lmp->lm_rwt = 1;
|
|
}
|
|
|
|
/*
|
|
* Perform the relocation.
|
|
*/
|
|
if (rp->r_extern == 0) {
|
|
#if TARGET==SUN4
|
|
upd_reloc(rp, (long *)ra, (long) lmp->lm_addr);
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
*(long *)(ra) += (int)lmp->lm_addr;
|
|
#endif
|
|
} else {
|
|
#if TARGET==SUN4
|
|
if (rp->r_type == RELOC_JMP_SLOT)
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
if (rp->r_jmptable)
|
|
#endif
|
|
continue;
|
|
sp = &LM2LP(lmp)->lp_symtab[rp->r_symbolnum];
|
|
symbol = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
|
|
if ((sp = lookup(symbol, &llmp, clmp)) == NULL)
|
|
panic("ld.so: Undefined symbol: %s\n",
|
|
symbol);
|
|
#if TARGET==SUN4
|
|
j = sp->n_value + (long) (sp->n_type==N_COMM ||
|
|
sp->n_type==N_ABS+N_EXT ? 0 :
|
|
LM2LP(llmp)->lp_symbol_base)
|
|
+ rp->r_addend;
|
|
if (isitpcrel(rp))
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
j = sp->n_value + (long) (sp->n_type==N_COMM ||
|
|
sp->n_type==N_ABS+N_EXT ? 0 :
|
|
LM2LP(llmp)->lp_symbol_base);
|
|
if (rp->r_pcrel)
|
|
#endif
|
|
j -= (long)lmp->lm_addr;
|
|
#if TARGET==SUN4
|
|
upd_reloc(rp, (long *)ra, j);
|
|
#endif TARGET==SUN4
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
/*
|
|
* Optimize out the call to upd_reloc. Note
|
|
* that upd_reloc has unimplemented sanity checking
|
|
* that, when implemented, may require removing
|
|
* or otherwise altering this optimization.
|
|
*/
|
|
*(long *)ra += j;
|
|
#endif TARGET==SUN3 || TARGET==SUN2
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if TARGET==SUN4
|
|
/*
|
|
* This is a special version of upd_reloc, used only to do our initial
|
|
* self-relocation. There are two differences between this routine and the
|
|
* standard one. First, it takes a pointer to the iflush() routine. Second,
|
|
* to save memory, it implements a very limited subset of the relocation types.
|
|
*/
|
|
static void
|
|
upd_reloc_flushptr(rp, where, what, iflush_addr)
|
|
struct relocation_info *rp;
|
|
long *where;
|
|
long what;
|
|
void (*iflush_addr)();
|
|
{
|
|
switch (rp->r_type) {
|
|
case RELOC_RELATIVE:
|
|
what += *where << (32-22);
|
|
*(long *)where = (*(long *)where & ~MASK(22)) |
|
|
((what >> (32-22)) & MASK(22));
|
|
(*iflush_addr)(where);
|
|
where++;
|
|
what += (*where & MASK(10));
|
|
*(long *)where = (*(long *)where & ~MASK(10)) | (what & MASK(10));
|
|
(*iflush_addr)(where);
|
|
break;
|
|
case RELOC_32:
|
|
case RELOC_GLOB_DAT:
|
|
case RELOC_DISP32:
|
|
what += *where;
|
|
*(long *)where = what;
|
|
(*iflush_addr)(where);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "unknown relocation type %d", rp->r_type);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Perform a specific relocation operation.
|
|
*/
|
|
static void
|
|
upd_reloc(rp, where, what)
|
|
struct relocation_info *rp;
|
|
long *where;
|
|
long what;
|
|
{
|
|
#if TARGET==SUN4
|
|
switch (rp->r_type) {
|
|
case RELOC_RELATIVE:
|
|
what += *where << (32-22);
|
|
*(long *)where = (*(long *)where & ~MASK(22)) |
|
|
((what >> (32-22)) & MASK(22));
|
|
iflush(where);
|
|
where++;
|
|
what += (*where & MASK(10));
|
|
*(long *)where = (*(long *)where & ~MASK(10)) | (what & MASK(10));
|
|
iflush(where);
|
|
break;
|
|
case RELOC_8:
|
|
case RELOC_DISP8:
|
|
what += *where & MASK(8);
|
|
if (!IN_RANGE(what,8))
|
|
fprintf(stderr, "byte displacement overflow at %#x",
|
|
rp->r_address);
|
|
*where = what;
|
|
iflush(where);
|
|
break;
|
|
case RELOC_LO10:
|
|
case RELOC_BASE10:
|
|
what += *where & MASK(10);
|
|
*(long *)where = (*(long *)where & ~MASK(10)) | (what & MASK(10));
|
|
iflush(where);
|
|
break;
|
|
case RELOC_BASE13:
|
|
case RELOC_13:
|
|
what += *where & MASK(13);
|
|
*(long *)where = (*(long *)where & ~MASK(13)) | (what & MASK(13));
|
|
iflush(where);
|
|
break;
|
|
|
|
case RELOC_16:
|
|
case RELOC_DISP16:
|
|
what += *where & MASK(16);
|
|
if (!IN_RANGE(what,16))
|
|
fprintf(stderr, "word displacement overflow at %#x",
|
|
rp->r_address);
|
|
*(short *)where = what;
|
|
iflush(((long) where) & ~3);
|
|
break;
|
|
case RELOC_22:
|
|
case RELOC_BASE22:
|
|
what += *where & MASK(22);
|
|
if (!IN_RANGE(what,22))
|
|
fprintf(stderr, "sethi displacement overflow at %#x",
|
|
rp->r_address);
|
|
*(long *)where = (*(long *)where & ~MASK(22)) | (what & MASK(22));
|
|
iflush(where);
|
|
break;
|
|
case RELOC_HI22:
|
|
what += (*where<<32-22) & MASK(22);
|
|
*(long *)where = (*(long *)where & ~MASK(22))
|
|
| ((what>>(32-22)) & MASK(22));
|
|
iflush(where);
|
|
break;
|
|
case RELOC_WDISP22:
|
|
what += *where & MASK(22);
|
|
if (what & MASK(2) )
|
|
fprintf(stderr, "odd word displacement at %#x",
|
|
rp->r_address);
|
|
what >>= 2;
|
|
if (!IN_RANGE(what,22))
|
|
fprintf(stderr, "branch displacement overflow at %#x",
|
|
rp->r_address);
|
|
*(long *)where = (*(long *)where & ~MASK(22)) | (what & MASK(22));
|
|
iflush(where);
|
|
break;
|
|
|
|
case RELOC_WDISP30:
|
|
what += *where & MASK(30);
|
|
if (what & MASK(2) )
|
|
fprintf(stderr, "odd word displacement at %#x",
|
|
rp->r_address);
|
|
what >>= 2;
|
|
*(long *)where = (*(long *)where & ~MASK(30)) | (what&MASK(30));
|
|
iflush(where);
|
|
break;
|
|
case RELOC_32:
|
|
case RELOC_GLOB_DAT:
|
|
case RELOC_DISP32:
|
|
what += *where;
|
|
*(long *)where = what;
|
|
iflush(where);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "unknown relocation type %d", rp->r_type);
|
|
break;
|
|
}
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
/*
|
|
* Put the value back in the segment,
|
|
* while checking for overflow.
|
|
*/
|
|
what += *where;
|
|
*(long *)where = what;
|
|
/* ++++ need to add this
|
|
switch (rp->r_length) {
|
|
case 0:
|
|
if (what < -128 || what > 127)
|
|
fprintf(stderr, "byte displacement overflow at %#x",
|
|
rp->r_address);
|
|
*where = what;
|
|
break;
|
|
case 1:
|
|
if (what < -32768 || what > 32767)
|
|
fprintf(stderr, "word displacement overflow at %#x",
|
|
rp->r_address);
|
|
*(short *)where = what;
|
|
break;
|
|
case 2:
|
|
*(long *)where = what;
|
|
break;
|
|
}
|
|
*/
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Calculate hash value for symbol being looked up.
|
|
*/
|
|
static int
|
|
gethashv(sn)
|
|
char *sn;
|
|
{
|
|
int val; /* accumulator for hash value */
|
|
/*
|
|
* Calculate hash value for symbol being looked up.
|
|
*/
|
|
for (val = 0; *sn;)
|
|
val = (val<<1) + *sn++;
|
|
return(val);
|
|
}
|
|
|
|
static struct nlist *
|
|
ldsofindsb(lmp, sn, clmp)
|
|
struct link_map *lmp;
|
|
char *sn;
|
|
struct link_map *clmp;
|
|
{
|
|
struct nlist *sp;
|
|
|
|
if ((sp = findsb(lmp, sn, clmp)) == 0)
|
|
return (0);
|
|
if ((sp->n_value == 0) || (sp->n_type == N_EXT+N_UNDF))
|
|
return (0);
|
|
LM2LP(lmp)->lp_symbol_base = ld_lmp->lm_addr;
|
|
return (findsb(ld_lmp, sn, clmp));
|
|
}
|
|
|
|
#define HASHMASK 0x7fffffff
|
|
|
|
static struct nlist *
|
|
findsb(lmp, sn, clmp)
|
|
struct link_map *lmp;
|
|
char *sn;
|
|
struct link_map *clmp;
|
|
{
|
|
register char *cp; /* temporary */
|
|
register char *s1; /* string handling pointers */
|
|
struct fshash *p; /* working pointer to .so symbols */
|
|
int i; /* temporary */
|
|
struct nlist *sp; /* symbol entry pointer */
|
|
static int hashval; /* cache hash value of last symbol */
|
|
static char *lp; /* cache last symbol pointer */
|
|
|
|
/*
|
|
* this is a questionable cache since the same pointer may
|
|
* pointed to a different symbol. ++++
|
|
*/
|
|
if (lp != sn)
|
|
hashval = gethashv(sn);
|
|
|
|
if (LM2LP(lmp)->lp_hash == (struct fshash *)&lmp->lm_addr[0])
|
|
return(0); /* not found */
|
|
i = (hashval & HASHMASK) % (lmp->lm_ld->v2->ld_buckets == 0
|
|
? RTHS : lmp->lm_ld->v2->ld_buckets);
|
|
p = LM2LP(lmp)->lp_hash + i;
|
|
if (p->fssymbno != -1)
|
|
do {
|
|
sp = &LM2LP(lmp)->lp_symtab[p->fssymbno];
|
|
s1 = sn;
|
|
cp = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
|
|
while (*s1 == *cp++)
|
|
if (*s1++=='\0') {
|
|
return(sp); /* found */
|
|
}
|
|
if (p->next == 0)
|
|
return(0); /* not found */
|
|
else
|
|
continue; /* next symbol */
|
|
} while (p = &LM2LP(lmp)->lp_hash[p->next]);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Lookup symbol with name (s). Return pointer to symbol table entry
|
|
* representing it as well as index to dynamic object which defines it.
|
|
*/
|
|
static
|
|
struct nlist *
|
|
lookup(s, lmpp, clmp)
|
|
char *s;
|
|
struct link_map **lmpp;
|
|
struct link_map *clmp;
|
|
{
|
|
register struct link_map *lmp; /* working pointer */
|
|
register char *cp; /* string handling pointers */
|
|
register char *s1; /* string handling pointers */
|
|
struct nlist *sp; /* symbol table entry */
|
|
struct nlist *savesp; /* save common data symbol entry */
|
|
int msize = 0; /* common maximum size */
|
|
struct rtc_symb *rs; /* new common */
|
|
struct rtc_symb *trs; /* temporary for new common */
|
|
static struct nlist *lsp; /* cached copy of last symbol found */
|
|
static struct link_map *lmpc; /* cached link map pointer */
|
|
|
|
/*
|
|
* Heuristic: if this symbol is the same as the last one, then
|
|
* just skip all this work.
|
|
* XXX should count this to see how effective it is.
|
|
*/
|
|
if (cached_symbol)
|
|
if (!strcmp(cached_symbol, s)) {
|
|
*lmpp = lmpc;
|
|
return(lsp);
|
|
}
|
|
cached_symbol = s;
|
|
|
|
/*
|
|
* Over all loaded objects, determine find the hash entry corresponding
|
|
* to this value, and search for a definition for this symbol.
|
|
*/
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next) {
|
|
lmpc = lmp;
|
|
if (lsp = sp = (*(LM2LP(lmp)->lp_interp))(lmp, s, clmp)) {
|
|
/*
|
|
* We found a match. If it is simply a reference
|
|
* and not a definition we proceed to the next object.
|
|
* If it is a definition, and not a common then we
|
|
* have found what we're looking for so return it.
|
|
* Otherwise, determine whether the common has already
|
|
* been allocated, and if so, return that pointer.
|
|
*/
|
|
if (sp->n_value == 0)
|
|
continue;
|
|
if (sp->n_type != N_EXT+N_UNDF) {
|
|
if (msize == 0) {
|
|
*lmpp = lmp;
|
|
return (sp);
|
|
}
|
|
} else {
|
|
/*
|
|
* look up the runtime allocated commom
|
|
* symbol table
|
|
*/
|
|
savesp = sp;
|
|
trs = rtcp;
|
|
while (trs) {
|
|
s1 = s;
|
|
cp = trs->rtc_sp->n_un.n_name;
|
|
while (*s1 == *cp++)
|
|
if (*s1++=='\0')
|
|
return (lsp = trs->rtc_sp);
|
|
trs = trs->rtc_next;
|
|
}
|
|
|
|
/*
|
|
* It's an unallocated common,
|
|
* accumulate size information.
|
|
*/
|
|
if (msize < sp->n_value)
|
|
msize = sp->n_value;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we got this far, we either have found nothing suitable or
|
|
* else have a common. In the former, punt, otherwise we now
|
|
* know enough to allocate the common so do so.
|
|
*/
|
|
if (msize == 0) {
|
|
return (0);
|
|
} else {
|
|
rs = (struct rtc_symb *) rtmalloc(sizeof (struct rtc_symb));
|
|
rs->rtc_sp = (struct nlist *)rtmalloc(sizeof (struct nlist));
|
|
trs = rtcp;
|
|
rtcp = rs;
|
|
rs->rtc_next = trs;
|
|
*(rs->rtc_sp) = *savesp;
|
|
rs->rtc_sp->n_un.n_name = rtmalloc(strlen(s) + 1);
|
|
strcpy(rs->rtc_sp->n_un.n_name, s);
|
|
rs->rtc_sp->n_type = N_COMM;
|
|
rs->rtc_sp->n_value = (long)rtmalloc(msize);
|
|
*lmpp = lmpc = hlmp;
|
|
return (lsp = rs->rtc_sp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Procedure call binding. Called on initial call reference to a global
|
|
* unbound symbol.
|
|
*/
|
|
int
|
|
binder(pc, relocindex)
|
|
caddr_t pc;
|
|
int relocindex;
|
|
{
|
|
int address; /* target address */
|
|
register struct link_map *lmp; /* link_map describing calling obj */
|
|
register struct /* working relocation pointer */
|
|
relocation_info *rp;
|
|
register struct nlist *sp; /* entry for symbol being referenced */
|
|
register char *symbol; /* symbol being searched for */
|
|
struct link_map *llmp; /* link_map in which symbol is found */
|
|
|
|
/*
|
|
* Find the object that our caller came from.
|
|
*/
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next)
|
|
if (pc > (caddr_t)(LM2LP(lmp)->lp_plt) &&
|
|
pc < (caddr_t)((int)LM2LP(lmp)->lp_plt +
|
|
lmp->lm_ld->v2->ld_plt_sz))
|
|
goto gotit;
|
|
/*
|
|
* If no object can be found, we can't understand how we got
|
|
* here, so we panic.
|
|
*/
|
|
if (lmp == NULL)
|
|
panic("ld.so: unidentifiable procedure reference at 0x%x\n",
|
|
pc);
|
|
|
|
gotit:
|
|
/*
|
|
* Need to know which link_dynamic version for plt handling.
|
|
* This interface will be revised post 4.0 ++++++
|
|
*/
|
|
#if TARGET==SUN4
|
|
#define LAST22BITS 0x3fffff
|
|
version_no = lmp->lm_ld->ld_version;
|
|
if (version_no == 3)
|
|
relocindex = *(int *)(pc + 4) & LAST22BITS;
|
|
#endif
|
|
/*
|
|
* Find the symbol being referenced, and then find a definition for it.
|
|
* If the latter can not be found, then terminate the program.
|
|
*/
|
|
/*
|
|
* N.B. Different error handling possibilities are a CASE
|
|
* opportunity: namely, incremental program construction and
|
|
* calls to debuggers.
|
|
*/
|
|
rp = &LM2LP(lmp)->lp_rp[relocindex];
|
|
sp = &LM2LP(lmp)->lp_symtab[rp->r_symbolnum];
|
|
symbol = &LM2LP(lmp)->lp_symstr[sp->n_un.n_strx];
|
|
if ((sp = lookup(symbol, &llmp, lmp)) == NULL)
|
|
panic("ld.so: call to undefined procedure %s from 0x%x\n",
|
|
symbol, pc);
|
|
|
|
/*
|
|
* Rebuild the relocation entry to direct future calls directly
|
|
* to the target procedure. Return the address to which this call
|
|
* should ultimately return.
|
|
*/
|
|
/*
|
|
* N.B. Much opportunity here for:
|
|
* - call graph profiling.
|
|
* - alternative procedure call implementation (RPC).
|
|
* - value-added interposing.
|
|
*/
|
|
address = (int)&LM2LP(llmp)->lp_symbol_base[sp->n_value];
|
|
stuffit((long *)&lmp->lm_addr[rp->r_address], address);
|
|
return (address);
|
|
}
|
|
|
|
/*
|
|
* Rebuild a _PLT_ entry after initial binding.
|
|
*/
|
|
stuffit(where, what)
|
|
long *where;
|
|
int what;
|
|
{
|
|
#if TARGET==SUN4
|
|
long *i = where;
|
|
|
|
switch (version_no) {
|
|
case 3:
|
|
*i = SETHI;
|
|
*i = (*i & ~MASK(22)) | ((what>>(32-22)) & MASK(22));
|
|
iflush(i);
|
|
i++;
|
|
*i = JMPI;
|
|
*i = (*i & ~MASK(10)) | (what & MASK(10));
|
|
iflush(i);
|
|
break;
|
|
default:
|
|
*i = SETHI;
|
|
*i = (*i & ~MASK(22)) | ((what>>(32-22)) & MASK(22));
|
|
iflush(i);
|
|
i++;
|
|
*i = ORIT;
|
|
*i = (*i & ~MASK(10)) | (what & MASK(10));
|
|
iflush(i);
|
|
*i++;
|
|
*i = JMPI;
|
|
iflush(i);
|
|
i++;
|
|
*i = NOP;
|
|
iflush(i);
|
|
break;
|
|
}
|
|
#endif
|
|
#if TARGET==SUN3 || TARGET==SUN2
|
|
char *i = (char *)where;
|
|
|
|
*where = what;
|
|
*(short *)(i - 2) = JUMP;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Utility function to round "v" to the next "r" boundary.
|
|
*/
|
|
round(v, r)
|
|
u_int v;
|
|
u_int r;
|
|
{
|
|
r--;
|
|
v += r;
|
|
v &= ~(long)r;
|
|
return (v);
|
|
}
|
|
|
|
/*
|
|
* Make sure we have a source of heap.
|
|
*/
|
|
static
|
|
get_zero_object()
|
|
{
|
|
int zfd;
|
|
|
|
if (devzero_fd == -1)
|
|
devzero_fd = open("/dev/zero", O_RDONLY);
|
|
return (devzero_fd != -1);
|
|
}
|
|
|
|
/*
|
|
* Local heap allocator. Very simple, does not support storage freeing.
|
|
* XXX Should be upgraded to support multiple heaps. Should also handle
|
|
* /dev/zero allocation errors.
|
|
*/
|
|
caddr_t
|
|
rtmalloc(nb)
|
|
int nb;
|
|
{
|
|
static caddr_t cp = 0;
|
|
static caddr_t sp = 0;
|
|
caddr_t tp;
|
|
struct rlimit rlimit;
|
|
int sl;
|
|
|
|
if (cp == 0) {
|
|
getrlimit(RLIMIT_STACK, &rlimit);
|
|
sl = rlimit.rlim_cur - (rlimit.rlim_cur % PAGSIZ);
|
|
sp = cp = (caddr_t)(top_of_stack - sl);
|
|
}
|
|
if (cp + round(nb, (long)sizeof (double)) >= sp) {
|
|
if (!get_zero_object())
|
|
map_error("map heap", "/dev/zero");
|
|
if ((sp = mmap(sp, round(nb, PAGSIZ), PROT_READ | PROT_WRITE,
|
|
MAP_FIXED | MAP_PRIVATE, devzero_fd, 0)) == (caddr_t)-1)
|
|
map_error("map heap", "/dev/zero");
|
|
sp += round(nb,PAGSIZ);
|
|
}
|
|
tp = cp;
|
|
cp += round(nb, sizeof (double));
|
|
return (tp);
|
|
}
|
|
|
|
/*
|
|
* Run-time link editor private getenv system call. Scans for our
|
|
* special strings, and also calculates top of stack.
|
|
*/
|
|
char ld_library_path[] = "LD_LIBRARY_PATH";
|
|
char ld_trace[] = "LD_TRACE_LOADED_OBJECTS";
|
|
char ld_profile[] = "LD_PROFILE";
|
|
char ld_preload[] = "LD_PRELOAD";
|
|
char ld_symbols_public[]= "LD_SYMBOLS_PUBLIC";
|
|
|
|
static
|
|
rtgetenv()
|
|
{
|
|
register char **p = environ;
|
|
register char *v;
|
|
|
|
/*
|
|
* Guard against no environment. This seems practically impossible,
|
|
* as the variable "environ" is initialized in crt0 with an address
|
|
* on the stack. However, if it actually does happen, we need to
|
|
* fake out the top of user_stack.
|
|
*/
|
|
if ((p == NULL) || (*p == NULL)) {
|
|
top_of_stack = (caddr_t)(((int)&p + PAGSIZ - 1)
|
|
& ~(PAGSIZ - 1));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Scan for LD_ environment variables that affect our behavior.
|
|
*/
|
|
while (v = *p++) {
|
|
if (strncmp(v, "LD_", 3))
|
|
continue;
|
|
if (!strncmp(v, ld_library_path, sizeof (ld_library_path) - 1))
|
|
library_path = v + sizeof (ld_library_path);
|
|
else if (!strncmp(v, ld_preload, sizeof (ld_preload) - 1))
|
|
preload = v + sizeof (ld_preload);
|
|
else if (!strncmp(v, ld_trace, sizeof (ld_trace) - 1))
|
|
tracing = v + sizeof (ld_trace);
|
|
else if (!strncmp(v, ld_profile, sizeof (ld_profile) - 1))
|
|
profile = v + sizeof (ld_profile);
|
|
else if (!strncmp(v, ld_symbols_public,
|
|
sizeof (ld_symbols_public) - 1))
|
|
symbols_public = v + sizeof (ld_symbols_public);
|
|
}
|
|
|
|
/*
|
|
* Calculate the top of stack -- avoid dependence upon USRSTACK,
|
|
* but add dependence upon knowing that the stack ends with environment
|
|
* strings. Note: program bootstraps already depend upon this
|
|
* knowledge, and since we're sort of kind of part of the bootstrap
|
|
* this isn't too much of a hack.
|
|
*/
|
|
for (v = *(p - 2); *v; v++)
|
|
;;
|
|
top_of_stack = (caddr_t)(((int)v + PAGSIZ - 1) & ~(PAGSIZ - 1));
|
|
}
|
|
|
|
/*
|
|
* Simple programmatic interface to the dynamic linker.
|
|
*
|
|
* These functions constitute a simple interface that permits programs to
|
|
* add shared objects, lookup symbols in such objects, and remove those
|
|
* objects -- all under program control. Such objects have special
|
|
* symbol lookup interpretations, implemented by interpreter functions
|
|
* such as the one immediately following.
|
|
*/
|
|
struct nlist *
|
|
dl_interpreter(lmp, s, clmp)
|
|
struct link_map *lmp;
|
|
char *s;
|
|
struct link_map *clmp;
|
|
{
|
|
if (LM2LP(lmp)->lp_dlh != LM2LP(clmp)->lp_dlh)
|
|
return(0);
|
|
else
|
|
return(findsb(lmp, s, clmp));
|
|
}
|
|
|
|
/*
|
|
* Add the shared object in "path" to the program. Reference count the
|
|
* objects so that multiple calls actually load only one instance of
|
|
* the object -- also account for objects that might have been automatically
|
|
* loaded at program start-up.
|
|
*/
|
|
struct dl_object *
|
|
dlopen(path, mode)
|
|
char *path; /* path name of object */
|
|
int mode; /* open mode */
|
|
{
|
|
struct dl_object *dlp; /* temporary return */
|
|
|
|
/*
|
|
* Jacket function for real worker routine. Establishes and
|
|
* cleans up general environment.
|
|
*/
|
|
dlp = dlopen_worker(path, mode);
|
|
if (devzero_fd != -1) {
|
|
(void) close(devzero_fd);
|
|
devzero_fd = -1;
|
|
}
|
|
return (dlp);
|
|
}
|
|
|
|
static struct dl_object *
|
|
dlopen_worker(path, mode)
|
|
char *path; /* path name of object */
|
|
int mode; /* open mode */
|
|
{
|
|
struct link_map *lmp; /* link map temporary */
|
|
struct link_map *nlmp; /* newly created link maps */
|
|
struct dl_object *dlp; /* dl object temporary */
|
|
struct dl_object *wdlp; /* working dl temporary */
|
|
struct dl_object **dlpp; /* insertion pointer for dependents */
|
|
struct link_object *lop; /* dependent link objects */
|
|
char *save_error; /* error saving */
|
|
static int once = 1; /* one-time only initialization */
|
|
|
|
/*
|
|
* Initialize error state.
|
|
*/
|
|
dl_error = DLE_none;
|
|
|
|
/*
|
|
* Currently, no modes supported.
|
|
*/
|
|
if (mode != 1) {
|
|
dl_error = DLE_mode_error;
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* If this is our first time, then we'll allocate dl pointers for
|
|
* every link map in the program. However, these dl pointers will
|
|
* be "private" to "main" -- used only to allow dlsym() to work.
|
|
* Thus, after allocating all but the "head" dl pointer, we'll
|
|
* clear the back pointer to the dl pointer from each link map.
|
|
*/
|
|
if (once) {
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next) {
|
|
if (once) {
|
|
dlp = new_dlp(lmp);
|
|
dlpp = &dlp->dl_dep;
|
|
once = 0;
|
|
} else {
|
|
*dlpp = new_dlp(lmp);
|
|
dlpp = &(*dlpp)->dl_dep;
|
|
LM2LP(lmp)->lp_dlp = NULL;
|
|
}
|
|
LM2LP(lmp)->lp_refcnt++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If a null path, then we're operating on "main." Simply return
|
|
* the dl pointer for "main".
|
|
*/
|
|
if (!path)
|
|
return (LM2LP(hlmp)->lp_dlp);
|
|
|
|
/*
|
|
* Scan link map list looking for this object. If found,
|
|
* then get a dl_object descriptor for it (creating the
|
|
* descriptor if necessary.) Note that the situation in
|
|
* which we find an object with a descriptor, but where
|
|
* that object is either not on a dl "chain" (because
|
|
* it's part of "main") or is not the head of that chain,
|
|
* then we have a conflict. The conflict is the result
|
|
* of having two "libraries" be depended upon by two different
|
|
* shared objects, leading to confusion over how the resolution
|
|
* of symbols should be handled. We simply prohibit this.
|
|
*/
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next)
|
|
if (!strcmp(path, lmp->lm_name)) {
|
|
dlp = LM2LP(lmp)->lp_dlp;
|
|
if (dlp) {
|
|
if ((LM2LP(lmp)->lp_dlh != NULL) &&
|
|
(LM2LP(lmp)->lp_dlh != dlp)) {
|
|
dl_error = DLE_conflict;
|
|
return (NULL);
|
|
}
|
|
dlp->dl_refcnt++;
|
|
} else {
|
|
LM2LP(lmp)->lp_refcnt++;
|
|
dlp = new_dlp(lmp);
|
|
}
|
|
return (dlp);
|
|
}
|
|
|
|
/*
|
|
* No link map for it. Make one, and then make a
|
|
* dl_object descriptor for it. Add it to the
|
|
* address space, set it as the head of a chain.
|
|
*/
|
|
if (!(lmp = mapit(path, 0, 0, dl_interpreter))) {
|
|
dl_error = DLE_can_not_open;
|
|
return (NULL);
|
|
}
|
|
dlp = new_dlp(lmp);
|
|
LM2LP(lmp)->lp_dlh = dlp;
|
|
dlpp = &dlp->dl_dep;
|
|
|
|
/*
|
|
* Now, see if there are any dependent objects. For each,
|
|
* load it up, get a dlp, etc. and add it to the list of
|
|
* dependencies.
|
|
*/
|
|
for (wdlp = dlp; wdlp; wdlp = wdlp->dl_dep) {
|
|
lmp = wdlp->dl_lmp;
|
|
if (lmp->lm_ld) {
|
|
for (lop = (struct link_object *)
|
|
&TEXTBASE(lmp)[lmp->lm_ld->v2->ld_need];
|
|
lop != (struct link_object *)&TEXTBASE(lmp)[0];
|
|
lop = (struct link_object *)
|
|
&TEXTBASE(lmp)[lop->lo_next])
|
|
if ((nlmp =
|
|
have_we_got_it(lop, TEXTBASE(lmp))) ==
|
|
NULL) {
|
|
if (nlmp = map_so(lmp, lop)) {
|
|
*dlpp = new_dlp(nlmp);
|
|
LM2LP(nlmp)->lp_interp =
|
|
dl_interpreter;
|
|
LM2LP(nlmp)->lp_dlh = dlp;
|
|
dlpp = &(*dlpp)->dl_dep;
|
|
} else {
|
|
dl_error = DLE_can_not_open;
|
|
break;
|
|
}
|
|
} else
|
|
if (LM2LP(nlmp)->lp_dlh) {
|
|
dl_error = DLE_conflict;
|
|
break;
|
|
}
|
|
}
|
|
if (dl_error != DLE_none)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If an error occurred, we're hosed. Close off everything we
|
|
* opened including ourselves.
|
|
*/
|
|
if (dl_error != DLE_none) {
|
|
save_error = dl_error;
|
|
(void) dlclose(dlp);
|
|
dl_error = save_error;
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Now, walk the list and relocate everything.
|
|
*/
|
|
for (wdlp = dlp; wdlp; wdlp = wdlp->dl_dep) {
|
|
relocate(wdlp->dl_lmp, wdlp->dl_lmp);
|
|
if (wdlp->dl_lmp->lm_rwt) {
|
|
(void) mprotect(TEXTBASE(wdlp->dl_lmp),
|
|
wdlp->dl_lmp->lm_ld->v2->ld_text,
|
|
PROT_READ | PROT_EXEC);
|
|
wdlp->dl_lmp->lm_rwt = 0;
|
|
}
|
|
}
|
|
return (dlp);
|
|
}
|
|
|
|
/*
|
|
* dlsym lookup routine: does single symbol table lookup, and filters
|
|
* out entries that are undefined.
|
|
*/
|
|
static struct nlist *
|
|
dl_lookup(lmp, sn, clmp)
|
|
struct link_map *lmp;
|
|
char *sn;
|
|
struct link_map *clmp;
|
|
{
|
|
struct nlist *sp;
|
|
|
|
sp = findsb(lmp, sn, clmp);
|
|
if (sp != NULL)
|
|
if ((sp->n_type == N_UNDF+N_EXT) && (sp->n_value == 0))
|
|
sp = NULL;
|
|
return (sp);
|
|
}
|
|
|
|
/*
|
|
* Convert dlsym responses to appropriate address based on type of
|
|
* symbol. Note that in the case of dlsym references to an unallocated
|
|
* common, we have to "simulate" a reference to it in order to stimulate
|
|
* the allocation process.
|
|
*/
|
|
static caddr_t
|
|
dl_absolute(sp, lmp)
|
|
struct nlist *sp; /* symbol table entry of symbol */
|
|
struct link_map *lmp; /* link map that defined it */
|
|
{
|
|
|
|
if (sp->n_type == N_UNDF+N_EXT && sp->n_value != 0) {
|
|
sp = lookup(&LM2LP(lmp)->lp_symstr[sp->n_un.n_strx], &lmp, lmp);
|
|
if (sp == NULL)
|
|
return (0);
|
|
}
|
|
if (sp->n_type == N_COMM || sp->n_type == N_ABS+N_EXT)
|
|
return ((caddr_t)sp->n_value);
|
|
else
|
|
return ((caddr_t)(sp->n_value + lmp->lm_addr));
|
|
}
|
|
|
|
/*
|
|
* dlsym: lookup a symbol in a dlp and/or its dependents.
|
|
*/
|
|
#define MAX_DLSYM_SYMBOL 1024 /* maximum size of symbol we match */
|
|
caddr_t
|
|
dlsym(dlp, symbol)
|
|
struct dl_object *dlp;
|
|
char *symbol;
|
|
{
|
|
int i; /* temporary */
|
|
caddr_t pc; /* caller's pc */
|
|
struct dl_object *wdlp; /* working dlp */
|
|
struct link_map *lmp; /* link map of symbol table */
|
|
struct nlist *sp; /* symbol table entry */
|
|
char *cp; /* temporary */
|
|
char buffer[MAX_DLSYM_SYMBOL + 2];
|
|
/* "_" assembly, including null pad */
|
|
|
|
/*
|
|
* Initialize error.
|
|
*/
|
|
dl_error = DLE_none;
|
|
|
|
/*
|
|
* Check dl object handle -- if valid, then retrieve
|
|
* symbol from appropriate object. Note that dlp of
|
|
* NULL is valid, and refers to the "caller" of dlsym.
|
|
*/
|
|
if (dlp) {
|
|
if (!valid_dl_object(dlp))
|
|
return (NULL);
|
|
} else {
|
|
#ifdef notdef
|
|
pc = caller();
|
|
if ((lmp = pc_to_lmp(pc)) == NULL) {
|
|
dl_error = DLE_bad_handle;
|
|
return (NULL);
|
|
}
|
|
#else notdef
|
|
dl_error = DLE_bad_handle; /* XXX */
|
|
return (NULL);
|
|
#endif notdef
|
|
}
|
|
for (wdlp = dlp; wdlp; wdlp = wdlp->dl_dep)
|
|
if (sp = dl_lookup(wdlp->dl_lmp, symbol, wdlp->dl_lmp))
|
|
return (dl_absolute(sp, wdlp->dl_lmp));
|
|
|
|
/*
|
|
* Symbol not found as supplied. However, most of our symbols
|
|
* will be in the "C" name space, where the implementation prepends
|
|
* a "_" to the symbol as it emits it. Therefore, attempt to find
|
|
* the symbol with the "_" prepend.
|
|
*/
|
|
buffer[0] = '_';
|
|
cp = &buffer[1];
|
|
i = 1;
|
|
while (i < MAX_DLSYM_SYMBOL) {
|
|
if ((*cp++ = *symbol++) == '\0') {
|
|
for (wdlp = dlp; wdlp; wdlp = wdlp->dl_dep)
|
|
if (sp = dl_lookup(wdlp->dl_lmp, buffer,
|
|
wdlp->dl_lmp))
|
|
return (dl_absolute(sp,
|
|
wdlp->dl_lmp));
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
dl_error = DLE_undefined;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Functions to retrieve a symbol from a dlopen'ed object.
|
|
*/
|
|
static struct link_map *
|
|
pc_to_lmp(pc)
|
|
caddr_t pc; /* pc needing an lmp */
|
|
{
|
|
int size; /* size of program */
|
|
struct link_map *lmp; /* temporary */
|
|
|
|
size = max(SIZE(*(struct exec *)lmp->lm_addr),
|
|
N_SYMOFF((*(struct exec *)lmp->lm_addr)) +
|
|
sizeof (struct nlist));
|
|
for (lmp = hlmp; lmp; lmp = lmp->lm_next)
|
|
if (pc > lmp->lm_addr &&
|
|
pc < (lmp->lm_addr + size))
|
|
break;
|
|
return (lmp);
|
|
}
|
|
|
|
/*
|
|
* Remove a dynamically loaded object from the program. If it is
|
|
* the last reference, then *really* remove the name -- and if the
|
|
* name is the last reference to the object, then *really* remove
|
|
* the object.
|
|
*/
|
|
int
|
|
dlclose(dlp)
|
|
struct dl_object *dlp;
|
|
{
|
|
struct link_map *lmp; /* temporary */
|
|
struct dl_object *wdlp; /* temporary */
|
|
struct dl_object *ndlp; /* where we're going next */
|
|
|
|
/*
|
|
* Clear error.
|
|
*/
|
|
dl_error = DLE_none;
|
|
|
|
/*
|
|
* Validity check object handle.
|
|
*/
|
|
if (!valid_dl_object(dlp)) {
|
|
dl_error = DLE_bad_handle;
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Valid object: is it the one for "main"? If so, simply
|
|
* return success -- this dl pointer is never deallocated
|
|
* and is always valid. (That it is "valid" is probably an
|
|
* error, technically, but this corner case should not matter.)
|
|
*/
|
|
if (dlp->dl_lmp == hlmp)
|
|
return (0);
|
|
|
|
/*
|
|
* Valid object: decrement reference count. If we've still
|
|
* got any references then just leave.
|
|
*/
|
|
if (--dlp->dl_refcnt)
|
|
return (0);
|
|
|
|
/*
|
|
* Reference count went to zero. By definition, that means
|
|
* that the reference count on everything we loaded also
|
|
* went to zero. So free them all as well.
|
|
*/
|
|
for (wdlp = dlp; wdlp; wdlp = ndlp) {
|
|
lmp = wdlp->dl_lmp;
|
|
ndlp = wdlp->dl_dep;
|
|
wdlp->dl_refcnt = 0;
|
|
free_dlp(wdlp);
|
|
if (--LM2LP(lmp)->lp_refcnt)
|
|
continue;
|
|
free_lmp(lmp);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
char *
|
|
dlerror()
|
|
{
|
|
char *error = dl_error;
|
|
|
|
if (dl_error == DLE_none)
|
|
return (0);
|
|
dl_error = DLE_none;
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* dl_object allocation and management. "free'ed" dl_objects are not
|
|
* returned to the "heap" (because we don't have a general heap manager
|
|
* at present.) So, they are kept on a list in case we need to use them
|
|
* and requests for new dl_object's are allocated from this list rather
|
|
* than from the heap.
|
|
*/
|
|
static struct dl_object *
|
|
new_dlp(lmp)
|
|
struct link_map *lmp;
|
|
{
|
|
struct dl_object *dlp;
|
|
|
|
if (dlp = fdlp)
|
|
fdlp = dlp->dl_next;
|
|
else
|
|
dlp = (struct dl_object *)
|
|
rtmalloc(sizeof (struct dl_object));
|
|
dlp->dl_magic = DL_MAGIC;
|
|
dlp->dl_cigam = DL_CIGAM;
|
|
LM2LP(lmp)->lp_dlp = dlp;
|
|
dlp->dl_lmp = lmp;
|
|
dlp->dl_refcnt++;
|
|
dlp->dl_next = NULL;
|
|
dlp->dl_dep = NULL;
|
|
*pdlpp = dlp;
|
|
pdlpp = &dlp->dl_next;
|
|
return (dlp);
|
|
}
|
|
|
|
static void
|
|
free_dlp(dlp)
|
|
struct dl_object *dlp;
|
|
{
|
|
struct dl_object *tdlp; /* temporary */
|
|
struct dl_object **pdlp; /* previous pointer */
|
|
|
|
tdlp = hdlp, pdlp = &hdlp;
|
|
while (tdlp) {
|
|
if (tdlp == dlp) {
|
|
LM2LP(dlp->dl_lmp)->lp_dlp = NULL;
|
|
LM2LP(dlp->dl_lmp)->lp_dlh = NULL;
|
|
dlp->dl_magic = 0;
|
|
dlp->dl_cigam = 0;
|
|
*pdlp = dlp->dl_next;
|
|
if (pdlpp == &dlp->dl_next)
|
|
pdlpp = pdlp;
|
|
dlp->dl_next = fdlp;
|
|
fdlp = dlp;
|
|
return;
|
|
}
|
|
pdlp = &tdlp->dl_next;
|
|
tdlp = tdlp->dl_next;
|
|
}
|
|
panic("ld.so: mangled dl object list.\n");
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* Sanity check a program-provided dl_object handle.
|
|
*/
|
|
static
|
|
valid_dl_object(dlp)
|
|
struct dl_object *dlp;
|
|
{
|
|
if (dlp)
|
|
if (dlp->dl_magic == DL_MAGIC &&
|
|
dlp->dl_cigam == DL_CIGAM &&
|
|
dlp->dl_refcnt != 0 &&
|
|
(LM2LP(dlp->dl_lmp)->lp_dlh == NULL ||
|
|
(LM2LP(dlp->dl_lmp)->lp_dlh != NULL &&
|
|
LM2LP(dlp->dl_lmp)->lp_dlh == dlp)))
|
|
return (1);
|
|
dl_error = DLE_bad_handle;
|
|
return (0);
|
|
}
|
|
|
|
#ifdef WHOAMI
|
|
/*
|
|
* This routine is only available in-house. It returns the
|
|
* program name of the running process.
|
|
*/
|
|
char *
|
|
whoami()
|
|
{
|
|
return(progname);
|
|
}
|
|
#endif
|
|
|