diff options
author | jdp <jdp@FreeBSD.org> | 1996-01-13 00:15:25 +0000 |
---|---|---|
committer | jdp <jdp@FreeBSD.org> | 1996-01-13 00:15:25 +0000 |
commit | b74213a26d074f6e24d5e78eb4a20d226cee9c79 (patch) | |
tree | 3432e9ed6811db131bf29547809583ae06d2a2ec /gnu | |
parent | 41258e18430154b46256126d760b163c30d5de17 (diff) | |
download | FreeBSD-src-b74213a26d074f6e24d5e78eb4a20d226cee9c79.zip FreeBSD-src-b74213a26d074f6e24d5e78eb4a20d226cee9c79.tar.gz |
This release is a moderate restructuring of the dynamic linker.
It addresses a number of problems that were present in earlier
versions.
The calls to the "init" and "fini" functions of shared libraries
have been reordered, so that they are called in a strictly nested
fashion, as is required for C++ constructors and destructors. In
addition, the "init" functions are called in better order relative
to each other. That makes the system more tolerant of C++ programs
which depend on a library's being initialized before its clients.
The dynamic linker is now more tolerant of shared libraries in
which dependencies on other shared libraries are incompletely
recorded.
Cleanup in the event of errors has been improved throughout the
dynamic linker. A number of memory leaks were eliminated.
The warning message for a shared library whose minor version number
is too old has been clarified.
The code dealing with the "ld.so.hints" file has been cleaned up.
A bug that caused the hints file to be unmapped incompletely has
been fixed. A different bug that could potentially cause the hints
file to be mapped on top of a loaded object has been fixed.
The code that searches for shared libraries has been cleaned up.
The searching is now more compatible with that done by SunOS and
SVR4. Also, some unnecessary and useless searches of both the
hints file and library directories have been eliminated.
Reviewed by: nate@freebsd.org
Diffstat (limited to 'gnu')
-rw-r--r-- | gnu/usr.bin/ld/rtld/rtld.c | 1197 |
1 files changed, 605 insertions, 592 deletions
diff --git a/gnu/usr.bin/ld/rtld/rtld.c b/gnu/usr.bin/ld/rtld/rtld.c index 2fd7d4f..3300538 100644 --- a/gnu/usr.bin/ld/rtld/rtld.c +++ b/gnu/usr.bin/ld/rtld/rtld.c @@ -27,7 +27,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: rtld.c,v 1.31 1995/10/27 22:01:00 jdp Exp $ + * $Id: rtld.c,v 1.31.1.5 1995/11/28 01:15:45 jdp Exp jdp $ */ #include <sys/param.h> @@ -73,17 +73,28 @@ #endif /* + * Structure for building a list of shared objects. + */ +struct so_list { + struct so_map *sol_map; /* Link map for shared object */ + struct so_list *sol_next; /* Next entry in the list */ +}; + +/* * Loader private data, hung off <so_map>->som_spd */ struct somap_private { int spd_version; struct so_map *spd_parent; + struct so_list *spd_children; + struct so_map *spd_prev; int spd_refcount; int spd_flags; -#define RTLD_MAIN 1 -#define RTLD_RTLD 2 -#define RTLD_DL 4 -#define RTLD_INIT 8 +#define RTLD_MAIN 0x01 +#define RTLD_RTLD 0x02 +#define RTLD_DL 0x04 +#define RTLD_INIT 0x08 +#define RTLD_TRACED 0x10 unsigned long a_text; /* text size, if known */ unsigned long a_data; /* initialized data size */ unsigned long a_bss; /* uninitialized data size */ @@ -152,9 +163,10 @@ static char *main_progname = __main_progname; static char us[] = "/usr/libexec/ld.so"; static int anon_fd = -1; static char *ld_library_path; +static int tracing; -struct so_map *link_map_head, *main_map; -struct so_map **link_map_tail = &link_map_head; +struct so_map *link_map_head; +struct so_map *link_map_tail; struct rt_symbol *rt_symbol_head; static void *__dlopen __P((char *, int)); @@ -168,24 +180,26 @@ static struct ld_entry ld_entry = { }; void xprintf __P((char *, ...)); -static void load_objects __P(( struct crt_ldso *, - struct _dynamic *)); -static struct so_map *map_object __P((struct sod *, struct so_map *)); -static int unmap_object __P((struct so_map *)); -static struct so_map *load_object __P((struct sod *, struct so_map *, - int, int)); -static int unload_object __P((struct so_map *)); +static struct so_map *map_object __P(( char *, + struct sod *, + struct so_map *)); +static int map_sods __P((struct so_map *)); +static int reloc_and_init __P((struct so_map *)); +static void unmap_object __P((struct so_map *, int)); static struct so_map *alloc_link_map __P(( char *, struct sod *, struct so_map *, caddr_t, struct _dynamic *)); +static void free_link_map __P((struct so_map *)); static inline int check_text_reloc __P(( struct relocation_info *, struct so_map *, caddr_t)); static int reloc_map __P((struct so_map *)); static void reloc_copy __P((struct so_map *)); -static void init_map __P((struct so_map *, char *, int)); -static void call_map __P((struct so_map *, char *)); -static char *rtfindlib __P((char *, int, int, int *)); +static void init_object __P((struct so_map *)); +static void init_sods __P((struct so_list *)); +static int call_map __P((struct so_map *, char *)); +static char *findhint __P((char *, int, int *)); +static char *rtfindlib __P((char *, int, int)); void binder_entry __P((void)); long binder __P((jmpslot_t *)); static struct nzlist *lookup __P((char *, struct so_map **, int)); @@ -193,11 +207,9 @@ static inline struct rt_symbol *lookup_rts __P((char *)); static struct rt_symbol *enter_rts __P((char *, long, int, caddr_t, long, struct so_map *)); static void generror __P((char *, ...)); -static void maphints __P((void)); +static int maphints __P((void)); static void unmaphints __P((void)); -static int dl_cascade __P((struct so_map *)); - static inline int strcmp (register const char *s1, register const char *s2) { @@ -222,6 +234,7 @@ struct _dynamic *dp; struct relocation_info *reloc; struct relocation_info *reloc_limit; /* End+1 of relocation */ struct so_debug *ddp; + struct so_map *main_map; struct so_map *smp; /* Check version */ @@ -270,39 +283,45 @@ struct _dynamic *dp; } else ld_library_path = getenv("LD_LIBRARY_PATH"); - /* Setup directory search */ - add_search_path(ld_library_path); + tracing = getenv("LD_TRACE_LOADED_OBJECTS") != NULL; + + /* + * Setup the directory search list for findshlib. We use only + * the standard search path. Any extra directories from + * LD_LIBRARY_PATH are searched explicitly, in rtfindlib. + */ std_search_path(); anon_open(); - /* Load required objects into the process address space */ - load_objects(crtp, dp); + + /* Make a link map entry for the main program */ + main_map = alloc_link_map(main_progname, + (struct sod *) NULL, (struct so_map *) NULL, + (caddr_t) 0, crtp->crt_dp); + LM_PRIVATE(main_map)->spd_refcount++; + LM_PRIVATE(main_map)->spd_flags |= RTLD_MAIN; + + /* Make a link map entry for ourselves */ + smp = alloc_link_map(us, + (struct sod *) NULL, (struct so_map *) NULL, + (caddr_t) crtp->crt_ba, dp); + LM_PRIVATE(smp)->spd_refcount++; + LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD; /* Fill in some fields in main's __DYNAMIC structure */ crtp->crt_dp->d_entry = &ld_entry; crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next; - /* Relocate all loaded objects according to their RRS segments */ - for (smp = link_map_head; smp; smp = smp->som_next) { - if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) - continue; - if (reloc_map(smp) < 0) - return -1; - } + /* Map all the shared objects that the main program depends upon */ + if(map_sods(main_map) == -1) + return -1; - /* Copy any relocated initialized data. */ - for (smp = link_map_head; smp; smp = smp->som_next) { - if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) - continue; - reloc_copy(smp); - } + if(tracing) /* We're done */ + exit(0); - /* Call any object initialization routines. */ - for (smp = link_map_head; smp; smp = smp->som_next) { - if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) - continue; - init_map(smp, ".init", 0); - } + /* Relocate and initialize all mapped objects */ + if(reloc_and_init(main_map) == -1) /* Failed */ + return -1; ddp = crtp->crt_dp->d_debug; ddp->dd_cc = rt_symbol_head; @@ -336,81 +355,18 @@ struct _dynamic *dp; } -static void -load_objects(crtp, dp) -struct crt_ldso *crtp; -struct _dynamic *dp; -{ - struct so_map *smp; - int tracing = (int)getenv("LD_TRACE_LOADED_OBJECTS"); - - /* Handle LD_PRELOAD's here */ - - /* Make an entry for the main program */ - smp = alloc_link_map(main_progname, (struct sod *)0, (struct so_map *)0, - (caddr_t)0, crtp->crt_dp); - LM_PRIVATE(smp)->spd_refcount++; - LM_PRIVATE(smp)->spd_flags |= RTLD_MAIN; - - /* Make an entry for ourselves */ - smp = alloc_link_map(us, (struct sod *)0, (struct so_map *)0, - (caddr_t)crtp->crt_ba, dp); - LM_PRIVATE(smp)->spd_refcount++; - LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD; - - for (smp = link_map_head; smp; smp = smp->som_next) { - struct sod *sodp; - long next = 0; - - if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) - continue; - - if (smp->som_dynamic) - next = LD_NEED(smp->som_dynamic); - - while (next) { - struct so_map *newmap; - - sodp = (struct sod *)(LM_LDBASE(smp) + next); - if ((newmap = map_object(sodp, smp)) == NULL) { - if (!tracing) { - errx(1, "%s: %s", main_progname, - __dlerror()); - } - newmap = alloc_link_map(NULL, sodp, smp, 0, 0); - } - LM_PRIVATE(newmap)->spd_refcount++; - next = sodp->sod_next; - } - } - - if (! tracing) - return; - - for (smp = link_map_head; smp; smp = smp->som_next) { - struct sod *sodp; - char *name, *path; - - if ((sodp = smp->som_sod) == NULL) - continue; - name = sodp->sod_name + LM_LDBASE(LM_PARENT(smp)); - - if ((path = smp->som_path) == NULL) - path = "not found"; - - if (sodp->sod_library) - printf("\t-l%s.%d => %s (%p)\n", name, - sodp->sod_major, path, smp->som_addr); - else - printf("\t%s => %s (%p)\n", name, path, smp->som_addr); - } - - exit(0); -} - /* - * Allocate a new link map for shared object NAME loaded at ADDR as a - * result of the presence of link object LOP in the link map PARENT. + * Allocate a new link map and return a pointer to it. + * + * PATH is the pathname of the shared object. + * + * SODP is a pointer to the shared object dependency structure responsible + * for causing the new object to be loaded. PARENT is the shared object + * into which SODP points. Both can be NULL if the new object is not + * being loaded as a result of a shared object dependency. + * + * ADDR is the address at which the object has been mapped. DP is a pointer + * to its _dynamic structure. */ static struct so_map * alloc_link_map(path, sodp, parent, addr, dp) @@ -424,6 +380,10 @@ alloc_link_map(path, sodp, parent, addr, dp) struct somap_private *smpp; size_t smp_size; +#ifdef DEBUG /* { */ + xprintf("alloc_link_map: \"%s\" at %p\n", path, addr); +#endif /* } */ + /* * Allocate so_map and private area with a single malloc. Round * up the size of so_map so the private area is aligned. @@ -434,24 +394,27 @@ alloc_link_map(path, sodp, parent, addr, dp) smp = (struct so_map *)xmalloc(smp_size + sizeof (struct somap_private)); smpp = (struct somap_private *) (((caddr_t) smp) + smp_size); + + /* Link the new entry into the list of link maps */ smp->som_next = NULL; - *link_map_tail = smp; - link_map_tail = &smp->som_next; + smpp->spd_prev = link_map_tail; + if(link_map_tail == NULL) /* First link map entered into list */ + link_map_head = link_map_tail = smp; + else { /* Append to end of list */ + link_map_tail->som_next = smp; + link_map_tail = smp; + } smp->som_addr = addr; - if (path == NULL) - smp->som_path = NULL; - else - smp->som_path = strdup(path); + smp->som_path = strdup(path); smp->som_sod = sodp; smp->som_dynamic = dp; smp->som_spd = (caddr_t)smpp; -/*XXX*/ if (addr == 0) main_map = smp; - smpp->spd_refcount = 0; smpp->spd_flags = 0; smpp->spd_parent = parent; + smpp->spd_children = NULL; smpp->a_text = 0; smpp->a_data = 0; smpp->a_bss = 0; @@ -462,202 +425,411 @@ alloc_link_map(path, sodp, parent, addr, dp) return smp; } - static struct so_map * -find_object(sodp, smp) - struct sod *sodp; +/* + * Remove the specified link map entry from the list of link maps, and free + * the associated storage. + */ + static void +free_link_map(smp) struct so_map *smp; { - char *path, *name = (char *)(sodp->sod_name + LM_LDBASE(smp)); - int usehints = 0; - struct so_map *p; - - if (sodp->sod_library) { - usehints = 1; -again: - path = rtfindlib(name, sodp->sod_major, - sodp->sod_minor, &usehints); - if (path == NULL) { - generror ("Can't find shared library \"%s\"", - name); - return NULL; - } - } else { - if (careful && *name != '/') { - generror ("Shared library path must start with \"/\" for \"%s\"", - name); - return NULL; - } - path = name; - } + struct somap_private *smpp = LM_PRIVATE(smp); - /* Check if already loaded */ - for (p = link_map_head; p; p = p->som_next) - if (p->som_path && strcmp(p->som_path, path) == 0) - break; +#ifdef DEBUG /* { */ + xprintf("free_link_map: \"%s\"\n", smp->som_path); +#endif /* } */ + + if(smpp->spd_prev == NULL) /* Removing first entry in list */ + link_map_head = smp->som_next; + else /* Update link of previous entry */ + smpp->spd_prev->som_next = smp->som_next; + + if(smp->som_next == NULL) /* Removing last entry in list */ + link_map_tail = smpp->spd_prev; + else /* Update back link of next entry */ + LM_PRIVATE(smp->som_next)->spd_prev = smpp->spd_prev; - return p; + free(smp->som_path); + free(smp); } /* - * Map object identified by link object sodp which was found in link - * map smp. Returns a pointer to the link map for the requested object. + * Map the shared object specified by PATH into memory, if it is not + * already mapped. Increment the object's reference count, and return a + * pointer to its link map. + * + * As a special case, if PATH is NULL, it is taken to refer to the main + * program. * - * On failure, it sets an error message that can be retrieved by __dlerror, - * and returns NULL. + * SODP is a pointer to the shared object dependency structure that caused + * this object to be requested. PARENT is a pointer to the link map of + * the shared object containing that structure. For a shared object not + * being mapped as a result of a shared object dependency, these pointers + * should be NULL. An example of this is a shared object that is explicitly + * loaded via dlopen(). + * + * The return value is a pointer to the link map for the requested object. + * If the operation failed, the return value is NULL. In that case, an + * error message can be retrieved by calling dlerror(). */ static struct so_map * -map_object(sodp, smp) +map_object(path, sodp, parent) + char *path; struct sod *sodp; - struct so_map *smp; + struct so_map *parent; { - struct _dynamic *dp; - char *path, *name = (char *)(sodp->sod_name + LM_LDBASE(smp)); - int fd; - caddr_t addr; - struct exec hdr; - int usehints = 0; - struct so_map *p; - struct somap_private *smpp; - - if (sodp->sod_library) { - usehints = 1; -again: - path = rtfindlib(name, sodp->sod_major, - sodp->sod_minor, &usehints); - if (path == NULL) { - generror ("Can't find shared library" - " \"lib%s.so.%d.%d\"", - name, sodp->sod_major, sodp->sod_minor); - return NULL; + struct so_map *smp; + + if(path == NULL) /* Special case for the main program itself */ + smp = link_map_head; + else { + /* Check whether the shared object is already mapped */ + for(smp = link_map_head; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & (RTLD_MAIN|RTLD_RTLD)) + && smp->som_path != NULL + && strcmp(smp->som_path, path) == 0) + break; } - } else { - if (careful && *name != '/') { - generror ("Shared library path must start with \"/\" for \"%s\"", - name); + } + + if (smp == NULL) { /* We must map the object */ + struct _dynamic *dp; + int fd; + caddr_t addr; + struct exec hdr; + struct somap_private *smpp; + + if ((fd = open(path, O_RDONLY, 0)) == -1) { + generror ("open failed for \"%s\" : %s", + path, strerror (errno)); return NULL; } - path = name; - } - /* Check if already loaded */ - for (p = link_map_head; p; p = p->som_next) - if (p->som_path && strcmp(p->som_path, path) == 0) - break; + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + generror ("header read failed for \"%s\"", path); + (void)close(fd); + return NULL; + } - if (p != NULL) - return p; + if (N_BADMAG(hdr)) { + generror ("bad magic number in \"%s\"", path); + (void)close(fd); + return NULL; + } - if ((fd = open(path, O_RDONLY, 0)) == -1) { - if (usehints) { - usehints = 0; - goto again; + /* + * Map the entire address space of the object. It is + * tempting to map just the text segment at first, in + * order to avoid having to use mprotect to change the + * protections of the data segment. But that would not + * be correct. Mmap might find a group of free pages + * large enough to hold the text segment, but not large + * enough for the entire object. When we then mapped + * in the data and BSS segments, they would either be + * non-contiguous with the text segment (if we didn't + * specify MAP_FIXED), or they would map over some + * previously mapped region (if we did use MAP_FIXED). + * The only way we can be sure of getting a contigous + * region that is large enough is to map the entire + * region at once. + */ + if ((addr = mmap(0, hdr.a_text + hdr.a_data + hdr.a_bss, + PROT_READ|PROT_EXEC, + MAP_COPY, fd, 0)) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)close(fd); + return NULL; } - generror ("open failed for \"%s\" : %s", - path, strerror (errno)); - return NULL; - } - if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { - generror ("header read failed for \"%s\"", path); (void)close(fd); - return NULL; - } - if (N_BADMAG(hdr)) { - generror ("bad magic number in \"%s\"", path); - (void)close(fd); - return NULL; + /* Change the data segment to writable */ + if (mprotect(addr + hdr.a_text, hdr.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { + generror ("mprotect failed for \"%s\" : %s", + path, strerror (errno)); + (void)munmap(addr, hdr.a_text + hdr.a_data + hdr.a_bss); + return NULL; + } + + /* Map in pages of zeros for the BSS segment */ + if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_ANON|MAP_COPY|MAP_FIXED, + anon_fd, 0) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)munmap(addr, hdr.a_text + hdr.a_data + hdr.a_bss); + return NULL; + } + + /* Assume _DYNAMIC is the first data item */ + dp = (struct _dynamic *)(addr+hdr.a_text); + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += (long)addr; + + smp = alloc_link_map(path, sodp, parent, addr, dp); + + /* save segment sizes for unmap. */ + smpp = LM_PRIVATE(smp); + smpp->a_text = hdr.a_text; + smpp->a_data = hdr.a_data; + smpp->a_bss = hdr.a_bss; } - if ((addr = mmap(0, hdr.a_text + hdr.a_data + hdr.a_bss, - PROT_READ|PROT_EXEC, - MAP_COPY, fd, 0)) == (caddr_t)-1) { - generror ("mmap failed for \"%s\" : %s", - path, strerror (errno)); - (void)close(fd); - return NULL; + LM_PRIVATE(smp)->spd_refcount++; + if(LM_PRIVATE(smp)->spd_refcount == 1) { /* First use of object */ + /* + * Recursively map all of the shared objects that this + * one depends upon. + */ + if(map_sods(smp) == -1) { /* Failed */ + unmap_object(smp, 0); /* Clean up */ + return NULL; + } } - if (mprotect(addr + hdr.a_text, hdr.a_data, - PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { - generror ("mprotect failed for \"%s\" : %s", - path, strerror (errno)); - (void)close(fd); - return NULL; + return smp; +} + +/* + * Map all of the shared objects that a given object depends upon. PARENT is + * a pointer to the link map for the shared object whose dependencies are + * to be mapped. + * + * Returns 0 on success. Returns -1 on failure. In that case, an error + * message can be retrieved by calling dlerror(). + */ + static int +map_sods(parent) + struct so_map *parent; +{ + struct somap_private *parpp = LM_PRIVATE(parent); + struct so_list **soltail = &parpp->spd_children; + long next = LD_NEED(parent->som_dynamic); + + while(next != 0) { + struct sod *sodp = + (struct sod *) (LM_LDBASE(parent) + next); + char *name = + (char *) (LM_LDBASE(parent) + sodp->sod_name); + char *path = NULL; + struct so_map *smp = NULL; + + if(sodp->sod_library) { + path = rtfindlib(name, sodp->sod_major, + sodp->sod_minor); + if(path == NULL) { + generror ("Can't find shared library" + " \"lib%s.so.%d.%d\"", name, + sodp->sod_major, sodp->sod_minor); + } + } else { + if(careful && name[0] != '/') { + generror("Shared library path must start" + " with \"/\" for \"%s\"", name); + } else + path = strdup(name); + } + + if(path != NULL) + smp = map_object(path, sodp, parent); + + if(tracing && + (smp == NULL || !(LM_PRIVATE(smp)->spd_flags & RTLD_TRACED))) { + if(sodp->sod_library) + printf("\t-l%s.%d => ", name, sodp->sod_major); + else + printf("\t%s => ", name); + + if(path == NULL) + printf("not found\n"); + else if(smp == NULL) + printf("can't load\n"); + else { + printf("%s (%p)\n", path, smp->som_addr); + LM_PRIVATE(smp)->spd_flags |= RTLD_TRACED; + } + } + + if(path != NULL) + free(path); + + if(smp != NULL) { + struct so_list *solp = (struct so_list *) + xmalloc(sizeof(struct so_list)); + solp->sol_map = smp; + solp->sol_next = NULL; + *soltail = solp; + soltail = &solp->sol_next; + } else if(!tracing) + break; + + next = sodp->sod_next; } - if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_ANON|MAP_COPY|MAP_FIXED, - anon_fd, 0) == (caddr_t)-1) { - generror ("mmap failed for \"%s\" : %s", - path, strerror (errno)); - (void)close(fd); - return NULL; + if(next != 0) { + /* + * Oh drat, we have to clean up a mess. + * + * We failed to load a shared object that we depend upon. + * So now we have to unload any dependencies that we had + * already successfully loaded prior to the error. + * + * Cleaning up doesn't matter so much for the initial + * loading of the program, since any failure is going to + * terminate the program anyway. But it is very important + * to clean up properly when something is being loaded + * via dlopen(). + */ + struct so_list *solp; + + while((solp = parpp->spd_children) != NULL) { + unmap_object(solp->sol_map, 0); + parpp->spd_children = solp->sol_next; + free(solp); + } + + return -1; } - (void)close(fd); + return 0; +} - /* Assume _DYNAMIC is the first data item */ - dp = (struct _dynamic *)(addr+hdr.a_text); +/* + * Relocate and initialize the tree of shared objects rooted at the given + * link map entry. Returns 0 on success, or -1 on failure. On failure, + * an error message can be retrieved via dlerror(). + */ + static int +reloc_and_init(root) + struct so_map *root; +{ + struct so_map *smp; - /* Fixup __DYNAMIC structure */ - (long)dp->d_un.d_sdt += (long)addr; + /* + * Relocate all newly-loaded objects. We avoid recursion for this + * step by taking advantage of a few facts. This function is called + * only when there are in fact some newly-loaded objects to process. + * Furthermore, all newly-loaded objects will have their link map + * entries at the end of the link map list. And, the root of the + * tree of objects just loaded will have been the first to be loaded + * and therefore the first new object in the link map list. Finally, + * we take advantage of the fact that we can relocate the newly-loaded + * objects in any order. + * + * All these facts conspire to let us simply loop over the tail + * portion of the link map list, relocating each object so + * encountered. + */ + for(smp = root; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)) { + if(reloc_map(smp) < 0) + return -1; + } + } - p = alloc_link_map(path, sodp, smp, addr, dp); + /* + * Copy any relocated initialized data. Again, we can just loop + * over the appropriate portion of the link map list. + */ + for(smp = root; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)) + reloc_copy(smp); + } - /* save segment sizes for unmap. */ - smpp = LM_PRIVATE(p); - smpp->a_text = hdr.a_text; - smpp->a_data = hdr.a_data; - smpp->a_bss = hdr.a_bss; - return p; + /* + * Call any object initialization routines. + * + * Here, the order is very important, and we cannot simply loop + * over the newly-loaded objects as we did before. Rather, we + * have to initialize the tree of new objects depth-first, and + * process the sibling objects at each level in reverse order + * relative to the dependency list. + * + * Here is the reason we initialize depth-first. If an object + * depends on one or more other objects, then the objects it + * depends on should be initialized first, before the parent + * object itself. For it is possible that the parent's + * initialization routine will need the services provided by the + * objects it depends on -- and those objects had better already + * be initialized. + * + * We initialize the objects at each level of the tree in reverse + * order for a similar reason. When an object is linked with + * several libraries, it is common for routines in the earlier + * libraries to call routines in the later libraries. So, again, + * the later libraries need to be initialized first. + * + * The upshot of these rules is that we have to use recursion to + * get the libraries initialized in the best order. But the + * recursion is never likely to be very deep. + */ + init_object(root); + + return 0; } /* - * Unmap an object that is nolonger in use. + * Remove a reference to the shared object specified by SMP. If no + * references remain, unmap the object and, recursively, its descendents. + * This function also takes care of calling the finalization routines for + * objects that are removed. + * + * If KEEP is true, then the actual calls to munmap() are skipped, + * and the object is kept in memory. That is used only for finalization, + * from dlexit(), when the program is exiting. There are two reasons + * for it. First, the program is exiting and there is no point in + * spending the time to explicitly unmap its shared objects. Second, + * even after dlexit() has been called, there are still a couple of + * calls that are made to functions in libc. (This is really a bug + * in crt0.) So libc and the main program, at least, must remain + * mapped in that situation. + * + * Under no reasonable circumstances should this function fail. If + * anything goes wrong, we consider it an internal error, and report + * it with err(). */ - static int -unmap_object(smp) + static void +unmap_object(smp, keep) struct so_map *smp; + int keep; { - struct so_map *prev, *p; - struct somap_private *smpp; - - /* Find the object in the list and unlink it */ + struct somap_private *smpp = LM_PRIVATE(smp); - for (prev = NULL, p = link_map_head; - p != smp; - prev = p, p = p->som_next) continue; + smpp->spd_refcount--; + if(smpp->spd_refcount == 0) { /* Finished with this object */ + struct so_list *solp; - if (prev == NULL) { - link_map_head = smp->som_next; - if (smp->som_next == NULL) - link_map_tail = &link_map_head; - } else { - prev->som_next = smp->som_next; - if (smp->som_next == NULL) - link_map_tail = &prev->som_next; - } + if(smpp->spd_flags & RTLD_INIT) { /* Was initialized */ + /* + * Call the object's finalization routine. For + * backward compatibility, we first try to call + * ".fini". If that does not exist, we call + * "__fini". + */ + if(call_map(smp, ".fini") == -1) + call_map(smp, "__fini"); + } - /* Unmap the sections we have mapped */ + /* Recursively unreference the object's descendents */ + while((solp = smpp->spd_children) != NULL) { + unmap_object(solp->sol_map, keep); + smpp->spd_children = solp->sol_next; + free(solp); + } - smpp = LM_PRIVATE(smp); + if(!keep) { /* Unmap the object from memory */ + if(munmap(smp->som_addr, + smpp->a_text + smpp->a_data + smpp->a_bss) < 0) + err(1, "internal error 1: munmap failed"); - if (munmap(smp->som_addr, smpp->a_text + smpp->a_data) < 0) { - generror ("munmap failed: %s", strerror (errno)); - return -1; - } - if (smpp->a_bss > 0) { - if (munmap(smp->som_addr + smpp->a_text + smpp->a_data, - smpp->a_bss) < 0) { - generror ("munmap failed: %s", strerror (errno)); - return -1; - } - } - if (smp->som_path) free(smp->som_path); - free(smp); - return 0; + /* Unlink and free the object's link map entry */ + free_link_map(smp); + } + } } static inline int @@ -857,7 +1029,7 @@ reloc_map(smp) return 0; } -static void + static void reloc_copy(smp) struct so_map *smp; { @@ -871,42 +1043,45 @@ reloc_copy(smp) } } -static void -init_map(smp, sym, dependants) + static void +init_object(smp) struct so_map *smp; - char *sym; - int dependants; { - struct so_map *src_map = smp; - struct nzlist *np; - - if (LM_PRIVATE(smp)->spd_flags & RTLD_INIT) - return; + struct somap_private *smpp = LM_PRIVATE(smp); - LM_PRIVATE(smp)->spd_flags |= RTLD_INIT; + if(!(smpp->spd_flags & RTLD_INIT)) { /* Not initialized yet */ + smpp->spd_flags |= RTLD_INIT; - if (dependants) { - struct sod *sodp; - struct so_map *smp2; - long next; + /* Make sure all the children are initialized */ + if(smpp->spd_children != NULL) + init_sods(smpp->spd_children); - next = LD_NEED(smp->som_dynamic); - - while (next) { - sodp = (struct sod *)(LM_LDBASE(smp) + next); - smp2 = find_object(sodp, smp); - if (smp2) - init_map(smp2, sym, dependants); - next = sodp->sod_next; - } + if(call_map(smp, ".init") == -1) + call_map(smp, "__init"); } +} - np = lookup(sym, &src_map, 1); - if (np) - (*(void (*)())(src_map->som_addr + np->nz_value))(); + static void +init_sods(solp) + struct so_list *solp; +{ + /* Recursively initialize the rest of the list */ + if(solp->sol_next != NULL) + init_sods(solp->sol_next); + + /* Initialize the first element of the list */ + init_object(solp->sol_map); } -static void + +/* + * Call a function in a given shared object. SMP is the shared object, and + * SYM is the name of the function. + * + * Returns 0 on success, or -1 if the symbol was not found. Failure is not + * necessarily an error condition, so no error message is generated. + */ + static int call_map(smp, sym) struct so_map *smp; char *sym; @@ -915,121 +1090,12 @@ call_map(smp, sym) struct nzlist *np; np = lookup(sym, &src_map, 1); - if (np) + if (np) { (*(void (*)())(src_map->som_addr + np->nz_value))(); -} - -/* - * Load an object with all its dependant objects, recording the type of the - * object and optionally calling its init function. - */ -static struct so_map * -load_object(sodp, parent, type, init) - struct sod *sodp; - struct so_map *parent; - int type; - int init; -{ - struct so_map* smp; - - /* - * Find or map the object. - */ - smp = map_object(sodp, parent); - if (smp == NULL) return NULL; - - /* - * The first time the object is mapped, load it's dependant objects and - * relocate it. - */ - if (LM_PRIVATE(smp)->spd_refcount++ == 0) { - struct sod *sodp; - struct so_map *smp2; - long next; - - next = LD_NEED(smp->som_dynamic); - - /* - * Load dependant objects but defer initialisation until later. - * When all the dependants (and sub dependants, etc.) have been - * loaded and relocated, it is safe to call the init functions, - * using a recursive call to init_map. This ensures that if init - * code in the dependants calls code in the parent, it will work - * as expected. - */ - while (next) { - sodp = (struct sod *)(LM_LDBASE(smp) + next); - /* - * Dependant objects (of both dlopen and main) don't get a - * specific type. - */ - if ((smp2 = load_object(sodp, smp, 0, 0)) == NULL) { -#ifdef DEBUG -xprintf("ld.so: map_object failed on cascaded %s %s (%d.%d): %s\n", - smp->sod_library ? "library" : "file", smp->sod_name, - smp->sod_major, smp->sod_minor, strerror(errno)); -#endif - unload_object(smp); - return NULL; - } - next = sodp->sod_next; - } - - LM_PRIVATE(smp)->spd_flags |= type; - if (reloc_map(smp) < 0) { - unload_object(smp); - return NULL; - } - reloc_copy(smp); - if (init) { - init_map(smp, ".init", 1); + return 0; } - } - return smp; -} - -/* - * Unload an object, recursively unloading dependant objects. - */ -static int -unload_object(smp) - struct so_map *smp; -{ - struct so_map *smp2; - struct sod *sodp; - long next; - - if (--LM_PRIVATE(smp)->spd_refcount != 0) return -1; - - /* - * Call destructors for the object (before unloading its dependants - * since destructors may use them. Only call destructors if constructors - * have been called. - */ - if (LM_PRIVATE(smp)->spd_flags & RTLD_INIT) - call_map(smp, ".fini"); - - /* - * Unmap any dependant objects first. - */ - next = LD_NEED(smp->som_dynamic); - while (next) { - sodp = (struct sod *)(LM_LDBASE(smp) + next); - smp2 = find_object(sodp, smp); - if (smp2) - unload_object(smp2); - next = sodp->sod_next; - } - - /* - * Remove from address space. - */ - if (unmap_object(smp) < 0) - return -1; - - return 0; } /* @@ -1303,72 +1369,69 @@ binder(jsp) return addr; } - -static int hfd; -static long hsize; -static struct hints_header *hheader; +static struct hints_header *hheader; /* NULL means not mapped */ static struct hints_bucket *hbuckets; static char *hstrtab; -#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1) - - static void +/* + * Map the hints file into memory, if it is not already mapped. Returns + * 0 on success, or -1 on failure. + */ + static int maphints __P((void)) { - caddr_t addr; + static int hints_bad; /* TRUE if hints are unusable */ + int hfd; + struct hints_header hdr; + caddr_t addr; + + if (hheader != NULL) /* Already mapped */ + return 0; + + if (hints_bad) /* Known to be corrupt or unavailable */ + return -1; if ((hfd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { - hheader = (struct hints_header *)-1; - return; + hints_bad = 1; + return -1; } - hsize = PAGSIZ; - addr = mmap(0, hsize, PROT_READ, MAP_COPY, hfd, 0); + /* Read the header and check it */ - if (addr == (caddr_t)-1) { + if (read(hfd, &hdr, sizeof hdr) != sizeof hdr || + HH_BADMAG(hdr) || + hdr.hh_version != LD_HINTS_VERSION_1) { close(hfd); - hheader = (struct hints_header *)-1; - return; + hints_bad = 1; + return -1; } - hheader = (struct hints_header *)addr; - if (HH_BADMAG(*hheader)) { - munmap(addr, hsize); - close(hfd); - hheader = (struct hints_header *)-1; - return; - } + /* Map the hints into memory */ - if (hheader->hh_version != LD_HINTS_VERSION_1) { - munmap(addr, hsize); + addr = mmap(0, hdr.hh_ehints, PROT_READ, MAP_SHARED, hfd, 0); + if (addr == (caddr_t)-1) { close(hfd); - hheader = (struct hints_header *)-1; - return; + hints_bad = 1; + return -1; } - if (hheader->hh_ehints > hsize) { - if (mmap(addr+hsize, hheader->hh_ehints - hsize, - PROT_READ, MAP_COPY|MAP_FIXED, - hfd, hsize) != (caddr_t)(addr+hsize)) { - - munmap((caddr_t)hheader, hsize); - close(hfd); - hheader = (struct hints_header *)-1; - return; - } - } + close(hfd); + hheader = (struct hints_header *)addr; hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab); hstrtab = (char *)(addr + hheader->hh_strtab); + + return 0; } +/* + * Unmap the hints file, if it is currently mapped. + */ static void unmaphints() { - - if (HINTS_VALID) { - munmap((caddr_t)hheader, hsize); - close(hfd); + if (hheader != NULL) { + munmap((caddr_t)hheader, hheader->hh_ehints); hheader = NULL; } } @@ -1391,15 +1454,24 @@ hinthash(cp, vmajor) #undef major #undef minor +/* + * Search for a library in the hints generated by ldconfig. On success, + * returns the full pathname of the matching library. This string is + * always dynamically allocated on the heap. + * + * Returns the minor number of the matching library via the pointer + * argument MINORP. + * + * Returns NULL if the library cannot be found. + */ static char * -findhint(name, major, minor, preferred_path) +findhint(name, major, minorp) char *name; - int major, minor; - char *preferred_path; + int major; + int *minorp; { - struct hints_bucket *bp; - - bp = hbuckets + (hinthash(name, major) % hheader->hh_nbucket); + struct hints_bucket *bp = + hbuckets + (hinthash(name, major) % hheader->hh_nbucket); while (1) { /* Sanity check */ @@ -1412,16 +1484,16 @@ findhint(name, major, minor, preferred_path) break; } - if (strcmp(name, hstrtab + bp->hi_namex) == 0) { - /* It's `name', check version numbers */ - if (bp->hi_major == major && - (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) { - if (preferred_path == NULL || - strcmp(preferred_path, - hstrtab + bp->hi_pathx) == 0) { - return hstrtab + bp->hi_pathx; - } - } + /* + * We accept the current hints entry if its name matches + * and its major number matches. We don't have to search + * for the best minor number, because that was already + * done by "ldconfig" when it built the hints file. + */ + if (strcmp(name, hstrtab + bp->hi_namex) == 0 && + bp->hi_major == major) { + *minorp = bp->hi_ndewey >= 2 ? bp->hi_minor : -1; + return strdup(hstrtab + bp->hi_pathx); } if (bp->hi_next == -1) @@ -1435,79 +1507,51 @@ findhint(name, major, minor, preferred_path) return NULL; } +/* + * Search for the given shared library. On success, returns a string + * containing the full pathname for the library. This string is always + * dynamically allocated on the heap. + * + * Returns NULL if the library cannot be found. + */ static char * -rtfindlib(name, major, minor, usehints) +rtfindlib(name, major, minor) char *name; int major, minor; - int *usehints; { - char *cp, *ld_path = ld_library_path; - int realminor; - - if (hheader == NULL) - maphints(); - - if (!HINTS_VALID || !(*usehints)) - goto lose; + char *ld_path = ld_library_path; + char *path = NULL; + int realminor = -1; - if (ld_path != NULL) { - /* Prefer paths from LD_LIBRARY_PATH */ - while ((cp = strsep(&ld_path, ":")) != NULL) { + if (ld_path != NULL) { /* First, search the directories in ld_path */ + /* + * There is no point in trying to use the hints file for this. + */ + char *dir; - cp = findhint(name, major, minor, cp); + while (path == NULL && (dir = strsep(&ld_path, ":")) != NULL) { + path = search_lib_dir(dir, name, &major, &realminor, 0); if (ld_path) *(ld_path-1) = ':'; - if (cp) - return cp; } - /* Not found in hints, try directory search */ - realminor = -1; - cp = (char *)findshlib(name, &major, &realminor, 0); - if (cp && realminor >= minor) - return cp; } - /* No LD_LIBRARY_PATH or lib not found in there; check default */ - cp = findhint(name, major, minor, NULL); - if (cp) - return cp; + if (path == NULL && maphints() == 0) /* Search the hints file */ + path = findhint(name, major, &realminor); -lose: - /* No hints available for name */ - *usehints = 0; - realminor = -1; - cp = (char *)findshlib(name, &major, &realminor, 0); - if (cp) { - if (realminor < minor && getenv("LD_SUPPRESS_WARNINGS") == NULL) - warnx("warning: lib%s.so.%d.%d: " - "minor version < %d expected, using it anyway", - name, major, realminor, minor); - return cp; - } - generror ("Can't find shared library \"%s\"", - name); - return NULL; -} + if (path == NULL) /* Search the standard directories */ + path = findshlib(name, &major, &realminor, 0); -static struct somap_private dlmap_private = { - 0, - (struct so_map *)0, - 0, -#ifdef SUN_COMPAT - 0, -#endif -}; + if (path != NULL && + realminor < minor && + getenv("LD_SUPPRESS_WARNINGS") == NULL) { + warnx("warning: %s: minor version %d" + " older than expected %d, using it anyway", + path, realminor, minor); + } -static struct so_map dlmap = { - (caddr_t)0, - "internal", - (struct so_map *)0, - (struct sod *)0, - (caddr_t)0, - (u_int)0, - (struct _dynamic *)0, - (caddr_t)&dlmap_private -}; + return path; +} /* * Buffer for error messages and a pointer that is set to point to the buffer @@ -1520,42 +1564,29 @@ static char *dlerror_msg = NULL; static void * -__dlopen(name, mode) - char *name; +__dlopen(path, mode) + char *path; int mode; { - struct sod *sodp; + struct so_map *old_tail = link_map_tail; struct so_map *smp; - /* - * A NULL argument returns the current set of mapped objects. - */ - if (name == NULL) { - LM_PRIVATE(link_map_head)->spd_refcount++; - return link_map_head; - } - if ((sodp = (struct sod *)malloc(sizeof(struct sod))) == NULL) { - generror ("malloc failed: %s", strerror (errno)); - return NULL; - } - - sodp->sod_name = (long)strdup(name); - sodp->sod_library = 0; - sodp->sod_major = sodp->sod_minor = 0; + anon_open(); - if ((smp = load_object(sodp, &dlmap, RTLD_DL, 1)) == NULL) { -#ifdef DEBUG - xprintf("%s: %s\n", name, dlerror_buf); -#endif + /* Map the object, and the objects on which it depends */ + smp = map_object(path, (struct sod *) NULL, (struct so_map *) NULL); + if(smp == NULL) /* Failed */ return NULL; + LM_PRIVATE(smp)->spd_flags |= RTLD_DL; + + /* Relocate and initialize all newly-mapped objects */ + if(link_map_tail != old_tail) { /* We have mapped some new objects */ + if(reloc_and_init(smp) == -1) /* Failed */ + return NULL; } - /* - * If this was newly loaded, call the _init() function in the - * object as per manpage. - */ - if (LM_PRIVATE(smp)->spd_refcount == 1) - call_map(smp, "__init"); + unmaphints(); + anon_close(); return smp; } @@ -1565,31 +1596,22 @@ __dlclose(fd) void *fd; { struct so_map *smp = (struct so_map *)fd; + struct so_map *scanp; #ifdef DEBUG xprintf("dlclose(%s): refcount = %d\n", smp->som_path, LM_PRIVATE(smp)->spd_refcount); #endif - - if (smp == NULL) { - generror("NULL argument to dlclose"); - return -1; + /* Check the argument for validity */ + for(scanp = link_map_head; scanp != NULL; scanp = scanp->som_next) + if(scanp == smp) /* We found the map in the list */ + break; + if(scanp == NULL || !(LM_PRIVATE(smp)->spd_flags & RTLD_DL)) { + generror("Invalid argument to dlclose"); + return -1; } - if (LM_PRIVATE(smp)->spd_refcount > 1) { - LM_PRIVATE(smp)->spd_refcount--; - return 0; - } - - /* - * Call the function _fini() in the object as per manpage. - */ - call_map(smp, "__fini"); - - free((void*) smp->som_sod->sod_name); - free(smp->som_sod); - if (unload_object(smp) < 0) - return -1; + unmap_object(smp, 0); return 0; } @@ -1599,22 +1621,16 @@ __dlsym(fd, sym) void *fd; char *sym; { - struct so_map *smp = (struct so_map *)fd, *src_map = NULL; + struct so_map *src_map = (struct so_map *)fd; struct nzlist *np; long addr; - /* - * Restrict search to passed map if dlopen()ed. - */ - if (smp && LM_PRIVATE(smp)->spd_flags & RTLD_DL) - src_map = smp; - np = lookup(sym, &src_map, 1); if (np == NULL) { generror ("Symbol \"%s\" not found", sym); return NULL; } - /* Fixup jmpslot so future calls transfer directly to target */ + addr = np->nz_value; if (src_map) addr += (long)src_map->som_addr; @@ -1636,13 +1652,11 @@ __dlerror __P((void)) static void __dlexit __P((void)) { - struct so_map *smp; +#ifdef DEBUG +xprintf("__dlexit called\n"); +#endif - for (smp = link_map_head; smp; smp = smp->som_next) { - if (LM_PRIVATE(smp)->spd_flags & (RTLD_RTLD|RTLD_MAIN)) - continue; - call_map(smp, ".fini"); - } + unmap_object(link_map_head, 1); } /* @@ -1688,4 +1702,3 @@ char *fmt; (void)write(1, buf, strlen(buf)); va_end(ap); } - |