rtld: Sync with FreeBSD after gnu_hash import
authorJohn Marino <draco@marino.st>
Fri, 4 May 2012 16:41:58 +0000 (18:41 +0200)
committerJohn Marino <draco@marino.st>
Sat, 5 May 2012 07:33:18 +0000 (09:33 +0200)
For the most part, FreeBSD took our gnu_hash implementation without much
modification.  Most of these changes are caused by whitespace differences
due to a different style scheme, and by declarating variables separately
from their assignments.  Notable exceptions were:
* FreeBSD didn't use int_fast32_t type
* FreeBSD keeps checking the first character before doing strcmp
* FreeBSD renamed the symlook_obj2 to symlook_obj1_*

The only additions were two debug statements.  This commits syncs the
following back to DragonFly:

FreeBSD SVN 234840 (30 APR 2012)
FreeBSD SVN 234841 (30 APR 2012)

libexec/rtld-elf/rtld.c

index d3fd379..dd921a8 100644 (file)
@@ -1,7 +1,7 @@
 /*-
  * 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.
  *
@@ -112,7 +112,6 @@ static void map_stacks_exec(RtldLockState *);
 static Obj_Entry *obj_from_addr(const void *);
 static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *);
 static void objlist_call_init(Objlist *, RtldLockState *);
-static void preinitialize_main_object (void);
 static void objlist_clear(Objlist *);
 static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *);
 static void objlist_init(Objlist *);
@@ -136,8 +135,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_obj2(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 *);
@@ -146,6 +145,7 @@ static void ref_dag(Obj_Entry *);
 static int origin_subst_one(char **, const char *, const char *,
   const char *, char *);
 static char *origin_subst(const char *, const char *);
+static void preinitialize_main_object (void);
 static int  rtld_verify_versions(const Objlist *);
 static int  rtld_verify_object_versions(Obj_Entry *);
 static void object_add_name(Obj_Entry *, const char *);
@@ -561,6 +561,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);
@@ -965,6 +968,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;
@@ -1055,8 +1063,8 @@ 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;
@@ -1068,14 +1076,14 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
 
        case DT_GNU_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_gnu = hashtab[0];
                obj->symndx_gnu = hashtab[1];
-               const Elf32_Word nmaskwords = hashtab[2];
-               const int bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords;
+               nmaskwords = hashtab[2];
+               bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords;
                /* Number of bitmask words is required to be power of 2 */
-               const bool nmw_power2 = ((nmaskwords & (nmaskwords - 1)) == 0);
+               nmw_power2 = ((nmaskwords & (nmaskwords - 1)) == 0);
                obj->maskwords_bm_gnu = nmaskwords - 1;
                obj->shift2_gnu = hashtab[3];
                obj->bloom_gnu = (Elf_Addr *) (hashtab + 4);
@@ -1243,10 +1251,10 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
        obj->dynsymcount = obj->nchains;
     else if (obj->valid_hash_gnu) {
        obj->dynsymcount = 0;
-       for (Elf32_Word bkt = 0; bkt < obj->nbuckets_gnu; bkt++) {
+       for (bkt = 0; bkt < obj->nbuckets_gnu; bkt++) {
            if (obj->buckets_gnu[bkt] == 0)
                continue;
-           const Elf32_Word *hashval = &obj->chain_zero_gnu[obj->buckets_gnu[bkt]];
+           hashval = &obj->chain_zero_gnu[obj->buckets_gnu[bkt]];
            do
                obj->dynsymcount++;
            while ((*hashval++ & 1u) == 0);
@@ -2168,6 +2176,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);
@@ -3720,9 +3730,9 @@ symlook_obj(SymLook *req, const Obj_Entry *obj)
      * the faster GNU version if available.
      */
     if (obj->valid_hash_gnu)
-       mres = symlook_obj2(req, obj);
+       mres = symlook_obj1_gnu(req, obj);
     else
-       mres = symlook_obj1(req, obj);
+       mres = symlook_obj1_sysv(req, obj);
 
     if (mres == 0) {
        if (obj->needed_filtees != NULL) {
@@ -3758,94 +3768,108 @@ static bool
 matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result,
        const unsigned long symnum)
 {
-    Elf_Versym verndx;
-    const Elf_Sym *symp = obj->symtab + symnum;
-    const char *strp = obj->strtab + symp->st_name;
-
-    switch (ELF_ST_TYPE(symp->st_info)) {
-    case STT_FUNC:
-    case STT_NOTYPE:
-    case STT_OBJECT:
-    case STT_COMMON:
-    case STT_GNU_IFUNC:
-       if (symp->st_value == 0)
-           return (false);
-       /* fallthrough */
-    case STT_TLS:
-       if (symp->st_shndx != SHN_UNDEF)
-           break;
-       else if (((req->flags & SYMLOOK_IN_PLT) == 0) &&
-             (ELF_ST_TYPE(symp->st_info) == STT_FUNC))
-           break;
-       /* fallthrough */
-    default:
-       return (false);
-    }
+       Elf_Versym verndx;
+       const Elf_Sym *symp;
+       const char *strp;
+
+       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:
+       case STT_COMMON:
+       case STT_GNU_IFUNC:
+               if (symp->st_value == 0)
+                       return (false);
+               /* fallthrough */
+       case STT_TLS:
+               if (symp->st_shndx != SHN_UNDEF)
+                       break;
+               else if (((req->flags & SYMLOOK_IN_PLT) == 0) &&
+                   (ELF_ST_TYPE(symp->st_info) == STT_FUNC))
+                       break;
+               /* fallthrough */
+       default:
+               return (false);
+       }
     if (strcmp(req->name, strp) != 0)
        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);
-               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) {
+       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);
+                               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);
-           }
-           else if (verndx >= VER_NDX_GIVEN) {
-               if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) {
-                   if (result->vsymp == NULL)
-                       result->vsymp = symp;
-                   result->vcount++;
+       }
+       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);
+                       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) ||
+                           (verndx >= VER_NDX_GIVEN) ||
+                           (obj->versyms[symnum] & VER_NDX_HIDDEN))
+                               return (false);
                }
-               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);
-           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) || (verndx >= VER_NDX_GIVEN) ||
-               (obj->versyms[symnum] & VER_NDX_HIDDEN))
-               return (false);
-       }
-    }
-    result->sym_out = symp;
-    return (true);
 }
 
 /*
@@ -3854,84 +3878,82 @@ matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result,
  * 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;
-
-    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);
+       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);
+       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_obj2(SymLook *req, const Obj_Entry *obj)
+symlook_obj1_gnu(SymLook *req, const Obj_Entry *obj)
 {
-    Elf_Addr bloom_word;
-    Elf32_Word bucket;
-    unsigned int h1, h2;
-    unsigned long symnum;
-    const int c = __ELF_WORD_SIZE;
-    Sym_Match_Result matchres;
-
-    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 / c) & obj->maskwords_bm_gnu];
-
-    /* calculate modulus 32 (64 for x86_64) of gnu hash and its derivative */
-    h1 = req->hash_gnu & (c - 1);
-    h2 = ((req->hash_gnu >> obj->shift2_gnu) & (c - 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);
-    const Elf32_Word *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;
+       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);
-           }
        }
-    while ((*hashval++ & 1u) == 0);
-    if (matchres.vcount == 1) {
-       req->sym_out = matchres.vsymp;
-       req->defobj_out = obj;
-       return (0);
-    }
-    return (ESRCH);
+       return (ESRCH);
 }
 
 static void