summaryrefslogtreecommitdiffstats
path: root/gnu
diff options
context:
space:
mode:
Diffstat (limited to 'gnu')
-rw-r--r--gnu/usr.bin/ld/rtld/rtld.c1197
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);
}
-
OpenPOWER on IntegriCloud