OSDN Git Service

avr32: common do_rem is good for it
[uclinux-h8/uClibc.git] / ldso / ldso / dl-hash.c
index 4eab974..2c659dc 100644 (file)
-/* Run an ELF binary on a linux system.
-
-   Copyright (C) 1993-1996, Eric Youngdale.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-\f
-
-
-/* Various symbol table handling functions, including symbol lookup */
-
-/*#include <stdlib.h>*/
-#include "string.h"
-#include <linux/unistd.h>
-#include <linux/elf.h>
-
-#include "libdl/dlfcn.h"
-#include "hash.h"
-#include "linuxelf.h"
-#include "syscall.h"
-#include "string.h"
-#include "sysdep.h"
-
+/* vi: set sw=4 ts=4: */
 /*
- * This is the start of the linked list that describes all of the files present
- * in the system with pointers to all of the symbol, string, and hash tables, 
- * as well as all of the other good stuff in the binary.
+ * Program to load an ELF binary on a linux system, and run it
+ * after resolving ELF shared library symbols
+ *
+ * Copyright (C) 2004 by Joakim Tjernlund <joakim.tjernlund@lumentis.se>
+ * Copyright (C) 2000-2006 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
+ *                             David Engel, Hongjiu Lu and Mitch D'Souza
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. The name of the above contributors may not be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
-struct elf_resolve * _dl_loaded_modules = NULL;
 
+/* Various symbol table handling functions, including symbol lookup */
 /*
  * This is the list of modules that are loaded when the image is first
  * started.  As we add more via dlopen, they get added into other
  * chains.
  */
-struct dyn_elf * _dl_symbol_tables = NULL;
+struct dyn_elf *_dl_symbol_tables = NULL;
 
 /*
  * This is the list of modules that are loaded via dlopen.  We may need
  * to search these for RTLD_GLOBAL files.
  */
-struct dyn_elf * _dl_handles = NULL;
+struct dyn_elf *_dl_handles = NULL;
+
+#ifdef __LDSO_GNU_HASH_SUPPORT__
+/* This is the new hash function that is used by the ELF linker to generate the
+ * GNU hash table that each executable and library will have if --hash-style=[gnu,both]
+ * is passed to the linker. We need it to decode the GNU hash table.  */
+static __inline__ Elf_Symndx _dl_gnu_hash (const unsigned char *name)
+{
+  unsigned long h = 5381;
+  unsigned char c;
+  for (c = *name; c != '\0'; c = *++name)
+    h = h * 33 + c;
+  return h & 0xffffffff;
+}
+#endif
+
+/* This is the hash function that is used by the ELF linker to generate the
+ * hash table that each executable and library is required to have.  We need
+ * it to decode the hash table.  */
+static __inline__ Elf_Symndx _dl_elf_hash(const unsigned char *name)
+{
+       unsigned long hash=0;
+       unsigned long tmp;
 
+       while (*name) {
+               hash = (hash << 4) + *name++;
+               tmp = hash & 0xf0000000;
+               /* The algorithm specified in the ELF ABI is as follows:
+                  if (tmp != 0)
+                      hash ^= tmp >> 24;
+                  hash &= ~tmp;
+                  But the following is equivalent and a lot
+                  faster, especially on modern processors. */
+               hash ^= tmp;
+               hash ^= tmp >> 24;
+       }
+       return hash;
+}
 
 /*
- * This is the hash function that is used by the ELF linker to generate
- * the hash table that each executable and library is required to
- * have.  We need it to decode the hash table.
+ * We call this function when we have just read an ELF library or executable.
+ * We add the relevant info to the symbol chain, so that we can resolve all
+ * externals properly.
  */
+struct elf_resolve *_dl_add_elf_hash_table(const char *libname,
+       DL_LOADADDR_TYPE loadaddr, unsigned long *dynamic_info, unsigned long dynamic_addr,
+       attribute_unused unsigned long dynamic_size)
+{
+       Elf_Symndx *hash_addr;
+       struct elf_resolve *tpnt;
+       int i;
+
+       tpnt = _dl_malloc(sizeof(struct elf_resolve));
+       _dl_memset(tpnt, 0, sizeof(struct elf_resolve));
+
+       if (!_dl_loaded_modules)
+               _dl_loaded_modules = tpnt;
+       else {
+               struct elf_resolve *t = _dl_loaded_modules;
+               while (t->next)
+                       t = t->next;
+               t->next = tpnt;
+               t->next->prev = t;
+               tpnt = t->next;
+       }
+
+       tpnt->next = NULL;
+       tpnt->init_flag = 0;
+       tpnt->libname = _dl_strdup(libname);
+       tpnt->dynamic_addr = (ElfW(Dyn) *)dynamic_addr;
+       tpnt->libtype = loaded_file;
+
+#ifdef __DSBT__
+       if (dynamic_info[DT_DSBT_BASE_IDX] != 0)
+               tpnt->dsbt_table = (void *)dynamic_info[DT_DSBT_BASE_IDX];
+       if (dynamic_info[DT_DSBT_SIZE_IDX] != 0)
+               tpnt->dsbt_size = dynamic_info[DT_DSBT_SIZE_IDX];
+       if (dynamic_info[DT_DSBT_INDEX_IDX] != 0)
+               tpnt->dsbt_index = dynamic_info[DT_DSBT_INDEX_IDX];
+#endif /* __DSBT__ */
 
-unsigned long _dl_elf_hash(const char * name){
-  unsigned long hash = 0;
-  unsigned long tmp;
+#ifdef __LDSO_GNU_HASH_SUPPORT__
+       if (dynamic_info[DT_GNU_HASH_IDX] != 0) {
+               Elf32_Word *hash32 = (Elf_Symndx*)dynamic_info[DT_GNU_HASH_IDX];
 
-  while (*name){
-    hash = (hash << 4) + *name++;
-    if((tmp = hash & 0xf0000000)) hash ^= tmp >> 24;
-    hash &= ~tmp;
-  };
-  return hash;
+               tpnt->nbucket = *hash32++;
+               Elf32_Word symbias = *hash32++;
+               Elf32_Word bitmask_nwords = *hash32++;
+               /* Must be a power of two.  */
+               _dl_assert ((bitmask_nwords & (bitmask_nwords - 1)) == 0);
+               tpnt->l_gnu_bitmask_idxbits = bitmask_nwords - 1;
+               tpnt->l_gnu_shift = *hash32++;
+
+               tpnt->l_gnu_bitmask = (ElfW(Addr) *) hash32;
+               hash32 += __ELF_NATIVE_CLASS / 32 * bitmask_nwords;
+
+               tpnt->l_gnu_buckets = hash32;
+               hash32 += tpnt->nbucket;
+               tpnt->l_gnu_chain_zero = hash32 - symbias;
+       } else
+       /* Fall using old SysV hash table if GNU hash is not present */
+#endif
+
+       if (dynamic_info[DT_HASH] != 0) {
+               hash_addr = (Elf_Symndx*)dynamic_info[DT_HASH];
+               tpnt->nbucket = *hash_addr++;
+               tpnt->nchain = *hash_addr++;
+               tpnt->elf_buckets = hash_addr;
+               hash_addr += tpnt->nbucket;
+               tpnt->chains = hash_addr;
+       }
+       tpnt->loadaddr = loadaddr;
+       for (i = 0; i < DYNAMIC_SIZE; i++)
+               tpnt->dynamic_info[i] = dynamic_info[i];
+       return tpnt;
 }
 
-/*
- * Check to see if a library has already been added to the hash chain.
- */
-struct elf_resolve * _dl_check_hashed_files(char * libname){
-  struct elf_resolve * tpnt;
-  int len = _dl_strlen(libname);
 
-  for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) {
-    if (_dl_strncmp(tpnt->libname, libname, len) == 0 &&
-       (tpnt->libname[len] == '\0' || tpnt->libname[len] == '.'))
-      return tpnt;
-  }
+/* Routine to check whether the symbol matches.  */
+static __attribute_noinline__ const ElfW(Sym) *
+check_match (const ElfW(Sym) *sym, char *strtab, const char* undef_name, int type_class)
+{
+
+#if defined(USE_TLS) && USE_TLS
+       if ((sym->st_value == 0 && (ELF_ST_TYPE(sym->st_info) != STT_TLS))
+                     || (type_class & (sym->st_shndx == SHN_UNDEF)))
+               /* No value or undefined symbol itself */
+               return NULL;
+
+       if (ELF_ST_TYPE(sym->st_info) > STT_FUNC
+               && ELF_ST_TYPE(sym->st_info) != STT_COMMON
+               && ELF_ST_TYPE(sym->st_info) != STT_TLS)
+               /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC and STT_COMMON
+                * entries (and STT_TLS if TLS is supported) since these
+                * are no code/data definitions.
+                */
+               return NULL;
+#else
+       if (type_class & (sym->st_shndx == SHN_UNDEF))
+               /* undefined symbol itself */
+               return NULL;
 
-  return NULL;
+       if (sym->st_value == 0)
+               /* No value */
+               return NULL;
+
+       if (ELF_ST_TYPE(sym->st_info) > STT_FUNC
+               && ELF_ST_TYPE(sym->st_info) != STT_COMMON)
+               /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC
+                * and STT_COMMON entries since these are no
+                * code/data definitions
+                */
+               return NULL;
+#endif
+#ifdef ARCH_SKIP_RELOC
+       if (ARCH_SKIP_RELOC(type_class, sym))
+               return NULL;
+#endif
+       if (_dl_strcmp(strtab + sym->st_name, undef_name) != 0)
+               return NULL;
+
+       /* This is the matching symbol */
+       return sym;
 }
 
-/*
- * We call this function when we have just read an ELF library or executable.
- * We add the relevant info to the symbol chain, so that we can resolve all
- * externals properly.
- */
 
-struct elf_resolve * _dl_add_elf_hash_table(char * libname,
-                                       char * loadaddr,
-                                       unsigned int * dynamic_info,
-                                       unsigned int dynamic_addr,
-                                       unsigned int dynamic_size){
-  unsigned int *  hash_addr;
-  struct elf_resolve * tpnt;
-  int i;
-
-  if (!_dl_loaded_modules) {
-    tpnt = _dl_loaded_modules = 
-      (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
-    _dl_memset (tpnt, 0, sizeof (*tpnt));
-  }
-  else {
-    tpnt = _dl_loaded_modules;
-    while(tpnt->next) tpnt = tpnt->next;
-    tpnt->next = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
-    _dl_memset (tpnt->next, 0, sizeof (*(tpnt->next)));
-    tpnt->next->prev = tpnt;
-    tpnt = tpnt->next;
-  };
-  
-  tpnt->next = NULL;
-  tpnt->init_flag = 0;
-  tpnt->libname = _dl_strdup(libname);
-  tpnt->dynamic_addr = dynamic_addr;
-  tpnt->dynamic_size = dynamic_size;
-  tpnt->libtype = loaded_file;
-
-  if( dynamic_info[DT_HASH] != 0 )
-  {
-    hash_addr = (unsigned int *) (dynamic_info[DT_HASH] + loadaddr);
-    tpnt->nbucket = *hash_addr++;
-    tpnt->nchain = *hash_addr++;
-    tpnt->elf_buckets = hash_addr;
-    hash_addr += tpnt->nbucket;
-    tpnt->chains = hash_addr;
-  }
-  tpnt->loadaddr = loadaddr;
-  for(i=0; i<24; i++) tpnt->dynamic_info[i] = dynamic_info[i];
-  return tpnt;
+#ifdef __LDSO_GNU_HASH_SUPPORT__
+
+static __always_inline const ElfW(Sym) *
+_dl_lookup_gnu_hash(struct elf_resolve *tpnt, ElfW(Sym) *symtab, unsigned long hash,
+                                       const char* undef_name, int type_class)
+{
+       Elf_Symndx symidx;
+       const ElfW(Sym) *sym;
+       char *strtab;
+
+       const ElfW(Addr) *bitmask = tpnt->l_gnu_bitmask;
+
+       ElfW(Addr) bitmask_word = bitmask[(hash / __ELF_NATIVE_CLASS) & tpnt->l_gnu_bitmask_idxbits];
+
+       unsigned int hashbit1 = hash & (__ELF_NATIVE_CLASS - 1);
+       unsigned int hashbit2 = ((hash >> tpnt->l_gnu_shift) & (__ELF_NATIVE_CLASS - 1));
+       _dl_assert (bitmask != NULL);
+
+       if (unlikely((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1)) {
+               unsigned long rem;
+               Elf32_Word bucket;
+
+               do_rem (rem, hash, tpnt->nbucket);
+               bucket = tpnt->l_gnu_buckets[rem];
+
+               if (bucket != 0) {
+                       const Elf32_Word *hasharr = &tpnt->l_gnu_chain_zero[bucket];
+                       do {
+                               if (((*hasharr ^ hash) >> 1) == 0) {
+                                       symidx = hasharr - tpnt->l_gnu_chain_zero;
+                                       strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]);
+                                       sym = check_match (&symtab[symidx], strtab, undef_name, type_class);
+                                       if (sym != NULL)
+                                               return sym;
+                               }
+                       } while ((*hasharr++ & 1u) == 0);
+               }
+       }
+       /* No symbol found.  */
+       return NULL;
 }
+#endif
+
+static __always_inline const ElfW(Sym) *
+_dl_lookup_sysv_hash(struct elf_resolve *tpnt, ElfW(Sym) *symtab, unsigned long hash,  const char* undef_name, int type_class)
+{
+       unsigned long hn;
+       char *strtab;
+       const ElfW(Sym) *sym;
+       Elf_Symndx symidx;
 
+       /* Avoid calling .urem here. */
+       do_rem(hn, hash, tpnt->nbucket);
+       strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]);
+
+       _dl_assert(tpnt->elf_buckets != NULL);
+
+       for (symidx = tpnt->elf_buckets[hn]; symidx != STN_UNDEF; symidx = tpnt->chains[symidx]) {
+               sym = check_match (&symtab[symidx], strtab, undef_name, type_class);
+               if (sym != NULL)
+                       /* At this point the symbol is that we are looking for */
+                       return sym;
+       }
+       /* No symbol found into the current module*/
+       return NULL;
+}
 
 /*
  * This function resolves externals, and this is either called when we process
  * relocations or when we call an entry in the PLT table for the first time.
  */
+char *_dl_find_hash(const char *name, struct r_scope_elem *scope, struct elf_resolve *mytpnt,
+       int type_class, struct symbol_ref *sym_ref)
+{
+       struct elf_resolve *tpnt = NULL;
+       ElfW(Sym) *symtab;
+       int i = 0;
+
+       unsigned long elf_hash_number = 0xffffffff;
+       const ElfW(Sym) *sym = NULL;
+
+       char *weak_result = NULL;
+       struct r_scope_elem *loop_scope;
+
+#ifdef __LDSO_GNU_HASH_SUPPORT__
+       unsigned long gnu_hash_number = _dl_gnu_hash((const unsigned char *)name);
+#endif
+
+       if ((sym_ref) && (sym_ref->sym) && (ELF32_ST_VISIBILITY(sym_ref->sym->st_other) == STV_PROTECTED)) {
+                       sym = sym_ref->sym;
+               if (mytpnt)
+                       tpnt = mytpnt;
+       } else
+       for (loop_scope = scope; loop_scope && !sym; loop_scope = loop_scope->next) {
+               for (i = 0; i < loop_scope->r_nlist; i++) {
+                       tpnt = loop_scope->r_list[i];
 
-char * _dl_find_hash(char * name, struct dyn_elf * rpnt1, 
-                    unsigned int instr_addr, struct elf_resolve * f_tpnt, 
-                    int copyrel){
-  struct elf_resolve * tpnt;
-  int si;
-  char * pnt;
-  int    pass;
-  char * strtab;
-  struct elf32_sym * symtab;
-  unsigned int elf_hash_number, hn;
-  char * weak_result;
-  struct elf_resolve * first_def;
-  struct dyn_elf * rpnt, first;
-  char * data_result = 0;         /* nakao */
-
-  weak_result = 0;
-  elf_hash_number = _dl_elf_hash(name);
-
-  /* A quick little hack to make sure that any symbol in the executable
-  will be preferred to one in a shared library.  This is necessary so
-  that any shared library data symbols referenced in the executable
-  will be seen at the same address by the executable, shared libraries
-  and dynamically loaded code. -Rob Ryan (robr@cmu.edu) */
-  if(!copyrel && rpnt1) {
-      first=(*_dl_symbol_tables);
-      first.next=rpnt1;
-      rpnt1=(&first);
-  }
-  
-  /*
-   * The passes are so that we can first search the regular symbols
-   * for whatever module was specified, and then search anything
-   * loaded with RTLD_GLOBAL.  When pass is 1, it means we are just
-   * starting the first dlopened module, and anything above that
-   * is just the next one in the chain.
-   */
-  for(pass = 0; (1==1); pass++)
-    {
-      
-      /*
-       * If we are just starting to search for RTLD_GLOBAL, setup
-       * the pointer for the start of the search.
-       */
-      if( pass == 1) {
-       rpnt1 = _dl_handles;
-      }
-
-      /*
-       * Anything after this, we need to skip to the next module.
-       */
-      else if( pass >= 2) {
-       rpnt1 = rpnt1->next_handle;
-      }
-
-      /*
-       * Make sure we still have a module, and make sure that this
-       * module was loaded with RTLD_GLOBAL.
-       */
-      if( pass != 0 )
-       {
-         if( rpnt1 == NULL ) break;
-         if( (rpnt1->flags & RTLD_GLOBAL) == 0) continue;
+                       if (!(tpnt->rtld_flags & RTLD_GLOBAL) && mytpnt) {
+                               if (mytpnt == tpnt)
+                                       ;
+                               else {
+                                       struct init_fini_list *tmp;
+
+                                       for (tmp = mytpnt->rtld_local; tmp; tmp = tmp->next) {
+                                               if (tmp->tpnt == tpnt)
+                                                       break;
+                                       }
+                                       if (!tmp)
+                                               continue;
+                               }
+                       }
+                       /* Don't search the executable when resolving a copy reloc. */
+                       if ((type_class &  ELF_RTYPE_CLASS_COPY) && tpnt->libtype == elf_executable)
+                               continue;
+
+                       /* If the hash table is empty there is nothing to do here.  */
+                       if (tpnt->nbucket == 0)
+                               continue;
+
+                       symtab = (ElfW(Sym) *) (intptr_t) (tpnt->dynamic_info[DT_SYMTAB]);
+
+#ifdef __LDSO_GNU_HASH_SUPPORT__
+                       /* Prefer GNU hash style, if any */
+                       if (tpnt->l_gnu_bitmask) {
+                               sym = _dl_lookup_gnu_hash(tpnt, symtab, gnu_hash_number, name, type_class);
+                               if (sym != NULL)
+                                       /* If sym has been found, do not search further */
+                                       break;
+                       } else {
+#endif
+                               /* Use the old SysV-style hash table */
+
+                               /* Calculate the old sysv hash number only once */
+                               if (elf_hash_number == 0xffffffff)
+                                       elf_hash_number = _dl_elf_hash((const unsigned char *)name);
+
+                               sym = _dl_lookup_sysv_hash(tpnt, symtab, elf_hash_number, name, type_class);
+                               if (sym != NULL)
+                                       /* If sym has been found, do not search further */
+                                       break;
+#ifdef __LDSO_GNU_HASH_SUPPORT__
+                       }
+#endif
+               } /* End of inner for */
        }
 
-      for(rpnt = (rpnt1 ? rpnt1 : _dl_symbol_tables); 
-         rpnt; rpnt = rpnt->next) {
-       tpnt = rpnt->dyn;
-       
-       /*
-        * The idea here is that if we are using dlsym, we want to
-        * first search the entire chain loaded from dlopen, and
-        * return a result from that if we found anything.  If this
-        * fails, then we continue the search into the stuff loaded
-        * when the image was activated.  For normal lookups, we start
-        * with rpnt == NULL, so we should never hit this.  
-        */
-       if( tpnt->libtype == elf_executable
-          && weak_result != 0 )
-         {
-           break;
-         }
-       
-       /*
-        * Avoid calling .urem here.
-        */
-       do_rem(hn, elf_hash_number, tpnt->nbucket);
-       symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] + 
-                                      tpnt->loadaddr);
-       strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
-       /*
-        * This crap is required because the first instance of a
-        * symbol on the chain will be used for all symbol references.
-        * Thus this instance must be resolved to an address that
-        * contains the actual function, 
-        */
-       
-       first_def = NULL;
-       
-       for(si = tpnt->elf_buckets[hn]; si; si = tpnt->chains[si]){
-         pnt = strtab + symtab[si].st_name;
-         
-         if(_dl_strcmp(pnt, name) == 0 && 
-            (ELF32_ST_TYPE(symtab[si].st_info) == STT_FUNC ||
-             ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE ||
-             ELF32_ST_TYPE(symtab[si].st_info) == STT_OBJECT) &&
-            symtab[si].st_value != 0) {
-           
-           /* Here we make sure that we find a module where the symbol is
-            * actually defined.
-            */
-           
-           if(f_tpnt) {
-             if(!first_def) first_def = tpnt;
-             if(first_def == f_tpnt && symtab[si].st_shndx == 0)
-               continue;
-           }
-           
-           switch(ELF32_ST_BIND(symtab[si].st_info)){
-           case STB_GLOBAL:
-             if ( tpnt->libtype != elf_executable
-               && ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE) { /* nakao */
-               data_result = tpnt->loadaddr + symtab[si].st_value; /* nakao */
-               break;                                              /* nakao */
-             } else                                                /* nakao */
-               return tpnt->loadaddr + symtab[si].st_value;
-           case STB_WEAK:
-             if (!weak_result) weak_result = tpnt->loadaddr + symtab[si].st_value;
-             break;
-           default:  /* Do local symbols need to be examined? */
-             break;
-           }
-         }
+       if (sym) {
+               if (sym_ref) {
+                       sym_ref->sym = sym;
+                       sym_ref->tpnt = tpnt;
+               }
+               /* At this point we have found the requested symbol, do binding */
+#if defined(USE_TLS) && USE_TLS
+               if (ELF_ST_TYPE(sym->st_info) == STT_TLS) {
+                       _dl_assert(sym_ref != NULL);
+                       return (char *)sym->st_value;
+               }
+#endif
+
+               switch (ELF_ST_BIND(sym->st_info)) {
+                       case STB_WEAK:
+#if 0
+       /* Perhaps we should support old style weak symbol handling
+       * per what glibc does when you export LD_DYNAMIC_WEAK */
+                               if (!weak_result)
+                                       weak_result = (char *)DL_FIND_HASH_VALUE(tpnt, type_class, sym);
+                               break;
+#endif
+                       case STB_GLOBAL:
+#ifdef __FDPIC__
+                       if (sym_ref)
+                               sym_ref->tpnt = tpnt;
+#endif
+                               return (char *)DL_FIND_HASH_VALUE(tpnt, type_class, sym);
+                       default:        /* Local symbols not handled here */
+                               break;
+               }
        }
-      }
-    }
-  if (data_result) return data_result; /* nakao */
-  return weak_result;
+#ifdef __FDPIC__
+       if (sym_ref)
+               sym_ref->tpnt = tpnt;
+#endif
+       return weak_result;
 }