[Midnightbsd-cvs] src: rtld.c: Merge

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Thu Nov 20 13:09:18 EST 2008


Log Message:
-----------
Merge

Modified Files:
--------------
    src/libexec/rtld-elf:
        rtld.c (r1.4 -> r1.5)

-------------- next part --------------
Index: rtld.c
===================================================================
RCS file: /home/cvs/src/libexec/rtld-elf/rtld.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -L libexec/rtld-elf/rtld.c -L libexec/rtld-elf/rtld.c -u -r1.4 -r1.5
--- libexec/rtld-elf/rtld.c
+++ libexec/rtld-elf/rtld.c
@@ -23,7 +23,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.
  *
- * $FreeBSD: src/libexec/rtld-elf/rtld.c,v 1.106.2.2 2005/12/30 22:13:56 marcel Exp $
+ * $FreeBSD: src/libexec/rtld-elf/rtld.c,v 1.124 2007/05/17 18:00:27 csjp Exp $
  */
 
 /*
@@ -40,6 +40,8 @@
 #include <sys/mount.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ktrace.h>
 
 #include <dlfcn.h>
 #include <err.h>
@@ -80,10 +82,11 @@
  * Function declarations.
  */
 static const char *basename(const char *);
-static void die(void);
+static void die(void) __dead2;
 static void digest_dynamic(Obj_Entry *, int);
 static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
 static Obj_Entry *dlcheck(void *);
+static Obj_Entry *do_load_object(int, const char *, char *, struct stat *);
 static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
 static bool donelist_check(DoneList *, const Obj_Entry *);
 static void errmsg_restore(char *);
@@ -92,17 +95,16 @@
 static char *find_library(const char *, const Obj_Entry *);
 static const char *gethints(void);
 static void init_dag(Obj_Entry *);
-static void init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *);
+static void init_dag1(Obj_Entry *, Obj_Entry *, DoneList *);
 static void init_rtld(caddr_t);
-static void initlist_add_neededs(Needed_Entry *needed, Objlist *list);
-static void initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail,
-  Objlist *list);
+static void initlist_add_neededs(Needed_Entry *, Objlist *);
+static void initlist_add_objects(Obj_Entry *, Obj_Entry **, Objlist *);
 static bool is_exported(const Elf_Sym *);
 static void linkmap_add(Obj_Entry *);
 static void linkmap_delete(Obj_Entry *);
 static int load_needed_objects(Obj_Entry *);
 static int load_preload_objects(void);
-static Obj_Entry *load_object(char *);
+static Obj_Entry *load_object(const char *, const Obj_Entry *);
 static Obj_Entry *obj_from_addr(const void *);
 static void objlist_call_fini(Objlist *);
 static void objlist_call_init(Objlist *);
@@ -118,19 +120,27 @@
 static int rtld_dirname(const char *, char *);
 static void rtld_exit(void);
 static char *search_library_path(const char *, const char *);
-static const void **get_program_var_addr(const char *name);
+static const void **get_program_var_addr(const char *);
 static void set_program_var(const char *, const void *);
-static const Elf_Sym *symlook_default(const char *, unsigned long hash,
-  const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt);
-static const Elf_Sym *symlook_list(const char *, unsigned long,
-  Objlist *, const Obj_Entry **, bool in_plt, DoneList *);
-static void trace_loaded_objects(Obj_Entry *obj);
+static const Elf_Sym *symlook_default(const char *, unsigned long,
+  const Obj_Entry *, const Obj_Entry **, const Ver_Entry *, int);
+static const Elf_Sym *symlook_list(const char *, unsigned long, const Objlist *,
+  const Obj_Entry **, const Ver_Entry *, int, DoneList *);
+static const Elf_Sym *symlook_needed(const char *, unsigned long,
+  const Needed_Entry *, const Obj_Entry **, const Ver_Entry *,
+  int, DoneList *);
+static void trace_loaded_objects(Obj_Entry *);
 static void unlink_object(Obj_Entry *);
 static void unload_object(Obj_Entry *);
 static void unref_dag(Obj_Entry *);
 static void ref_dag(Obj_Entry *);
+static int  rtld_verify_versions(const Objlist *);
+static int  rtld_verify_object_versions(Obj_Entry *);
+static void object_add_name(Obj_Entry *, const char *);
+static int  object_match_name(const Obj_Entry *, const char *);
+static void ld_utrace_log(int, void *, void *, size_t, int, const char *);
 
-void r_debug_state(struct r_debug*, struct link_map*);
+void r_debug_state(struct r_debug *, struct link_map *);
 
 /*
  * Data declarations.
@@ -148,11 +158,13 @@
 static char *ld_preload;	/* Environment variable for libraries to
 				   load first */
 static char *ld_tracing;	/* Called from ldd to print libs */
+static char *ld_utrace;		/* Use utrace() to log events. */
 static Obj_Entry *obj_list;	/* Head of linked list of shared objects */
 static Obj_Entry **obj_tail;	/* Link field of last object in list */
 static Obj_Entry *obj_main;	/* The main program shared object */
 static Obj_Entry obj_rtld;	/* The dynamic linker shared object */
 static unsigned int obj_count;	/* Number of objects in obj_list */
+static unsigned int obj_loads;	/* Number of objects in obj_list */
 
 static Objlist list_global =	/* Objects dlopened with RTLD_GLOBAL */
   STAILQ_HEAD_INITIALIZER(list_global);
@@ -182,6 +194,7 @@
     (func_ptr_type) &dlerror,
     (func_ptr_type) &dlopen,
     (func_ptr_type) &dlsym,
+    (func_ptr_type) &dlvsym,
     (func_ptr_type) &dladdr,
     (func_ptr_type) &dllockinit,
     (func_ptr_type) &dlinfo,
@@ -192,6 +205,7 @@
     (func_ptr_type) &__tls_get_addr,
     (func_ptr_type) &_rtld_allocate_tls,
     (func_ptr_type) &_rtld_free_tls,
+    (func_ptr_type) &dl_iterate_phdr,
     NULL
 };
 
@@ -222,6 +236,53 @@
     (dlp)->num_alloc = obj_count,				\
     (dlp)->num_used = 0)
 
+#define	UTRACE_DLOPEN_START		1
+#define	UTRACE_DLOPEN_STOP		2
+#define	UTRACE_DLCLOSE_START		3
+#define	UTRACE_DLCLOSE_STOP		4
+#define	UTRACE_LOAD_OBJECT		5
+#define	UTRACE_UNLOAD_OBJECT		6
+#define	UTRACE_ADD_RUNDEP		7
+#define	UTRACE_PRELOAD_FINISHED		8
+#define	UTRACE_INIT_CALL		9
+#define	UTRACE_FINI_CALL		10
+
+struct utrace_rtld {
+	char sig[4];			/* 'RTLD' */
+	int event;
+	void *handle;
+	void *mapbase;			/* Used for 'parent' and 'init/fini' */
+	size_t mapsize;
+	int refcnt;			/* Used for 'mode' */
+	char name[MAXPATHLEN];
+};
+
+#define	LD_UTRACE(e, h, mb, ms, r, n) do {			\
+	if (ld_utrace != NULL)					\
+		ld_utrace_log(e, h, mb, ms, r, n);		\
+} while (0)
+
+static void
+ld_utrace_log(int event, void *handle, void *mapbase, size_t mapsize,
+    int refcnt, const char *name)
+{
+	struct utrace_rtld ut;
+
+	ut.sig[0] = 'R';
+	ut.sig[1] = 'T';
+	ut.sig[2] = 'L';
+	ut.sig[3] = 'D';
+	ut.event = event;
+	ut.handle = handle;
+	ut.mapbase = mapbase;
+	ut.mapsize = mapsize;
+	ut.refcnt = refcnt;
+	bzero(ut.name, sizeof(ut.name));
+	if (name)
+		strlcpy(ut.name, name, sizeof(ut.name));
+	utrace(&ut, sizeof(ut));
+}
+
 /*
  * Main entry point for dynamic linking.  The first argument is the
  * stack pointer.  The stack is expected to be laid out as described
@@ -290,17 +351,28 @@
     trust = !issetugid();
 
     ld_bind_now = getenv(LD_ "BIND_NOW");
-    if (trust) {
-	ld_debug = getenv(LD_ "DEBUG");
-	libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL;
-	libmap_override = getenv(LD_ "LIBMAP");
-	ld_library_path = getenv(LD_ "LIBRARY_PATH");
-	ld_preload = getenv(LD_ "PRELOAD");
-	dangerous_ld_env = libmap_disable || (libmap_override != NULL) ||
-	    (ld_library_path != NULL) || (ld_preload != NULL);
-    } else
-	dangerous_ld_env = 0;
+    /* 
+     * If the process is tainted, then we un-set the dangerous environment
+     * variables.  The process will be marked as tainted until setuid(2)
+     * is called.  If any child process calls setuid(2) we do not want any
+     * future processes to honor the potentially un-safe variables.
+     */
+    if (!trust) {
+        unsetenv(LD_ "PRELOAD");
+        unsetenv(LD_ "LIBMAP");
+        unsetenv(LD_ "LIBRARY_PATH");
+        unsetenv(LD_ "LIBMAP_DISABLE");
+        unsetenv(LD_ "DEBUG");
+    }
+    ld_debug = getenv(LD_ "DEBUG");
+    libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL;
+    libmap_override = getenv(LD_ "LIBMAP");
+    ld_library_path = getenv(LD_ "LIBRARY_PATH");
+    ld_preload = getenv(LD_ "PRELOAD");
+    dangerous_ld_env = libmap_disable || (libmap_override != NULL) ||
+	(ld_library_path != NULL) || (ld_preload != NULL);
     ld_tracing = getenv(LD_ "TRACE_LOADED_OBJECTS");
+    ld_utrace = getenv(LD_ "UTRACE");
 
     if (ld_debug != NULL && *ld_debug != '\0')
 	debug = 1;
@@ -363,6 +435,7 @@
     *obj_tail = obj_main;
     obj_tail = &obj_main->next;
     obj_count++;
+    obj_loads++;
     /* Make sure we don't call the main program's init and fini functions. */
     obj_main->init = obj_main->fini = (Elf_Addr)NULL;
 
@@ -388,6 +461,10 @@
     	obj->refcount++;
     }
 
+    dbg("checking for required versions");
+    if (rtld_verify_versions(&list_main) == -1 && !ld_tracing)
+	die();
+
     if (ld_tracing) {		/* We're done */
 	trace_loaded_objects(obj_main);
 	exit(0);
@@ -555,6 +632,7 @@
     const Elf_Dyn *dynp;
     Needed_Entry **needed_tail = &obj->needed;
     const Elf_Dyn *dyn_rpath = NULL;
+    const Elf_Dyn *dyn_soname = NULL;
     int plttype = DT_REL;
 
     obj->bind_now = false;
@@ -616,6 +694,29 @@
 	    obj->strsize = dynp->d_un.d_val;
 	    break;
 
+	case DT_VERNEED:
+	    obj->verneed = (const Elf_Verneed *) (obj->relocbase +
+		dynp->d_un.d_val);
+	    break;
+
+	case DT_VERNEEDNUM:
+	    obj->verneednum = dynp->d_un.d_val;
+	    break;
+
+	case DT_VERDEF:
+	    obj->verdef = (const Elf_Verdef *) (obj->relocbase +
+		dynp->d_un.d_val);
+	    break;
+
+	case DT_VERDEFNUM:
+	    obj->verdefnum = dynp->d_un.d_val;
+	    break;
+
+	case DT_VERSYM:
+	    obj->versyms = (const Elf_Versym *)(obj->relocbase +
+		dynp->d_un.d_val);
+	    break;
+
 	case DT_HASH:
 	    {
 		const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
@@ -661,7 +762,7 @@
 	    break;
 
 	case DT_SONAME:
-	    /* Not used by the dynamic linker. */
+	    dyn_soname = dynp;
 	    break;
 
 	case DT_INIT:
@@ -715,6 +816,9 @@
 
     if (dyn_rpath != NULL)
 	obj->rpath = obj->strtab + dyn_rpath->d_un.d_val;
+
+    if (dyn_soname != NULL)
+	object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val);
 }
 
 /*
@@ -902,11 +1006,12 @@
  */
 const Elf_Sym *
 find_symdef(unsigned long symnum, const Obj_Entry *refobj,
-    const Obj_Entry **defobj_out, bool in_plt, SymCache *cache)
+    const Obj_Entry **defobj_out, int flags, SymCache *cache)
 {
     const Elf_Sym *ref;
     const Elf_Sym *def;
     const Obj_Entry *defobj;
+    const Ver_Entry *ventry;
     const char *name;
     unsigned long hash;
 
@@ -938,8 +1043,9 @@
 	    _rtld_error("%s: Bogus symbol table entry %lu", refobj->path,
 		symnum);
 	}
+	ventry = fetch_ventry(refobj, symnum);
 	hash = elf_hash(name);
-	def = symlook_default(name, hash, refobj, &defobj, in_plt);
+	def = symlook_default(name, hash, refobj, &defobj, ventry, flags);
     } else {
 	def = ref;
 	defobj = refobj;
@@ -1164,18 +1270,9 @@
 	Needed_Entry *needed;
 
 	for (needed = obj->needed;  needed != NULL;  needed = needed->next) {
-	    const char *name = obj->strtab + needed->name;
-	    char *path = find_library(name, obj);
-
-	    needed->obj = NULL;
-	    if (path == NULL && !ld_tracing)
+	    needed->obj = load_object(obj->strtab + needed->name, obj);
+	    if (needed->obj == NULL && !ld_tracing)
 		return -1;
-
-	    if (path) {
-		needed->obj = load_object(path);
-		if (needed->obj == NULL && !ld_tracing)
-		    return -1;		/* XXX - cleanup */
-	    }
 	}
     }
 
@@ -1194,41 +1291,41 @@
     p += strspn(p, delim);
     while (*p != '\0') {
 	size_t len = strcspn(p, delim);
-	char *path;
 	char savech;
 
 	savech = p[len];
 	p[len] = '\0';
-	if ((path = find_library(p, NULL)) == NULL)
-	    return -1;
-	if (load_object(path) == NULL)
+	if (load_object(p, NULL) == NULL)
 	    return -1;	/* XXX - cleanup */
 	p[len] = savech;
 	p += len;
 	p += strspn(p, delim);
     }
+    LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL);
     return 0;
 }
 
 /*
- * Load a shared object into memory, if it is not already loaded.  The
- * argument must be a string allocated on the heap.  This function assumes
- * responsibility for freeing it when necessary.
+ * Load a shared object into memory, if it is not already loaded.
  *
  * Returns a pointer to the Obj_Entry for the object.  Returns NULL
  * on failure.
  */
 static Obj_Entry *
-load_object(char *path)
+load_object(const char *name, const Obj_Entry *refobj)
 {
     Obj_Entry *obj;
     int fd = -1;
     struct stat sb;
-    struct statfs fs;
+    char *path;
 
     for (obj = obj_list->next;  obj != NULL;  obj = obj->next)
-	if (strcmp(obj->path, path) == 0)
-	    break;
+	if (object_match_name(obj, name))
+	    return obj;
+
+    path = find_library(name, refobj);
+    if (path == NULL)
+	return NULL;
 
     /*
      * If we didn't find a match by pathname, open the file and check
@@ -1238,63 +1335,80 @@
      * To avoid a race, we open the file and use fstat() rather than
      * using stat().
      */
-    if (obj == NULL) {
-	if ((fd = open(path, O_RDONLY)) == -1) {
-	    _rtld_error("Cannot open \"%s\"", path);
-	    return NULL;
-	}
-	if (fstat(fd, &sb) == -1) {
-	    _rtld_error("Cannot fstat \"%s\"", path);
+    if ((fd = open(path, O_RDONLY)) == -1) {
+	_rtld_error("Cannot open \"%s\"", path);
+	free(path);
+	return NULL;
+    }
+    if (fstat(fd, &sb) == -1) {
+	_rtld_error("Cannot fstat \"%s\"", path);
+	close(fd);
+	free(path);
+	return NULL;
+    }
+    for (obj = obj_list->next;  obj != NULL;  obj = obj->next) {
+	if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) {
 	    close(fd);
-	    return NULL;
-	}
-	for (obj = obj_list->next;  obj != NULL;  obj = obj->next) {
-	    if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) {
-		close(fd);
-		break;
-	    }
+	    break;
 	}
     }
+    if (obj != NULL) {
+	object_add_name(obj, name);
+	free(path);
+	close(fd);
+	return obj;
+    }
 
-    if (obj == NULL) {	/* First use of this object, so we must map it in */
-	/*
-	 * but first, make sure that environment variables haven't been 
-	 * used to circumvent the noexec flag on a filesystem.
-	 */
-	if (dangerous_ld_env) {
-	    if (fstatfs(fd, &fs) != 0) {
-		_rtld_error("Cannot fstatfs \"%s\"", path);
-		close(fd);
-		return NULL;
-	    }
-	    if (fs.f_flags & MNT_NOEXEC) {
-		_rtld_error("Cannot execute objects on %s\n", fs.f_mntonname);
-		close(fd);
+    /* First use of this object, so we must map it in */
+    obj = do_load_object(fd, name, path, &sb);
+    if (obj == NULL)
+	free(path);
+    close(fd);
+
+    return obj;
+}
+
+static Obj_Entry *
+do_load_object(int fd, const char *name, char *path, struct stat *sbp)
+{
+    Obj_Entry *obj;
+    struct statfs fs;
+
+    /*
+     * but first, make sure that environment variables haven't been
+     * used to circumvent the noexec flag on a filesystem.
+     */
+    if (dangerous_ld_env) {
+	if (fstatfs(fd, &fs) != 0) {
+	    _rtld_error("Cannot fstatfs \"%s\"", path);
 		return NULL;
-	    }
 	}
-	dbg("loading \"%s\"", path);
-	obj = map_object(fd, path, &sb);
-	close(fd);
-	if (obj == NULL) {
-	    free(path);
+	if (fs.f_flags & MNT_NOEXEC) {
+	    _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname);
 	    return NULL;
 	}
+    }
+    dbg("loading \"%s\"", path);
+    obj = map_object(fd, path, sbp);
+    if (obj == NULL)
+        return NULL;
+
+    object_add_name(obj, name);
+    obj->path = path;
+    digest_dynamic(obj, 0);
 
-	obj->path = path;
-	digest_dynamic(obj, 0);
+    *obj_tail = obj;
+    obj_tail = &obj->next;
+    obj_count++;
+    obj_loads++;
+    linkmap_add(obj);	/* for GDB & dlinfo() */
 
-	*obj_tail = obj;
-	obj_tail = &obj->next;
-	obj_count++;
-	linkmap_add(obj);	/* for GDB & dlinfo() */
-
-	dbg("  %p .. %p: %s", obj->mapbase,
-	  obj->mapbase + obj->mapsize - 1, obj->path);
-	if (obj->textrel)
-	    dbg("  WARNING: %s has impure text", obj->path);
-    } else
-	free(path);
+    dbg("  %p .. %p: %s", obj->mapbase,
+         obj->mapbase + obj->mapsize - 1, obj->path);
+    if (obj->textrel)
+	dbg("  WARNING: %s has impure text", obj->path);
+    LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+	obj->path);    
 
     return obj;
 }
@@ -1333,6 +1447,8 @@
 	if (elm->obj->refcount == 0) {
 	    dbg("calling fini function for %s at %p", elm->obj->path,
 	        (void *)elm->obj->fini);
+	    LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini, 0, 0,
+		elm->obj->path);
 	    call_initfini_pointer(elm->obj, elm->obj->fini);
 	}
     }
@@ -1358,6 +1474,8 @@
     STAILQ_FOREACH(elm, list, link) {
 	dbg("calling init function for %s at %p", elm->obj->path,
 	    (void *)elm->obj->init);
+	LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init, 0, 0,
+	    elm->obj->path);
 	call_initfini_pointer(elm->obj, elm->obj->init);
     }
     errmsg_restore(saved_msg);
@@ -1631,6 +1749,8 @@
 	wlock_release(rtld_bind_lock, lockstate);
 	return -1;
     }
+    LD_UTRACE(UTRACE_DLCLOSE_START, handle, NULL, 0, root->dl_refcount,
+	root->path);
 
     /* Unreference the object and its dependencies. */
     root->dl_refcount--;
@@ -1652,6 +1772,7 @@
 	unload_object(root);
 	GDB_STATE(RT_CONSISTENT,NULL);
     }
+    LD_UTRACE(UTRACE_DLCLOSE_STOP, handle, NULL, 0, 0, NULL);
     wlock_release(rtld_bind_lock, lockstate);
     return 0;
 }
@@ -1694,6 +1815,7 @@
     Objlist initlist;
     int result, lockstate;
 
+    LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name);
     ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
     if (ld_tracing != NULL)
 	environ = (char **)*get_program_var_addr("environ");
@@ -1709,9 +1831,7 @@
 	obj = obj_main;
 	obj->refcount++;
     } else {
-	char *path = find_library(name, obj_main);
-	if (path != NULL)
-	    obj = load_object(path);
+	obj = load_object(name, obj_main);
     }
 
     if (obj) {
@@ -1721,14 +1841,14 @@
 	mode &= RTLD_MODEMASK;
 	if (*old_obj_tail != NULL) {		/* We loaded something new. */
 	    assert(*old_obj_tail == obj);
-
 	    result = load_needed_objects(obj);
+	    init_dag(obj);
+	    if (result != -1)
+		result = rtld_verify_versions(&obj->dagmembers);
 	    if (result != -1 && ld_tracing)
 		goto trace;
-
 	    if (result == -1 ||
-	      (init_dag(obj), relocate_objects(obj, mode == RTLD_NOW,
-	       &obj_rtld)) == -1) {
+	      (relocate_objects(obj, mode == RTLD_NOW, &obj_rtld)) == -1) {
 		obj->dl_refcount--;
 		unref_dag(obj);
 		if (obj->refcount == 0)
@@ -1748,6 +1868,8 @@
 	}
     }
 
+    LD_UTRACE(UTRACE_DLOPEN_STOP, obj, NULL, 0, obj ? obj->dl_refcount : 0,
+	name);
     GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL);
 
     /* Call the init functions with no locks held. */
@@ -1763,46 +1885,46 @@
     exit(0);
 }
 
-void *
-dlsym(void *handle, const char *name)
+static void *
+do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
+    int flags)
 {
-    const Obj_Entry *obj;
-    unsigned long hash;
+    DoneList donelist;
+    const Obj_Entry *obj, *defobj;
     const Elf_Sym *def;
-    const Obj_Entry *defobj;
+    unsigned long hash;
     int lockstate;
 
     hash = elf_hash(name);
     def = NULL;
     defobj = NULL;
+    flags |= SYMLOOK_IN_PLT;
 
     lockstate = rlock_acquire(rtld_bind_lock);
     if (handle == NULL || handle == RTLD_NEXT ||
 	handle == RTLD_DEFAULT || handle == RTLD_SELF) {
-	void *retaddr;
 
-	retaddr = __builtin_return_address(0);	/* __GNUC__ only */
 	if ((obj = obj_from_addr(retaddr)) == NULL) {
 	    _rtld_error("Cannot determine caller's shared object");
 	    rlock_release(rtld_bind_lock, lockstate);
 	    return NULL;
 	}
 	if (handle == NULL) {	/* Just the caller's shared object. */
-	    def = symlook_obj(name, hash, obj, true);
+	    def = symlook_obj(name, hash, obj, ve, flags);
 	    defobj = obj;
 	} else if (handle == RTLD_NEXT || /* Objects after caller's */
 		   handle == RTLD_SELF) { /* ... caller included */
 	    if (handle == RTLD_NEXT)
 		obj = obj->next;
 	    for (; obj != NULL; obj = obj->next) {
-		if ((def = symlook_obj(name, hash, obj, true)) != NULL) {
+		if ((def = symlook_obj(name, hash, obj, ve, flags)) != NULL) {
 		    defobj = obj;
 		    break;
 		}
 	    }
 	} else {
 	    assert(handle == RTLD_DEFAULT);
-	    def = symlook_default(name, hash, obj, &defobj, true);
+	    def = symlook_default(name, hash, obj, &defobj, ve, flags);
 	}
     } else {
 	if ((obj = dlcheck(handle)) == NULL) {
@@ -1810,20 +1932,20 @@
 	    return NULL;
 	}
 
+	donelist_init(&donelist);
 	if (obj->mainprog) {
-	    DoneList donelist;
-
 	    /* Search main program and all libraries loaded by it. */
-	    donelist_init(&donelist);
-	    def = symlook_list(name, hash, &list_main, &defobj, true,
-	      &donelist);
+	    def = symlook_list(name, hash, &list_main, &defobj, ve, flags,
+			       &donelist);
 	} else {
-	    /*
-	     * XXX - This isn't correct.  The search should include the whole
-	     * DAG rooted at the given object.
-	     */
-	    def = symlook_obj(name, hash, obj, true);
-	    defobj = obj;
+	    Needed_Entry fake;
+
+	    /* Search the whole DAG rooted at the given object. */
+	    fake.next = NULL;
+	    fake.obj = (Obj_Entry *)obj;
+	    fake.name = 0;
+	    def = symlook_needed(name, hash, &fake, &defobj, ve, flags,
+				 &donelist);
 	}
     }
 
@@ -1849,6 +1971,26 @@
     return NULL;
 }
 
+void *
+dlsym(void *handle, const char *name)
+{
+	return do_dlsym(handle, name, __builtin_return_address(0), NULL,
+	    SYMLOOK_DLSYM);
+}
+
+void *
+dlvsym(void *handle, const char *name, const char *version)
+{
+	Ver_Entry ventry;
+
+	ventry.name = version;
+	ventry.file = NULL;
+	ventry.hash = elf_hash(version);
+	ventry.flags= 0;
+	return do_dlsym(handle, name, __builtin_return_address(0), &ventry,
+	    SYMLOOK_DLSYM);
+}
+
 int
 dladdr(const void *addr, Dl_info *info)
 {
@@ -1951,6 +2093,37 @@
     return (error);
 }
 
+int
+dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param)
+{
+    struct dl_phdr_info phdr_info;
+    const Obj_Entry *obj;
+    int error, lockstate;
+
+    lockstate = rlock_acquire(rtld_bind_lock);
+
+    error = 0;
+
+    for (obj = obj_list;  obj != NULL;  obj = obj->next) {
+	phdr_info.dlpi_addr = (Elf_Addr)obj->relocbase;
+	phdr_info.dlpi_name = STAILQ_FIRST(&obj->names) ?
+	    STAILQ_FIRST(&obj->names)->name : obj->path;
+	phdr_info.dlpi_phdr = obj->phdr;
+	phdr_info.dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]);
+	phdr_info.dlpi_tls_modid = obj->tlsindex;
+	phdr_info.dlpi_tls_data = obj->tlsinit;
+	phdr_info.dlpi_adds = obj_loads;
+	phdr_info.dlpi_subs = obj_loads - obj_count;
+
+	if ((error = callback(&phdr_info, sizeof phdr_info, param)) != 0)
+		break;
+
+    }
+    rlock_release(rtld_bind_lock, lockstate);
+
+    return (error);
+}
+
 struct fill_search_info_args {
     int		 request;
     unsigned int flags;
@@ -2163,7 +2336,7 @@
     for (obj = obj_main;  obj != NULL;  obj = obj->next) {
 	const Elf_Sym *def;
 
-	if ((def = symlook_obj(name, hash, obj, false)) != NULL) {
+	if ((def = symlook_obj(name, hash, obj, NULL, 0)) != NULL) {
 	    const void **addr;
 
 	    addr = (const void **)(obj->relocbase + def->st_value);
@@ -2196,8 +2369,8 @@
  * defining object via the reference parameter DEFOBJ_OUT.
  */
 static const Elf_Sym *
-symlook_default(const char *name, unsigned long hash,
-    const Obj_Entry *refobj, const Obj_Entry **defobj_out, bool in_plt)
+symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj,
+    const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags)
 {
     DoneList donelist;
     const Elf_Sym *def;
@@ -2211,7 +2384,7 @@
 
     /* Look first in the referencing object if linked symbolically. */
     if (refobj->symbolic && !donelist_check(&donelist, refobj)) {
-	symp = symlook_obj(name, hash, refobj, in_plt);
+	symp = symlook_obj(name, hash, refobj, ventry, flags);
 	if (symp != NULL) {
 	    def = symp;
 	    defobj = refobj;
@@ -2220,7 +2393,8 @@
 
     /* Search all objects loaded at program start up. */
     if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
-	symp = symlook_list(name, hash, &list_main, &obj, in_plt, &donelist);
+	symp = symlook_list(name, hash, &list_main, &obj, ventry, flags,
+	    &donelist);
 	if (symp != NULL &&
 	  (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
 	    def = symp;
@@ -2232,8 +2406,8 @@
     STAILQ_FOREACH(elm, &list_global, link) {
        if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK)
            break;
-       symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt,
-         &donelist);
+       symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, ventry,
+	   flags, &donelist);
 	if (symp != NULL &&
 	  (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
 	    def = symp;
@@ -2245,8 +2419,8 @@
     STAILQ_FOREACH(elm, &refobj->dldags, link) {
 	if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK)
 	    break;
-	symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, in_plt,
-	  &donelist);
+	symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, ventry,
+	    flags, &donelist);
 	if (symp != NULL &&
 	  (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
 	    def = symp;
@@ -2261,7 +2435,7 @@
      * in the "exports" array can be resolved from the dynamic linker.
      */
     if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
-	symp = symlook_obj(name, hash, &obj_rtld, in_plt);
+	symp = symlook_obj(name, hash, &obj_rtld, ventry, flags);
 	if (symp != NULL && is_exported(symp)) {
 	    def = symp;
 	    defobj = &obj_rtld;
@@ -2274,8 +2448,9 @@
 }
 
 static const Elf_Sym *
-symlook_list(const char *name, unsigned long hash, Objlist *objlist,
-  const Obj_Entry **defobj_out, bool in_plt, DoneList *dlp)
+symlook_list(const char *name, unsigned long hash, const Objlist *objlist,
+  const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags,
+  DoneList *dlp)
 {
     const Elf_Sym *symp;
     const Elf_Sym *def;
@@ -2287,7 +2462,7 @@
     STAILQ_FOREACH(elm, objlist, link) {
 	if (donelist_check(dlp, elm->obj))
 	    continue;
-	if ((symp = symlook_obj(name, hash, elm->obj, in_plt)) != NULL) {
+	if ((symp = symlook_obj(name, hash, elm->obj, ventry, flags)) != NULL) {
 	    if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) {
 		def = symp;
 		defobj = elm->obj;
@@ -2302,38 +2477,174 @@
 }
 
 /*
- * Search the symbol table of a single shared object for a symbol of
- * the given name.  Returns a pointer to the symbol, or NULL if no
+ * Search the symbol table of a shared object and all objects needed
+ * by it for a symbol of the given name.  Search order is
+ * breadth-first.  Returns a pointer to the symbol, or NULL if no
  * definition was found.
+ */
+static const Elf_Sym *
+symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed,
+  const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags,
+  DoneList *dlp)
+{
+    const Elf_Sym *def, *def_w;
+    const Needed_Entry *n;
+    const Obj_Entry *obj, *defobj, *defobj1;
+
+    def = def_w = NULL;
+    defobj = NULL;
+    for (n = needed; n != NULL; n = n->next) {
+	if ((obj = n->obj) == NULL ||
+	    donelist_check(dlp, obj) ||
+	    (def = symlook_obj(name, hash, obj, ventry, flags)) == NULL)
+	    continue;
+	defobj = obj;
+	if (ELF_ST_BIND(def->st_info) != STB_WEAK) {
+	    *defobj_out = defobj;
+	    return (def);
+	}
+    }
+    /*
+     * There we come when either symbol definition is not found in
+     * directly needed objects, or found symbol is weak.
+     */
+    for (n = needed; n != NULL; n = n->next) {
+	if ((obj = n->obj) == NULL)
+	    continue;
+	def_w = symlook_needed(name, hash, obj->needed, &defobj1,
+			       ventry, flags, dlp);
+	if (def_w == NULL)
+	    continue;
+	if (def == NULL || ELF_ST_BIND(def_w->st_info) != STB_WEAK) {
+	    def = def_w;
+	    defobj = defobj1;
+	}
+	if (ELF_ST_BIND(def_w->st_info) != STB_WEAK)
+	    break;
+    }
+    if (def != NULL)
+	*defobj_out = defobj;
+    return (def);
+}
+
+/*
+ * Search the symbol table of a single shared object for a symbol of
+ * the given name and version, if requested.  Returns a pointer to the
+ * symbol, or NULL if no definition was found.
  *
  * The symbol's hash value is passed in for efficiency reasons; that
  * eliminates many recomputations of the hash value.
  */
 const Elf_Sym *
 symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
-  bool in_plt)
+    const Ver_Entry *ventry, int flags)
 {
-    if (obj->buckets != NULL) {
-	unsigned long symnum = obj->buckets[hash % obj->nbuckets];
+    unsigned long symnum;
+    const Elf_Sym *vsymp;
+    Elf_Versym verndx;
+    int vcount;
+
+    if (obj->buckets == NULL)
+	return NULL;
 
-	while (symnum != STN_UNDEF) {
-	    const Elf_Sym *symp;
-	    const char *strp;
+    vsymp = NULL;
+    vcount = 0;
+    symnum = obj->buckets[hash % obj->nbuckets];
+
+    for (; symnum != STN_UNDEF; symnum = obj->chains[symnum]) {
+	const Elf_Sym *symp;
+	const char *strp;
 
-	    if (symnum >= obj->nchains)
+	if (symnum >= obj->nchains)
 		return NULL;	/* Bad object */
-	    symp = obj->symtab + symnum;
-	    strp = obj->strtab + symp->st_name;
 
-	    if (name[0] == strp[0] && strcmp(name, strp) == 0)
-		return symp->st_shndx != SHN_UNDEF ||
-		  (!in_plt && symp->st_value != 0 &&
-		  ELF_ST_TYPE(symp->st_info) == STT_FUNC) ? symp : NULL;
+	symp = obj->symtab + symnum;
+	strp = obj->strtab + symp->st_name;
+
+	switch (ELF_ST_TYPE(symp->st_info)) {
+	case STT_FUNC:
+	case STT_NOTYPE:
+	case STT_OBJECT:
+	    if (symp->st_value == 0)
+		continue;
+		/* fallthrough */
+	case STT_TLS:
+	    if (symp->st_shndx != SHN_UNDEF ||
+		((flags & SYMLOOK_IN_PLT) == 0 &&
+		 ELF_ST_TYPE(symp->st_info) == STT_FUNC))
+		break;
+		/* fallthrough */
+	default:
+	    continue;
+	}
+	if (name[0] != strp[0] || strcmp(name, strp) != 0)
+	    continue;
 
-	    symnum = obj->chains[symnum];
+	if (ventry == NULL) {
+	    if (obj->versyms != NULL) {
+		verndx = VER_NDX(obj->versyms[symnum]);
+		if (verndx > obj->vernum) {
+		    _rtld_error("%s: symbol %s references wrong version %d",
+			obj->path, obj->strtab + symnum, verndx);
+		    continue;
+		}
+		/*
+		 * If we are not called from dlsym (i.e. this is a normal
+		 * relocation from unversioned binary, accept the symbol
+		 * immediately if it happens to have first version after
+		 * this shared object became versioned. Otherwise, if
+		 * symbol is versioned and not hidden, remember it. If it
+		 * is the only symbol with this name exported by the
+		 * shared object, it will be returned as a match at the
+		 * end of the function. If symbol is global (verndx < 2)
+		 * accept it unconditionally.
+		 */
+		if ((flags & SYMLOOK_DLSYM) == 0 && verndx == VER_NDX_GIVEN)
+		    return symp;
+	        else if (verndx >= VER_NDX_GIVEN) {
+		    if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) {
+			if (vsymp == NULL)
+			    vsymp = symp;
+			vcount ++;
+		    }
+		    continue;
+		}
+	    }
+	    return symp;
+	} else {
+	    if (obj->versyms == NULL) {
+		if (object_match_name(obj, ventry->name)) {
+		    _rtld_error("%s: object %s should provide version %s for "
+			"symbol %s", obj_rtld.path, obj->path, ventry->name,
+			obj->strtab + symnum);
+		    continue;
+		}
+	    } else {
+		verndx = VER_NDX(obj->versyms[symnum]);
+		if (verndx > obj->vernum) {
+		    _rtld_error("%s: symbol %s references wrong version %d",
+			obj->path, obj->strtab + symnum, verndx);
+		    continue;
+		}
+		if (obj->vertab[verndx].hash != ventry->hash ||
+		    strcmp(obj->vertab[verndx].name, ventry->name)) {
+		    /*
+		     * Version does not match. Look if this is a global symbol
+		     * and if it is not hidden. If global symbol (verndx < 2)
+		     * is available, use it. Do not return symbol if we are
+		     * called by dlvsym, because dlvsym looks for a specific
+		     * version and default one is not what dlvsym wants.
+		     */
+		    if ((flags & SYMLOOK_DLSYM) ||
+			(obj->versyms[symnum] & VER_NDX_HIDDEN) ||
+			(verndx >= VER_NDX_GIVEN))
+			continue;
+		}
+	    }
+	    return symp;
 	}
     }
-    return NULL;
+    return (vcount == 1) ? vsymp : NULL;
 }
 
 static void
@@ -2454,6 +2765,8 @@
     linkp = &obj_list->next;
     while ((obj = *linkp) != NULL) {
 	if (obj->refcount == 0) {
+	    LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+		obj->path);
 	    dbg("unloading \"%s\"", obj->path);
 	    munmap(obj->mapbase, obj->mapsize);
 	    linkmap_delete(obj);
@@ -2476,7 +2789,7 @@
 	objlist_remove(&list_global, root);
 
     	/* Remove the object from all objects' DAG lists. */
-    	STAILQ_FOREACH(elm, &root->dagmembers , link) {
+    	STAILQ_FOREACH(elm, &root->dagmembers, link) {
 	    objlist_remove(&elm->obj->dldags, root);
 	    if (elm->obj != root)
 		unlink_object(elm->obj);
@@ -2489,7 +2802,7 @@
 {
     Objlist_Entry *elm;
 
-    STAILQ_FOREACH(elm, &root->dagmembers , link)
+    STAILQ_FOREACH(elm, &root->dagmembers, link)
 	elm->obj->refcount++;
 }
 
@@ -2498,7 +2811,7 @@
 {
     Objlist_Entry *elm;
 
-    STAILQ_FOREACH(elm, &root->dagmembers , link)
+    STAILQ_FOREACH(elm, &root->dagmembers, link)
 	elm->obj->refcount--;
 }
 
@@ -2542,56 +2855,46 @@
 
 /* XXX not sure what variants to use for arm. */
 
-#if defined(__ia64__) || defined(__alpha__) || defined(__powerpc__)
+#if defined(__ia64__) || defined(__powerpc__)
 
 /*
  * Allocate Static TLS using the Variant I method.
  */
 void *
-allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign)
+allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
 {
     Obj_Entry *obj;
-    size_t size;
-    char *tls;
-    Elf_Addr *dtv, *olddtv;
+    char *tcb;
+    Elf_Addr **tls;
+    Elf_Addr *dtv;
     Elf_Addr addr;
     int i;
 
-    size = tls_static_space;
-
-    tls = malloc(size);
-    dtv = calloc(1, (tls_max_index + 2) * sizeof(Elf_Addr));
-
-    *(Elf_Addr**) tls = dtv;
-
-    dtv[0] = tls_dtv_generation;
-    dtv[1] = tls_max_index;
-
-    if (oldtls) {
-	/*
-	 * Copy the static TLS block over whole.
-	 */
-	memcpy(tls + tcbsize, oldtls + tcbsize, tls_static_space - tcbsize);
+    if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
+	return (oldtcb);
 
-	/*
-	 * If any dynamic TLS blocks have been created tls_get_addr(),
-	 * move them over.
-	 */
-	olddtv = *(Elf_Addr**) oldtls;
-	for (i = 0; i < olddtv[1]; i++) {
-	    if (olddtv[i+2] < (Elf_Addr)oldtls ||
-		olddtv[i+2] > (Elf_Addr)oldtls + tls_static_space) {
-		dtv[i+2] = olddtv[i+2];
-		olddtv[i+2] = 0;
+    assert(tcbsize >= TLS_TCB_SIZE);
+    tcb = calloc(1, tls_static_space - TLS_TCB_SIZE + tcbsize);
+    tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE);
+
+    if (oldtcb != NULL) {
+	memcpy(tls, oldtcb, tls_static_space);
+	free(oldtcb);
+
+	/* Adjust the DTV. */
+	dtv = tls[0];
+	for (i = 0; i < dtv[1]; i++) {
+	    if (dtv[i+2] >= (Elf_Addr)oldtcb &&
+		dtv[i+2] < (Elf_Addr)oldtcb + tls_static_space) {
+		dtv[i+2] = dtv[i+2] - (Elf_Addr)oldtcb + (Elf_Addr)tls;
 	    }
 	}
-
-	/*
-	 * We assume that all tls blocks are allocated with the same
-	 * size and alignment.
-	 */
-	free_tls(oldtls, tcbsize, tcbalign);
     } else {
+	dtv = calloc(tls_max_index + 2, sizeof(Elf_Addr));
+	tls[0] = dtv;
+	dtv[0] = tls_dtv_generation;
+	dtv[1] = tls_max_index;
+
 	for (obj = objs; obj; obj = obj->next) {
 	    if (obj->tlsoffset) {
 		addr = (Elf_Addr)tls + obj->tlsoffset;
@@ -2605,35 +2908,30 @@
 	}
     }
 
-    return tls;
+    return (tcb);
 }
 
 void
-free_tls(void *tls, size_t tcbsize, size_t tcbalign)
+free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
 {
-    size_t size;
-    Elf_Addr* dtv;
-    int dtvsize, i;
+    Elf_Addr *dtv;
     Elf_Addr tlsstart, tlsend;
+    int dtvsize, i;
 
-    /*
-     * Figure out the size of the initial TLS block so that we can
-     * find stuff which __tls_get_addr() allocated dynamically.
-     */
-    size = tls_static_space;
+    assert(tcbsize >= TLS_TCB_SIZE);
+
+    tlsstart = (Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE;
+    tlsend = tlsstart + tls_static_space;
 
-    dtv = ((Elf_Addr**)tls)[0];
+    dtv = *(Elf_Addr **)tlsstart;
     dtvsize = dtv[1];
-    tlsstart = (Elf_Addr) tls;
-    tlsend = tlsstart + size;
     for (i = 0; i < dtvsize; i++) {
-	if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] > tlsend)) {
-	    free((void*) dtv[i+2]);
+	if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] >= tlsend)) {
+	    free((void*)dtv[i+2]);
 	}
     }
-
-    free((void*) tlsstart);
-    free((void*) dtv);
+    free(dtv);
+    free(tcb);
 }
 
 #endif
@@ -2657,7 +2955,7 @@
     size = round(tls_static_space, tcbalign);
 
     assert(tcbsize >= 2*sizeof(Elf_Addr));
-    tls = malloc(size + tcbsize);
+    tls = calloc(1, size + tcbsize);
     dtv = calloc(1, (tls_max_index + 2) * sizeof(Elf_Addr));
 
     segbase = (Elf_Addr)(tls + size);
@@ -2811,7 +3109,7 @@
      * block, we give our space back to the 'allocator'. This is a
      * simplistic workaround to allow libGL.so.1 to be loaded and
      * unloaded multiple times. We only handle the Variant II
-     * mechanism for now - this really needs a proper allocator.  
+     * mechanism for now - this really needs a proper allocator.
      */
     if (calculate_tls_end(obj->tlsoffset, obj->tlssize)
 	== calculate_tls_end(tls_last_offset, tls_last_size)) {
@@ -2842,3 +3140,235 @@
     free_tls(tcb, tcbsize, tcbalign);
     wlock_release(rtld_bind_lock, lockstate);
 }
+
+static void
+object_add_name(Obj_Entry *obj, const char *name)
+{
+    Name_Entry *entry;
+    size_t len;
+
+    len = strlen(name);
+    entry = malloc(sizeof(Name_Entry) + len);
+
+    if (entry != NULL) {
+	strcpy(entry->name, name);
+	STAILQ_INSERT_TAIL(&obj->names, entry, link);
+    }
+}
+
+static int
+object_match_name(const Obj_Entry *obj, const char *name)
+{
+    Name_Entry *entry;
+
+    STAILQ_FOREACH(entry, &obj->names, link) {
+	if (strcmp(name, entry->name) == 0)
+	    return (1);
+    }
+    return (0);
+}
+
+static Obj_Entry *
+locate_dependency(const Obj_Entry *obj, const char *name)
+{
+    const Objlist_Entry *entry;
+    const Needed_Entry *needed;
+
+    STAILQ_FOREACH(entry, &list_main, link) {
+	if (object_match_name(entry->obj, name))
+	    return entry->obj;
+    }
+
+    for (needed = obj->needed;  needed != NULL;  needed = needed->next) {
+	if (needed->obj == NULL)
+	    continue;
+	if (object_match_name(needed->obj, name))
+	    return needed->obj;
+    }
+    _rtld_error("%s: Unexpected  inconsistency: dependency %s not found",
+	obj->path, name);
+    die();
+}
+
+static int
+check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj,
+    const Elf_Vernaux *vna)
+{
+    const Elf_Verdef *vd;
+    const char *vername;
+
+    vername = refobj->strtab + vna->vna_name;
+    vd = depobj->verdef;
+    if (vd == NULL) {
+	_rtld_error("%s: version %s required by %s not defined",
+	    depobj->path, vername, refobj->path);
+	return (-1);
+    }
+    for (;;) {
+	if (vd->vd_version != VER_DEF_CURRENT) {
+	    _rtld_error("%s: Unsupported version %d of Elf_Verdef entry",
+		depobj->path, vd->vd_version);
+	    return (-1);
+	}
+	if (vna->vna_hash == vd->vd_hash) {
+	    const Elf_Verdaux *aux = (const Elf_Verdaux *)
+		((char *)vd + vd->vd_aux);
+	    if (strcmp(vername, depobj->strtab + aux->vda_name) == 0)
+		return (0);
+	}
+	if (vd->vd_next == 0)
+	    break;
+	vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+    }
+    if (vna->vna_flags & VER_FLG_WEAK)
+	return (0);
+    _rtld_error("%s: version %s required by %s not found",
+	depobj->path, vername, refobj->path);
+    return (-1);
+}
+
+static int
+rtld_verify_object_versions(Obj_Entry *obj)
+{
+    const Elf_Verneed *vn;
+    const Elf_Verdef  *vd;
+    const Elf_Verdaux *vda;
+    const Elf_Vernaux *vna;
+    const Obj_Entry *depobj;
+    int maxvernum, vernum;
+
+    maxvernum = 0;
+    /*
+     * Walk over defined and required version records and figure out
+     * max index used by any of them. Do very basic sanity checking
+     * while there.
+     */
+    vn = obj->verneed;
+    while (vn != NULL) {
+	if (vn->vn_version != VER_NEED_CURRENT) {
+	    _rtld_error("%s: Unsupported version %d of Elf_Verneed entry",
+		obj->path, vn->vn_version);
+	    return (-1);
+	}
+	vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux);
+	for (;;) {
+	    vernum = VER_NEED_IDX(vna->vna_other);
+	    if (vernum > maxvernum)
+		maxvernum = vernum;
+	    if (vna->vna_next == 0)
+		 break;
+	    vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next);
+	}
+	if (vn->vn_next == 0)
+	    break;
+	vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next);
+    }
+
+    vd = obj->verdef;
+    while (vd != NULL) {
+	if (vd->vd_version != VER_DEF_CURRENT) {
+	    _rtld_error("%s: Unsupported version %d of Elf_Verdef entry",
+		obj->path, vd->vd_version);
+	    return (-1);
+	}
+	vernum = VER_DEF_IDX(vd->vd_ndx);
+	if (vernum > maxvernum)
+		maxvernum = vernum;
+	if (vd->vd_next == 0)
+	    break;
+	vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+    }
+
+    if (maxvernum == 0)
+	return (0);
+
+    /*
+     * Store version information in array indexable by version index.
+     * Verify that object version requirements are satisfied along the
+     * way.
+     */
+    obj->vernum = maxvernum + 1;
+    obj->vertab = calloc(obj->vernum, sizeof(Ver_Entry));
+
+    vd = obj->verdef;
+    while (vd != NULL) {
+	if ((vd->vd_flags & VER_FLG_BASE) == 0) {
+	    vernum = VER_DEF_IDX(vd->vd_ndx);
+	    assert(vernum <= maxvernum);
+	    vda = (const Elf_Verdaux *)((char *)vd + vd->vd_aux);
+	    obj->vertab[vernum].hash = vd->vd_hash;
+	    obj->vertab[vernum].name = obj->strtab + vda->vda_name;
+	    obj->vertab[vernum].file = NULL;
+	    obj->vertab[vernum].flags = 0;
+	}
+	if (vd->vd_next == 0)
+	    break;
+	vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+    }
+
+    vn = obj->verneed;
+    while (vn != NULL) {
+	depobj = locate_dependency(obj, obj->strtab + vn->vn_file);
+	vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux);
+	for (;;) {
+	    if (check_object_provided_version(obj, depobj, vna))
+		return (-1);
+	    vernum = VER_NEED_IDX(vna->vna_other);
+	    assert(vernum <= maxvernum);
+	    obj->vertab[vernum].hash = vna->vna_hash;
+	    obj->vertab[vernum].name = obj->strtab + vna->vna_name;
+	    obj->vertab[vernum].file = obj->strtab + vn->vn_file;
+	    obj->vertab[vernum].flags = (vna->vna_other & VER_NEED_HIDDEN) ?
+		VER_INFO_HIDDEN : 0;
+	    if (vna->vna_next == 0)
+		 break;
+	    vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next);
+	}
+	if (vn->vn_next == 0)
+	    break;
+	vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next);
+    }
+    return 0;
+}
+
+static int
+rtld_verify_versions(const Objlist *objlist)
+{
+    Objlist_Entry *entry;
+    int rc;
+
+    rc = 0;
+    STAILQ_FOREACH(entry, objlist, link) {
+	/*
+	 * Skip dummy objects or objects that have their version requirements
+	 * already checked.
+	 */
+	if (entry->obj->strtab == NULL || entry->obj->vertab != NULL)
+	    continue;
+	if (rtld_verify_object_versions(entry->obj) == -1) {
+	    rc = -1;
+	    if (ld_tracing == NULL)
+		break;
+	}
+    }
+    if (rc == 0 || ld_tracing != NULL)
+    	rc = rtld_verify_object_versions(&obj_rtld);
+    return rc;
+}
+
+const Ver_Entry *
+fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
+{
+    Elf_Versym vernum;
+
+    if (obj->vertab) {
+	vernum = VER_NDX(obj->versyms[symnum]);
+	if (vernum >= obj->vernum) {
+	    _rtld_error("%s: symbol %s has wrong verneed value %d",
+		obj->path, obj->strtab + symnum, vernum);
+	} else if (obj->vertab[vernum].hash != 0) {
+	    return &obj->vertab[vernum];
+	}
+    }
+    return NULL;
+}


More information about the Midnightbsd-cvs mailing list