Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 13 May 2012 12:50:43 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r235396 - in stable/9/libexec/rtld-elf: . amd64 arm i386 ia64 powerpc powerpc64 sparc64
Message-ID:  <201205131250.q4DCohoD083772@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Sun May 13 12:50:42 2012
New Revision: 235396
URL: http://svn.freebsd.org/changeset/base/235396

Log:
  MFC r234840:
  Split the symlook_obj1 into a loop iterating over the ELF object symbol
  hash elements, and a helper matched_symbol() which match the given hash
  entry and request, performing needed type and version checks.
  
  MFC r234841:
  Add GNU hash support for rtld.
  
  MFC r235054:
  Work around a situation where symlook_obj() could be called for the
  object for which digest_dynamic1() was not done yet. Just return
  EINVAL and do not try to dereference NULL buckets hash array.

Modified:
  stable/9/libexec/rtld-elf/amd64/reloc.c
  stable/9/libexec/rtld-elf/arm/reloc.c
  stable/9/libexec/rtld-elf/i386/reloc.c
  stable/9/libexec/rtld-elf/ia64/reloc.c
  stable/9/libexec/rtld-elf/powerpc/reloc.c
  stable/9/libexec/rtld-elf/powerpc64/reloc.c
  stable/9/libexec/rtld-elf/rtld.c
  stable/9/libexec/rtld-elf/rtld.h
  stable/9/libexec/rtld-elf/sparc64/reloc.c
Directory Properties:
  stable/9/libexec/rtld-elf/   (props changed)

Modified: stable/9/libexec/rtld-elf/amd64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/amd64/reloc.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/amd64/reloc.c	Sun May 13 12:50:42 2012	(r235396)
@@ -132,7 +132,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry 
 	 * 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;

Modified: stable/9/libexec/rtld-elf/arm/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/arm/reloc.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/arm/reloc.c	Sun May 13 12:50:42 2012	(r235396)
@@ -268,7 +268,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry 
  	 * 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);

Modified: stable/9/libexec/rtld-elf/i386/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/i386/reloc.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/i386/reloc.c	Sun May 13 12:50:42 2012	(r235396)
@@ -133,7 +133,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry 
 	 * 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;

Modified: stable/9/libexec/rtld-elf/ia64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/ia64/reloc.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/ia64/reloc.c	Sun May 13 12:50:42 2012	(r235396)
@@ -104,7 +104,7 @@ alloc_fptrs(Obj_Entry *obj, bool mapped)
 	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 @@ free_fptrs(Obj_Entry *obj, bool mapped)
 	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 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry 
 	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;
 
 	/*

Modified: stable/9/libexec/rtld-elf/powerpc/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/powerpc/reloc.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/powerpc/reloc.c	Sun May 13 12:50:42 2012	(r235396)
@@ -299,7 +299,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry 
 	 * 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;

Modified: stable/9/libexec/rtld-elf/powerpc64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/powerpc64/reloc.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/powerpc64/reloc.c	Sun May 13 12:50:42 2012	(r235396)
@@ -287,7 +287,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry 
 	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;
 
 	/*

Modified: stable/9/libexec/rtld-elf/rtld.c
==============================================================================
--- stable/9/libexec/rtld-elf/rtld.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/rtld.c	Sun May 13 12:50:42 2012	(r235396)
@@ -1,7 +1,8 @@
 /*-
  * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
  * Copyright 2003 Alexander Kabaev <kan@FreeBSD.ORG>.
- * Copyright 2009, 2010, 2011 Konstantin Belousov <kib@FreeBSD.ORG>.
+ * Copyright 2009-2012 Konstantin Belousov <kib@FreeBSD.ORG>.
+ * Copyright 2012 John Marino <draco@marino.st>.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -132,7 +133,8 @@ static int symlook_global(SymLook *, Don
 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 int  object_match_name(const Obj_
 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 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
     }
 
     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 @@ digest_dynamic1(Obj_Entry *obj, int earl
     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 @@ digest_dynamic1(Obj_Entry *obj, int earl
 
 	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 @@ digest_dynamic1(Obj_Entry *obj, int earl
 	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 @@ elf_hash(const char *name)
 }
 
 /*
+ * 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 @@ find_symdef(unsigned long symnum, const 
      * 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 @@ do_load_object(int fd, const char *name,
 	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 @@ relocate_objects(Obj_Entry *first, bool 
 	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 @@ dladdr(const void *addr, Dl_info *info)
      * 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_obj(SymLook *req, const Obj_Entr
     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 @@ symlook_obj(SymLook *req, const Obj_Entr
     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 @@ symlook_obj1(SymLook *req, const Obj_Ent
 	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) {
-		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);
+		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);
+			}
 		}
-		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) {
+		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);
-		    continue;
+			_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 {
+	} 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);
 		}
-	    }
-	    req->sym_out = symp;
-	    req->defobj_out = obj;
-	    return (0);
 	}
-    }
-    if (vcount == 1) {
-	req->sym_out = vsymp;
-	req->defobj_out = obj;
-	return (0);
-    }
-    return (ESRCH);
+	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);
+		}
+	}
+	if (matchres.vcount == 1) {
+		req->sym_out = matchres.vsymp;
+		req->defobj_out = obj;
+		return (0);
+	}
+	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
@@ -4353,6 +4500,7 @@ symlook_init(SymLook *dst, const char *n
 	bzero(dst, sizeof(*dst));
 	dst->name = name;
 	dst->hash = elf_hash(name);
+	dst->hash_gnu = gnu_hash(name);
 }
 
 static void
@@ -4361,6 +4509,7 @@ symlook_init_from_req(SymLook *dst, cons
 
 	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;

Modified: stable/9/libexec/rtld-elf/rtld.h
==============================================================================
--- stable/9/libexec/rtld-elf/rtld.h	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/rtld.h	Sun May 13 12:50:42 2012	(r235396)
@@ -126,6 +126,12 @@ typedef struct Struct_Ver_Entry {
 	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 @@ typedef struct Struct_Obj_Entry {
     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 @@ typedef struct Struct_Obj_Entry {
     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 @@ struct Struct_RtldLockState {
 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;

Modified: stable/9/libexec/rtld-elf/sparc64/reloc.c
==============================================================================
--- stable/9/libexec/rtld-elf/sparc64/reloc.c	Sun May 13 11:34:05 2012	(r235395)
+++ stable/9/libexec/rtld-elf/sparc64/reloc.c	Sun May 13 12:50:42 2012	(r235396)
@@ -305,7 +305,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry 
 	 * 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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201205131250.q4DCohoD083772>