/* @(#)rtld.c 1.1 94/10/31 SMI */ /* * Run-time link editor. */ /* * Copyright (c) 1989, 1990, 1991, 1992 by Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #include "dynamic.h" #include "reloc_info.h" #include "rtld.h" #include #include "cache.h" #include #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