aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2012-04-30 13:31:10 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2012-04-30 13:31:10 +0000
commitf62651920d526d8ef7a8ea66487e5bd1814a7a6f (patch)
treeeb5584ba562f97d5359e6f5373fcc4ff48e9387b
parent34cb87ba95faba09438cf9288b5cc9124fcc5700 (diff)
downloadsrc-f62651920d526d8ef7a8ea66487e5bd1814a7a6f.tar.gz
src-f62651920d526d8ef7a8ea66487e5bd1814a7a6f.zip
Add GNU hash support for rtld.
Based on dragonflybsd support for GNU hash by John Marino <draco marino st> Reviewed by: kan Tested by: bapt MFC after: 2 weeks
Notes
Notes: svn path=/head/; revision=234841
-rw-r--r--libexec/rtld-elf/amd64/reloc.c2
-rw-r--r--libexec/rtld-elf/arm/reloc.c2
-rw-r--r--libexec/rtld-elf/i386/reloc.c2
-rw-r--r--libexec/rtld-elf/ia64/reloc.c6
-rw-r--r--libexec/rtld-elf/powerpc/reloc.c2
-rw-r--r--libexec/rtld-elf/powerpc64/reloc.c2
-rw-r--r--libexec/rtld-elf/rtld.c150
-rw-r--r--libexec/rtld-elf/rtld.h14
-rw-r--r--libexec/rtld-elf/sparc64/reloc.c2
9 files changed, 163 insertions, 19 deletions
diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c
index 9c755bc1eccf..7b002b2817cb 100644
--- a/libexec/rtld-elf/amd64/reloc.c
+++ b/libexec/rtld-elf/amd64/reloc.c
@@ -132,7 +132,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
* 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;
diff --git a/libexec/rtld-elf/arm/reloc.c b/libexec/rtld-elf/arm/reloc.c
index aaad2daa1641..005c468af74c 100644
--- a/libexec/rtld-elf/arm/reloc.c
+++ b/libexec/rtld-elf/arm/reloc.c
@@ -328,7 +328,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
* 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);
diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c
index 5f4e24721d0c..58073dbaa574 100644
--- a/libexec/rtld-elf/i386/reloc.c
+++ b/libexec/rtld-elf/i386/reloc.c
@@ -133,7 +133,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
* 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;
diff --git a/libexec/rtld-elf/ia64/reloc.c b/libexec/rtld-elf/ia64/reloc.c
index 01e20b8f9a71..1a41cb3cf8d2 100644
--- a/libexec/rtld-elf/ia64/reloc.c
+++ b/libexec/rtld-elf/ia64/reloc.c
@@ -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 *obj_rtld, int flags,
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;
/*
diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c
index 40a19ea213f5..838cfe6f5157 100644
--- a/libexec/rtld-elf/powerpc/reloc.c
+++ b/libexec/rtld-elf/powerpc/reloc.c
@@ -299,7 +299,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
* 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;
diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c
index 92df83d11834..b13500f5b43f 100644
--- a/libexec/rtld-elf/powerpc64/reloc.c
+++ b/libexec/rtld-elf/powerpc64/reloc.c
@@ -287,7 +287,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
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;
/*
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index b92d40457b87..9276a524e0c4 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -133,7 +133,8 @@ static int symlook_global(SymLook *, DoneList *);
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 *);
@@ -150,6 +151,7 @@ static int object_match_name(const Obj_Entry *, const char *);
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);
@@ -488,6 +490,9 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
}
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);
@@ -825,6 +830,11 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
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;
@@ -914,12 +924,35 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
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;
@@ -1096,6 +1129,22 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
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
@@ -1312,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.
@@ -1387,7 +1452,7 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj,
* 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;
@@ -1885,6 +1950,8 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp,
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);
@@ -2168,8 +2235,8 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj,
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;
@@ -2841,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;
/*
@@ -3412,7 +3479,15 @@ symlook_obj(SymLook *req, const Obj_Entry *obj)
SymLook req1;
int flags, res, mres;
- mres = symlook_obj1(req, obj);
+ /*
+ * There is at least one valid hash at this point, and we prefer to use
+ * the faster GNU version if available.
+ */
+ if (obj->valid_hash_gnu)
+ mres = symlook_obj1_gnu(req, obj);
+ else
+ mres = symlook_obj1_sysv(req, obj);
+
if (mres == 0) {
if (obj->needed_filtees != NULL) {
flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0;
@@ -3553,8 +3628,13 @@ matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result,
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(SymLook *req, const Obj_Entry *obj)
+symlook_obj1_sysv(SymLook *req, const Obj_Entry *obj)
{
unsigned long symnum;
Sym_Match_Result matchres;
@@ -3582,6 +3662,56 @@ symlook_obj1(SymLook *req, const Obj_Entry *obj)
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
trace_loaded_objects(Obj_Entry *obj)
{
@@ -4365,6 +4495,7 @@ symlook_init(SymLook *dst, const char *name)
bzero(dst, sizeof(*dst));
dst->name = name;
dst->hash = elf_hash(name);
+ dst->hash_gnu = gnu_hash(name);
}
static void
@@ -4373,6 +4504,7 @@ symlook_init_from_req(SymLook *dst, const SymLook *src)
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;
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index 314e522746af..47389fe6efa3 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -210,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 (%) */
@@ -257,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 (%) */
@@ -316,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;
diff --git a/libexec/rtld-elf/sparc64/reloc.c b/libexec/rtld-elf/sparc64/reloc.c
index ce0d9459d492..21fae5c05280 100644
--- a/libexec/rtld-elf/sparc64/reloc.c
+++ b/libexec/rtld-elf/sparc64/reloc.c
@@ -305,7 +305,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
* 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;