changeset 481:737774f27543

Update libexec/rtld-elf to @235221
author Aleksandr Rybalko <ray@ddteam.net>
date Thu, 10 May 2012 14:47:51 +0300
parents 31ffb4589c7c
children 32f69009f3f9
files head/libexec/rtld-elf/amd64/reloc.c head/libexec/rtld-elf/arm/reloc.c head/libexec/rtld-elf/i386/reloc.c head/libexec/rtld-elf/ia64/reloc.c head/libexec/rtld-elf/libmap.c head/libexec/rtld-elf/powerpc/reloc.c head/libexec/rtld-elf/powerpc64/reloc.c head/libexec/rtld-elf/rtld.c head/libexec/rtld-elf/rtld.h head/libexec/rtld-elf/sparc64/reloc.c
diffstat 10 files changed, 427 insertions(+), 158 deletions(-) [+]
line wrap: on
line diff
--- a/head/libexec/rtld-elf/amd64/reloc.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/amd64/reloc.c	Thu May 10 14:47:51 2012 +0300
@@ -22,7 +22,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: head/libexec/rtld-elf/amd64/reloc.c 233231 2012-03-20 13:20:49Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/amd64/reloc.c 234841 2012-04-30 13:31:10Z kib $
  */
 
 /*
@@ -132,7 +132,7 @@
 	 * limited amounts of stack available so we cannot use alloca().
 	 */
 	if (obj != obj_rtld) {
-	    cache = calloc(obj->nchains, sizeof(SymCache));
+	    cache = calloc(obj->dynsymcount, sizeof(SymCache));
 	    /* No need to check for NULL here */
 	} else
 	    cache = NULL;
--- a/head/libexec/rtld-elf/arm/reloc.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/arm/reloc.c	Thu May 10 14:47:51 2012 +0300
@@ -1,7 +1,7 @@
 /*	$NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $	*/
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/libexec/rtld-elf/arm/reloc.c 233231 2012-03-20 13:20:49Z kib $");
+__FBSDID("$FreeBSD: head/libexec/rtld-elf/arm/reloc.c 234841 2012-04-30 13:31:10Z kib $");
 #include <sys/param.h>
 #include <sys/mman.h>
 
@@ -328,7 +328,7 @@
  	 * The dynamic loader may be called from a thread, we have
 	 * limited amounts of stack available so we cannot use alloca().
 	 */
-	cache = calloc(obj->nchains, sizeof(SymCache));
+	cache = calloc(obj->dynsymcount, sizeof(SymCache));
 	/* No need to check for NULL here */
 
 	rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
--- a/head/libexec/rtld-elf/i386/reloc.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/i386/reloc.c	Thu May 10 14:47:51 2012 +0300
@@ -22,7 +22,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: head/libexec/rtld-elf/i386/reloc.c 233231 2012-03-20 13:20:49Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/i386/reloc.c 234841 2012-04-30 13:31:10Z kib $
  */
 
 /*
@@ -133,7 +133,7 @@
 	 * limited amounts of stack available so we cannot use alloca().
 	 */
 	if (obj != obj_rtld) {
-	    cache = calloc(obj->nchains, sizeof(SymCache));
+	    cache = calloc(obj->dynsymcount, sizeof(SymCache));
 	    /* No need to check for NULL here */
 	} else
 	    cache = NULL;
--- a/head/libexec/rtld-elf/ia64/reloc.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/ia64/reloc.c	Thu May 10 14:47:51 2012 +0300
@@ -22,7 +22,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: head/libexec/rtld-elf/ia64/reloc.c 233307 2012-03-22 14:20:51Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/ia64/reloc.c 234841 2012-04-30 13:31:10Z kib $
  */
 
 /*
@@ -104,7 +104,7 @@
 	struct fptr **fptrs;
 	size_t fbytes;
 
-	fbytes = obj->nchains * sizeof(struct fptr *);
+	fbytes = obj->dynsymcount * sizeof(struct fptr *);
 
 	/*
 	 * Avoid malloc, if requested. Happens when relocating
@@ -138,7 +138,7 @@
 	if (fptrs == NULL)
 		return;
 
-	fbytes = obj->nchains * sizeof(struct fptr *);
+	fbytes = obj->dynsymcount * sizeof(struct fptr *);
 	if (mapped)
 		munmap(fptrs, fbytes);
 	else
@@ -348,7 +348,7 @@
 	const Elf_Rela *relalim;
 	const Elf_Rela *rela;
 	SymCache *cache;
-	int bytes = obj->nchains * sizeof(SymCache);
+	int bytes = obj->dynsymcount * sizeof(SymCache);
 	int r = -1;
 
 	/*
--- a/head/libexec/rtld-elf/libmap.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/libmap.c	Thu May 10 14:47:51 2012 +0300
@@ -1,12 +1,14 @@
 /*
- * $FreeBSD: head/libexec/rtld-elf/libmap.c 232974 2012-03-14 15:39:59Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/libmap.c 235059 2012-05-05 16:37:14Z bapt $
  */
 
+#include <sys/types.h>
 #include <sys/param.h>
 #include <sys/fcntl.h>
 #include <sys/mman.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
+#include <dirent.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -39,9 +41,17 @@
 	TAILQ_ENTRY(lmp) lmp_link;
 };
 
+static TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head);
+struct lmc {
+	char *path;
+	TAILQ_ENTRY(lmc) next;
+};
+
 static int lm_count;
 
 static void lmc_parse(char *, size_t);
+static void lmc_parse_file(char *);
+static void lmc_parse_dir(char *);
 static void lm_add(const char *, const char *, const char *);
 static void lm_free(struct lm_list *);
 static char *lml_find(struct lm_list *, const char *);
@@ -61,37 +71,13 @@
 int
 lm_init(char *libmap_override)
 {
-	struct stat st;
-	char *lm_map, *p;
-	int fd;
+	char *p;
 
 	dbg("lm_init(\"%s\")", libmap_override);
 	TAILQ_INIT(&lmp_head);
 
-	fd = open(_PATH_LIBMAP_CONF, O_RDONLY);
-	if (fd == -1) {
-		dbg("lm_init: open(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
-		    rtld_strerror(errno));
-		goto override;
-	}
-	if (fstat(fd, &st) == -1) {
-		close(fd);
-		dbg("lm_init: fstat(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
-		    rtld_strerror(errno));
-		goto override;
-	}
-	lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-	if (lm_map == (const char *)MAP_FAILED) {
-		close(fd);
-		dbg("lm_init: mmap(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
-		    rtld_strerror(errno));
-		goto override;
-	}
-	close(fd);
-	lmc_parse(lm_map, st.st_size);
-	munmap(lm_map, st.st_size);
+	lmc_parse_file(_PATH_LIBMAP_CONF);
 
-override:
 	if (libmap_override) {
 		/*
 		 * Do some character replacement to make $LIBMAP look
@@ -116,14 +102,116 @@
 }
 
 static void
+lmc_parse_file(char *path)
+{
+	struct lmc *p;
+	struct stat st;
+	int fd;
+	char *rpath;
+	char *lm_map;
+
+	rpath = realpath(path, NULL);
+	if (rpath == NULL)
+		return;
+
+	TAILQ_FOREACH(p, &lmc_head, next) {
+		if (strcmp(p->path, rpath) == 0) {
+			free(rpath);
+			return;
+		}
+	}
+
+	fd = open(rpath, O_RDONLY);
+	if (fd == -1) {
+		dbg("lm_parse_file: open(\"%s\") failed, %s", rpath,
+		    rtld_strerror(errno));
+		free(rpath);
+		return;
+	}
+	if (fstat(fd, &st) == -1) {
+		close(fd);
+		dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath,
+		    rtld_strerror(errno));
+		free(rpath);
+		return;
+	}
+	lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (lm_map == (const char *)MAP_FAILED) {
+		close(fd);
+		dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath,
+		    rtld_strerror(errno));
+		free(rpath);
+		return;
+	}
+	close(fd);
+	p = xmalloc(sizeof(struct lmc));
+	p->path = rpath;
+	TAILQ_INSERT_HEAD(&lmc_head, p, next);
+	lmc_parse(lm_map, st.st_size);
+	munmap(lm_map, st.st_size);
+}
+
+static void
+lmc_parse_dir(char *idir)
+{
+	DIR *d;
+	struct dirent *dp;
+	struct lmc *p;
+	char conffile[MAXPATHLEN];
+	char *ext;
+	char *rpath;
+
+	rpath = realpath(idir, NULL);
+	if (rpath == NULL)
+		return;
+
+	TAILQ_FOREACH(p, &lmc_head, next) {
+		if (strcmp(p->path, rpath) == 0) {
+			free(rpath);
+			return;
+		}
+	}
+	d = opendir(idir);
+	if (d == NULL) {
+		free(rpath);
+		return;
+	}
+
+	p = xmalloc(sizeof(struct lmc));
+	p->path = rpath;
+	TAILQ_INSERT_HEAD(&lmc_head, p, next);
+
+	while ((dp = readdir(d)) != NULL) {
+		if (dp->d_ino == 0)
+			continue;
+		if (dp->d_type != DT_REG)
+			continue;
+		ext = strrchr(dp->d_name, '.');
+		if (ext == NULL)
+			continue;
+		if (strcmp(ext, ".conf") != 0)
+			continue;
+		if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN)
+			continue; /* too long */
+		if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN)
+			continue; /* too long */
+		if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN)
+			continue; /* too long */
+		lmc_parse_file(conffile);
+	}
+	closedir(d);
+}
+
+static void
 lmc_parse(char *lm_p, size_t lm_len)
 {
 	char *cp, *f, *t, *c, *p;
 	char prog[MAXPATHLEN];
-	char line[MAXPATHLEN + 2];
+	/* allow includedir + full length path */
+	char line[MAXPATHLEN + 13];
 	size_t cnt;
 	int i;
-	
+
 	cnt = 0;
 	p = NULL;
 	while (cnt < lm_len) {
@@ -181,7 +269,8 @@
 			while(rtld_isspace(*cp)) cp++;
 			if (!iseol(*cp)) continue;
 
-			strcpy(prog, c);
+			if (strlcpy(prog, c, sizeof prog) >= sizeof prog)
+				continue;
 			p = prog;
 			continue;
 		}
@@ -207,7 +296,12 @@
 		if (!iseol(*cp)) continue;
 
 		*cp = '\0';
-		lm_add(p, f, t);
+		if (strcmp(f, "includedir") == 0)
+			lmc_parse_dir(t);
+		else if (strcmp(f, "include") == 0)
+			lmc_parse_file(t);
+		else
+			lm_add(p, f, t);
 	}
 }
 
@@ -232,9 +326,17 @@
 lm_fini (void)
 {
 	struct lmp *lmp;
+	struct lmc *p;
 
 	dbg("%s()", __func__);
 
+	while (!TAILQ_EMPTY(&lmc_head)) {
+		p = TAILQ_FIRST(&lmc_head);
+		TAILQ_REMOVE(&lmc_head, p, next);
+		free(p->path);
+		free(p);
+	}
+
 	while (!TAILQ_EMPTY(&lmp_head)) {
 		lmp = TAILQ_FIRST(&lmp_head);
 		TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
--- a/head/libexec/rtld-elf/powerpc/reloc.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/powerpc/reloc.c	Thu May 10 14:47:51 2012 +0300
@@ -26,7 +26,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: head/libexec/rtld-elf/powerpc/reloc.c 233231 2012-03-20 13:20:49Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/powerpc/reloc.c 234841 2012-04-30 13:31:10Z kib $
  */
 
 #include <sys/param.h>
@@ -38,7 +38,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <machine/cpu.h>
-#include <machine/cpufunc.h>
+#include <machine/atomic.h>
 #include <machine/md_var.h>
 
 #include "debug.h"
@@ -299,7 +299,7 @@
 	 * limited amounts of stack available so we cannot use alloca().
 	 */
 	if (obj != obj_rtld) {
-		cache = calloc(obj->nchains, sizeof(SymCache));
+		cache = calloc(obj->dynsymcount, sizeof(SymCache));
 		/* No need to check for NULL here */
 	} else
 		cache = NULL;
@@ -499,7 +499,7 @@
 
 		jmptab = obj->pltgot + JMPTAB_BASE(N);
 		jmptab[reloff] = target;
-		powerpc_mb(); /* Order jmptab update before next changes */
+		mb(); /* Order jmptab update before next changes */
 
 		if (reloff < PLT_EXTENDED_BEGIN) {
 			/* for extended PLT entries, we keep the old code */
--- a/head/libexec/rtld-elf/powerpc64/reloc.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/powerpc64/reloc.c	Thu May 10 14:47:51 2012 +0300
@@ -26,7 +26,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: head/libexec/rtld-elf/powerpc64/reloc.c 233307 2012-03-22 14:20:51Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/powerpc64/reloc.c 234841 2012-04-30 13:31:10Z kib $
  */
 
 #include <sys/param.h>
@@ -287,7 +287,7 @@
 	const Elf_Rela *relalim;
 	const Elf_Rela *rela;
 	SymCache *cache;
-	int bytes = obj->nchains * sizeof(SymCache);
+	int bytes = obj->dynsymcount * sizeof(SymCache);
 	int r = -1;
 
 	/*
--- a/head/libexec/rtld-elf/rtld.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/rtld.c	Thu May 10 14:47:51 2012 +0300
@@ -1,7 +1,8 @@
 /*-
  * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
  * Copyright 2003 Alexander Kabaev <[email protected]>.
- * Copyright 2009, 2010, 2011 Konstantin Belousov <[email protected]>.
+ * Copyright 2009-2012 Konstantin Belousov <[email protected]>.
+ * Copyright 2012 John Marino <[email protected]>.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,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: head/libexec/rtld-elf/rtld.c 234170 2012-04-12 10:32:22Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/rtld.c 235054 2012-05-05 11:26:08Z kib $
  */
 
 /*
@@ -132,7 +133,8 @@
 static void symlook_init_from_req(SymLook *, const SymLook *);
 static int symlook_list(SymLook *, const Objlist *, DoneList *);
 static int symlook_needed(SymLook *, const Needed_Entry *, DoneList *);
-static int symlook_obj1(SymLook *, const Obj_Entry *);
+static int symlook_obj1_sysv(SymLook *, const Obj_Entry *);
+static int symlook_obj1_gnu(SymLook *, const Obj_Entry *);
 static void trace_loaded_objects(Obj_Entry *);
 static void unlink_object(Obj_Entry *);
 static void unload_object(Obj_Entry *);
@@ -149,6 +151,9 @@
 static void ld_utrace_log(int, void *, void *, size_t, int, const char *);
 static void rtld_fill_dl_phdr_info(const Obj_Entry *obj,
     struct dl_phdr_info *phdr_info);
+static uint32_t gnu_hash(const char *);
+static bool matched_symbol(SymLook *, const Obj_Entry *, Sym_Match_Result *,
+    const unsigned long);
 
 void r_debug_state(struct r_debug *, struct link_map *) __noinline;
 
@@ -485,6 +490,9 @@
     }
 
     digest_dynamic(obj_main, 0);
+    dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d",
+	obj_main->path, obj_main->valid_hash_sysv, obj_main->valid_hash_gnu,
+	obj_main->dynsymcount);
 
     linkmap_add(obj_main);
     linkmap_add(&obj_rtld);
@@ -822,6 +830,11 @@
     Needed_Entry **needed_tail = &obj->needed;
     Needed_Entry **needed_filtees_tail = &obj->needed_filtees;
     Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees;
+    const Elf_Hashelt *hashtab;
+    const Elf32_Word *hashval;
+    Elf32_Word bkt, nmaskwords;
+    int bloom_size32;
+    bool nmw_power2;
     int plttype = DT_REL;
 
     *dyn_rpath = NULL;
@@ -911,12 +924,35 @@
 
 	case DT_HASH:
 	    {
-		const Elf_Hashelt *hashtab = (const Elf_Hashelt *)
-		  (obj->relocbase + dynp->d_un.d_ptr);
+		hashtab = (const Elf_Hashelt *)(obj->relocbase +
+		    dynp->d_un.d_ptr);
 		obj->nbuckets = hashtab[0];
 		obj->nchains = hashtab[1];
 		obj->buckets = hashtab + 2;
 		obj->chains = obj->buckets + obj->nbuckets;
+		obj->valid_hash_sysv = obj->nbuckets > 0 && obj->nchains > 0 &&
+		  obj->buckets != NULL;
+	    }
+	    break;
+
+	case DT_GNU_HASH:
+	    {
+		hashtab = (const Elf_Hashelt *)(obj->relocbase +
+		    dynp->d_un.d_ptr);
+		obj->nbuckets_gnu = hashtab[0];
+		obj->symndx_gnu = hashtab[1];
+		nmaskwords = hashtab[2];
+		bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords;
+		/* Number of bitmask words is required to be power of 2 */
+		nmw_power2 = ((nmaskwords & (nmaskwords - 1)) == 0);
+		obj->maskwords_bm_gnu = nmaskwords - 1;
+		obj->shift2_gnu = hashtab[3];
+		obj->bloom_gnu = (Elf_Addr *) (hashtab + 4);
+		obj->buckets_gnu = hashtab + 4 + bloom_size32;
+		obj->chain_zero_gnu = obj->buckets_gnu + obj->nbuckets_gnu -
+		  obj->symndx_gnu;
+		obj->valid_hash_gnu = nmw_power2 && obj->nbuckets_gnu > 0 &&
+		  obj->buckets_gnu != NULL;
 	    }
 	    break;
 
@@ -1093,6 +1129,22 @@
 	obj->pltrelasize = obj->pltrelsize;
 	obj->pltrelsize = 0;
     }
+
+    /* Determine size of dynsym table (equal to nchains of sysv hash) */
+    if (obj->valid_hash_sysv)
+	obj->dynsymcount = obj->nchains;
+    else if (obj->valid_hash_gnu) {
+	obj->dynsymcount = 0;
+	for (bkt = 0; bkt < obj->nbuckets_gnu; bkt++) {
+	    if (obj->buckets_gnu[bkt] == 0)
+		continue;
+	    hashval = &obj->chain_zero_gnu[obj->buckets_gnu[bkt]];
+	    do
+		obj->dynsymcount++;
+	    while ((*hashval++ & 1u) == 0);
+	}
+	obj->dynsymcount += obj->symndx_gnu;
+    }
 }
 
 static void
@@ -1309,6 +1361,22 @@
 }
 
 /*
+ * The GNU hash function is the Daniel J. Bernstein hash clipped to 32 bits
+ * unsigned in case it's implemented with a wider type.
+ */
+static uint32_t
+gnu_hash(const char *s)
+{
+	uint32_t h;
+	unsigned char c;
+
+	h = 5381;
+	for (c = *s; c != '\0'; c = *++s)
+		h = h * 33 + c;
+	return (h & 0xffffffff);
+}
+
+/*
  * Find the library with the given name, and return its full pathname.
  * The returned string is dynamically allocated.  Generates an error
  * message and returns NULL if the library cannot be found.
@@ -1384,7 +1452,7 @@
      * If we have already found this symbol, get the information from
      * the cache.
      */
-    if (symnum >= refobj->nchains)
+    if (symnum >= refobj->dynsymcount)
 	return NULL;	/* Bad object */
     if (cache != NULL && cache[symnum].sym != NULL) {
 	*defobj_out = cache[symnum].obj;
@@ -1882,6 +1950,8 @@
 	object_add_name(obj, name);
     obj->path = path;
     digest_dynamic(obj, 0);
+    dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", obj->path,
+	obj->valid_hash_sysv, obj->valid_hash_gnu, obj->dynsymcount);
     if (obj->z_noopen && (flags & (RTLD_LO_DLOPEN | RTLD_LO_TRACE)) ==
       RTLD_LO_DLOPEN) {
 	dbg("refusing to load non-loadable \"%s\"", obj->path);
@@ -2165,8 +2235,8 @@
 	if (obj != rtldobj)
 	    dbg("relocating \"%s\"", obj->path);
 
-	if (obj->nbuckets == 0 || obj->nchains == 0 || obj->buckets == NULL ||
-	    obj->symtab == NULL || obj->strtab == NULL) {
+	if (obj->symtab == NULL || obj->strtab == NULL ||
+	  !(obj->valid_hash_sysv || obj->valid_hash_gnu)) {
 	    _rtld_error("%s: Shared object has no run-time symbol table",
 	      obj->path);
 	    return -1;
@@ -2838,7 +2908,7 @@
      * Walk the symbol list looking for the symbol whose address is
      * closest to the address sent in.
      */
-    for (symoffset = 0; symoffset < obj->nchains; symoffset++) {
+    for (symoffset = 0; symoffset < obj->dynsymcount; symoffset++) {
         def = obj->symtab + symoffset;
 
         /*
@@ -3409,7 +3479,17 @@
     SymLook req1;
     int flags, res, mres;
 
-    mres = symlook_obj1(req, obj);
+    /*
+     * If there is at least one valid hash at this point, we prefer to
+     * use the faster GNU version if available.
+     */
+    if (obj->valid_hash_gnu)
+	mres = symlook_obj1_gnu(req, obj);
+    else if (obj->valid_hash_sysv)
+	mres = symlook_obj1_sysv(req, obj);
+    else
+	return (EINVAL);
+
     if (mres == 0) {
 	if (obj->needed_filtees != NULL) {
 	    flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0;
@@ -3439,28 +3519,15 @@
     return (mres);
 }
 
-static int
-symlook_obj1(SymLook *req, const Obj_Entry *obj)
+/* Symbol match routine common to both hash functions */
+static bool
+matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result,
+    const unsigned long symnum)
 {
-    unsigned long symnum;
-    const Elf_Sym *vsymp;
-    Elf_Versym verndx;
-    int vcount;
-
-    if (obj->buckets == NULL)
-	return (ESRCH);
-
-    vsymp = NULL;
-    vcount = 0;
-    symnum = obj->buckets[req->hash % obj->nbuckets];
-
-    for (; symnum != STN_UNDEF; symnum = obj->chains[symnum]) {
+	Elf_Versym verndx;
 	const Elf_Sym *symp;
 	const char *strp;
 
-	if (symnum >= obj->nchains)
-	    return (ESRCH);	/* Bad object */
-
 	symp = obj->symtab + symnum;
 	strp = obj->strtab + symp->st_name;
 
@@ -3468,103 +3535,183 @@
 	case STT_FUNC:
 	case STT_NOTYPE:
 	case STT_OBJECT:
+	case STT_COMMON:
 	case STT_GNU_IFUNC:
-	    if (symp->st_value == 0)
-		continue;
+		if (symp->st_value == 0)
+			return (false);
 		/* fallthrough */
 	case STT_TLS:
-	    if (symp->st_shndx != SHN_UNDEF)
-		break;
+		if (symp->st_shndx != SHN_UNDEF)
+			break;
 #ifndef __mips__
-	    else if (((req->flags & SYMLOOK_IN_PLT) == 0) &&
-		 (ELF_ST_TYPE(symp->st_info) == STT_FUNC))
-		break;
+		else if (((req->flags & SYMLOOK_IN_PLT) == 0) &&
+		    (ELF_ST_TYPE(symp->st_info) == STT_FUNC))
+			break;
 		/* fallthrough */
 #endif
 	default:
-	    continue;
+		return (false);
 	}
 	if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0)
-	    continue;
+		return (false);
 
 	if (req->ventry == NULL) {
-	    if (obj->versyms != 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);
+				return (false);
+			}
+			/*
+			 * 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 by the calling
+			 * function. If symbol is global (verndx < 2)
+			 * accept it unconditionally.
+			 */
+			if ((req->flags & SYMLOOK_DLSYM) == 0 &&
+			    verndx == VER_NDX_GIVEN) {
+				result->sym_out = symp;
+				return (true);
+			}
+			else if (verndx >= VER_NDX_GIVEN) {
+				if ((obj->versyms[symnum] & VER_NDX_HIDDEN)
+				    == 0) {
+					if (result->vsymp == NULL)
+						result->vsymp = symp;
+					result->vcount++;
+				}
+				return (false);
+			}
+		}
+		result->sym_out = symp;
+		return (true);
+	}
+	if (obj->versyms == NULL) {
+		if (object_match_name(obj, req->ventry->name)) {
+			_rtld_error("%s: object %s should provide version %s "
+			    "for symbol %s", obj_rtld.path, obj->path,
+			    req->ventry->name, obj->strtab + symnum);
+			return (false);
+		}
+	} 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 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 ((req->flags & SYMLOOK_DLSYM) == 0 &&
-		  verndx == VER_NDX_GIVEN) {
-		    req->sym_out = symp;
-		    req->defobj_out = obj;
-		    return (0);
-		}
-		else if (verndx >= VER_NDX_GIVEN) {
-		    if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) {
-			if (vsymp == NULL)
-			    vsymp = symp;
-			vcount ++;
-		    }
-		    continue;
-		}
-	    }
-	    req->sym_out = symp;
-	    req->defobj_out = obj;
-	    return (0);
-	} else {
-	    if (obj->versyms == NULL) {
-		if (object_match_name(obj, req->ventry->name)) {
-		    _rtld_error("%s: object %s should provide version %s for "
-			"symbol %s", obj_rtld.path, obj->path,
-			req->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;
+			_rtld_error("%s: symbol %s references wrong version %d",
+			    obj->path, obj->strtab + symnum, verndx);
+			return (false);
 		}
 		if (obj->vertab[verndx].hash != req->ventry->hash ||
 		    strcmp(obj->vertab[verndx].name, req->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 ((req->flags & SYMLOOK_DLSYM) ||
-			(obj->versyms[symnum] & VER_NDX_HIDDEN) ||
-			(verndx >= VER_NDX_GIVEN))
-			continue;
+			/*
+			 * 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 ((req->flags & SYMLOOK_DLSYM) ||
+			    (verndx >= VER_NDX_GIVEN) ||
+			    (obj->versyms[symnum] & VER_NDX_HIDDEN))
+				return (false);
+		}
+	}
+	result->sym_out = symp;
+	return (true);
+}
+
+/*
+ * Search for symbol using SysV hash function.
+ * obj->buckets is known not to be NULL at this point; the test for this was
+ * performed with the obj->valid_hash_sysv assignment.
+ */
+static int
+symlook_obj1_sysv(SymLook *req, const Obj_Entry *obj)
+{
+	unsigned long symnum;
+	Sym_Match_Result matchres;
+
+	matchres.sym_out = NULL;
+	matchres.vsymp = NULL;
+	matchres.vcount = 0;
+
+	for (symnum = obj->buckets[req->hash % obj->nbuckets];
+	    symnum != STN_UNDEF; symnum = obj->chains[symnum]) {
+		if (symnum >= obj->nchains)
+			return (ESRCH);	/* Bad object */
+
+		if (matched_symbol(req, obj, &matchres, symnum)) {
+			req->sym_out = matchres.sym_out;
+			req->defobj_out = obj;
+			return (0);
 		}
-	    }
-	    req->sym_out = symp;
-	    req->defobj_out = obj;
-	    return (0);
+	}
+	if (matchres.vcount == 1) {
+		req->sym_out = matchres.vsymp;
+		req->defobj_out = obj;
+		return (0);
 	}
-    }
-    if (vcount == 1) {
-	req->sym_out = vsymp;
-	req->defobj_out = obj;
-	return (0);
-    }
-    return (ESRCH);
+	return (ESRCH);
+}
+
+/* Search for symbol using GNU hash function */
+static int
+symlook_obj1_gnu(SymLook *req, const Obj_Entry *obj)
+{
+	Elf_Addr bloom_word;
+	const Elf32_Word *hashval;
+	Elf32_Word bucket;
+	Sym_Match_Result matchres;
+	unsigned int h1, h2;
+	unsigned long symnum;
+
+	matchres.sym_out = NULL;
+	matchres.vsymp = NULL;
+	matchres.vcount = 0;
+
+	/* Pick right bitmask word from Bloom filter array */
+	bloom_word = obj->bloom_gnu[(req->hash_gnu / __ELF_WORD_SIZE) &
+	    obj->maskwords_bm_gnu];
+
+	/* Calculate modulus word size of gnu hash and its derivative */
+	h1 = req->hash_gnu & (__ELF_WORD_SIZE - 1);
+	h2 = ((req->hash_gnu >> obj->shift2_gnu) & (__ELF_WORD_SIZE - 1));
+
+	/* Filter out the "definitely not in set" queries */
+	if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0)
+		return (ESRCH);
+
+	/* Locate hash chain and corresponding value element*/
+	bucket = obj->buckets_gnu[req->hash_gnu % obj->nbuckets_gnu];
+	if (bucket == 0)
+		return (ESRCH);
+	hashval = &obj->chain_zero_gnu[bucket];
+	do {
+		if (((*hashval ^ req->hash_gnu) >> 1) == 0) {
+			symnum = hashval - obj->chain_zero_gnu;
+			if (matched_symbol(req, obj, &matchres, symnum)) {
+				req->sym_out = matchres.sym_out;
+				req->defobj_out = obj;
+				return (0);
+			}
+		}
+	} while ((*hashval++ & 1) == 0);
+	if (matchres.vcount == 1) {
+		req->sym_out = matchres.vsymp;
+		req->defobj_out = obj;
+		return (0);
+	}
+	return (ESRCH);
 }
 
 static void
@@ -4350,6 +4497,7 @@
 	bzero(dst, sizeof(*dst));
 	dst->name = name;
 	dst->hash = elf_hash(name);
+	dst->hash_gnu = gnu_hash(name);
 }
 
 static void
@@ -4358,6 +4506,7 @@
 
 	dst->name = src->name;
 	dst->hash = src->hash;
+	dst->hash_gnu = src->hash_gnu;
 	dst->ventry = src->ventry;
 	dst->flags = src->flags;
 	dst->defobj_out = NULL;
--- a/head/libexec/rtld-elf/rtld.h	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/rtld.h	Thu May 10 14:47:51 2012 +0300
@@ -22,7 +22,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: head/libexec/rtld-elf/rtld.h 233546 2012-03-27 14:10:15Z kib $
+ * $FreeBSD: head/libexec/rtld-elf/rtld.h 234841 2012-04-30 13:31:10Z kib $
  */
 
 #ifndef RTLD_H /* { */
@@ -126,6 +126,12 @@
 	const char  *file;
 } Ver_Entry;
 
+typedef struct Struct_Sym_Match_Result {
+    const Elf_Sym *sym_out;
+    const Elf_Sym *vsymp;
+    int vcount;
+} Sym_Match_Result;
+
 #define VER_INFO_HIDDEN	0x01
 
 /*
@@ -204,7 +210,16 @@
     const Elf_Hashelt *buckets;	/* Hash table buckets array */
     unsigned long nbuckets;	/* Number of buckets */
     const Elf_Hashelt *chains;	/* Hash table chain array */
-    unsigned long nchains;	/* Number of chains */
+    unsigned long nchains;	/* Number of entries in chain array */
+
+    Elf32_Word nbuckets_gnu;		/* Number of GNU hash buckets*/
+    Elf32_Word symndx_gnu;		/* 1st accessible symbol on dynsym table */
+    Elf32_Word maskwords_bm_gnu;  	/* Bloom filter words - 1 (bitmask) */
+    Elf32_Word shift2_gnu;		/* Bloom filter shift count */
+    Elf32_Word dynsymcount;		/* Total entries in dynsym table */
+    Elf_Addr *bloom_gnu;		/* Bloom filter used by GNU hash func */
+    const Elf_Hashelt *buckets_gnu;	/* GNU hash table bucket array */
+    const Elf_Hashelt *chain_zero_gnu;	/* GNU hash table value array (Zeroed) */
 
     char *rpath;		/* Search path specified in object */
     Needed_Entry *needed;	/* Shared objects needed by this one (%) */
@@ -251,6 +266,8 @@
     bool irelative : 1;		/* Object has R_MACHDEP_IRELATIVE relocs */
     bool gnu_ifunc : 1;		/* Object has references to STT_GNU_IFUNC */
     bool crt_no_init : 1;	/* Object' crt does not call _init/_fini */
+    bool valid_hash_sysv : 1;	/* A valid System V hash hash tag is available */
+    bool valid_hash_gnu : 1;	/* A valid GNU hash tag is available */
 
     struct link_map linkmap;	/* For GDB and dlinfo() */
     Objlist dldags;		/* Object belongs to these dlopened DAGs (%) */
@@ -310,6 +327,7 @@
 typedef struct Struct_SymLook {
     const char *name;
     unsigned long hash;
+    uint32_t hash_gnu;
     const Ver_Entry *ventry;
     int flags;
     const Obj_Entry *defobj_out;
--- a/head/libexec/rtld-elf/sparc64/reloc.c	Tue May 08 23:49:09 2012 +0300
+++ b/head/libexec/rtld-elf/sparc64/reloc.c	Thu May 10 14:47:51 2012 +0300
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/libexec/rtld-elf/sparc64/reloc.c 233231 2012-03-20 13:20:49Z kib $");
+__FBSDID("$FreeBSD: head/libexec/rtld-elf/sparc64/reloc.c 234841 2012-04-30 13:31:10Z kib $");
 
 #include <sys/param.h>
 #include <sys/mman.h>
@@ -305,7 +305,7 @@
 	 * limited amounts of stack available so we cannot use alloca().
 	 */
 	if (obj != obj_rtld) {
-		cache = calloc(obj->nchains, sizeof(SymCache));
+		cache = calloc(obj->dynsymcount, sizeof(SymCache));
 		/* No need to check for NULL here */
 	} else
 		cache = NULL;