Blame elf/dl-lookup.c

Packit 6c4009
/* Look up a symbol in the loaded objects.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <alloca.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <dl-hash.h>
Packit 6c4009
#include <dl-machine.h>
Packit 6c4009
#include <sysdep-cancel.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
#include <tls.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
Packit 6c4009
/* Return nonzero if check_match should consider SYM to fail to match a
Packit 6c4009
   symbol reference for some machine-specific reason.  */
Packit 6c4009
#ifndef ELF_MACHINE_SYM_NO_MATCH
Packit 6c4009
# define ELF_MACHINE_SYM_NO_MATCH(sym) 0
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#define VERSTAG(tag)	(DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (tag))
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct sym_val
Packit 6c4009
  {
Packit 6c4009
    const ElfW(Sym) *s;
Packit 6c4009
    struct link_map *m;
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Statistics function.  */
Packit 6c4009
#ifdef SHARED
Packit 6c4009
# define bump_num_relocations() ++GL(dl_num_relocations)
Packit 6c4009
#else
Packit 6c4009
# define bump_num_relocations() ((void) 0)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Utility function for do_lookup_x. The caller is called with undef_name,
Packit 6c4009
   ref, version, flags and type_class, and those are passed as the first
Packit 6c4009
   five arguments. The caller then computes sym, symidx, strtab, and map
Packit 6c4009
   and passes them as the next four arguments. Lastly the caller passes in
Packit 6c4009
   versioned_sym and num_versions which are modified by check_match during
Packit 6c4009
   the checking process.  */
Packit 6c4009
static const ElfW(Sym) *
Packit 6c4009
check_match (const char *const undef_name,
Packit 6c4009
	     const ElfW(Sym) *const ref,
Packit 6c4009
	     const struct r_found_version *const version,
Packit 6c4009
	     const int flags,
Packit 6c4009
	     const int type_class,
Packit 6c4009
	     const ElfW(Sym) *const sym,
Packit 6c4009
	     const Elf_Symndx symidx,
Packit 6c4009
	     const char *const strtab,
Packit 6c4009
	     const struct link_map *const map,
Packit 6c4009
	     const ElfW(Sym) **const versioned_sym,
Packit 6c4009
	     int *const num_versions)
Packit 6c4009
{
Packit 6c4009
  unsigned int stt = ELFW(ST_TYPE) (sym->st_info);
Packit 6c4009
  assert (ELF_RTYPE_CLASS_PLT == 1);
Packit 6c4009
  if (__glibc_unlikely ((sym->st_value == 0 /* No value.  */
Packit 6c4009
			 && sym->st_shndx != SHN_ABS
Packit 6c4009
			 && stt != STT_TLS)
Packit 6c4009
			|| ELF_MACHINE_SYM_NO_MATCH (sym)
Packit 6c4009
			|| (type_class & (sym->st_shndx == SHN_UNDEF))))
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC,
Packit 6c4009
     STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no
Packit 6c4009
     code/data definitions.  */
Packit 6c4009
#define ALLOWED_STT \
Packit 6c4009
  ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \
Packit 6c4009
   | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC))
Packit 6c4009
  if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0))
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
Packit 6c4009
    /* Not the symbol we are looking for.  */
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  const ElfW(Half) *verstab = map->l_versyms;
Packit 6c4009
  if (version != NULL)
Packit 6c4009
    {
Packit 6c4009
      if (__glibc_unlikely (verstab == NULL))
Packit 6c4009
	{
Packit 6c4009
	  /* We need a versioned symbol but haven't found any.  If
Packit 6c4009
	     this is the object which is referenced in the verneed
Packit 6c4009
	     entry it is a bug in the library since a symbol must
Packit 6c4009
	     not simply disappear.
Packit 6c4009
Packit 6c4009
	     It would also be a bug in the object since it means that
Packit 6c4009
	     the list of required versions is incomplete and so the
Packit 6c4009
	     tests in dl-version.c haven't found a problem.*/
Packit 6c4009
	  assert (version->filename == NULL
Packit 6c4009
		  || ! _dl_name_match_p (version->filename, map));
Packit 6c4009
Packit 6c4009
	  /* Otherwise we accept the symbol.  */
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  /* We can match the version information or use the
Packit 6c4009
	     default one if it is not hidden.  */
Packit 6c4009
	  ElfW(Half) ndx = verstab[symidx] & 0x7fff;
Packit 6c4009
	  if ((map->l_versions[ndx].hash != version->hash
Packit 6c4009
	       || strcmp (map->l_versions[ndx].name, version->name))
Packit 6c4009
	      && (version->hidden || map->l_versions[ndx].hash
Packit 6c4009
		  || (verstab[symidx] & 0x8000)))
Packit 6c4009
	    /* It's not the version we want.  */
Packit 6c4009
	    return NULL;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* No specific version is selected.  There are two ways we
Packit 6c4009
	 can got here:
Packit 6c4009
Packit 6c4009
	 - a binary which does not include versioning information
Packit 6c4009
	 is loaded
Packit 6c4009
Packit 6c4009
	 - dlsym() instead of dlvsym() is used to get a symbol which
Packit 6c4009
	 might exist in more than one form
Packit 6c4009
Packit 6c4009
	 If the library does not provide symbol version information
Packit 6c4009
	 there is no problem at all: we simply use the symbol if it
Packit 6c4009
	 is defined.
Packit 6c4009
Packit 6c4009
	 These two lookups need to be handled differently if the
Packit 6c4009
	 library defines versions.  In the case of the old
Packit 6c4009
	 unversioned application the oldest (default) version
Packit 6c4009
	 should be used.  In case of a dlsym() call the latest and
Packit 6c4009
	 public interface should be returned.  */
Packit 6c4009
      if (verstab != NULL)
Packit 6c4009
	{
Packit 6c4009
	  if ((verstab[symidx] & 0x7fff)
Packit 6c4009
	      >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3))
Packit 6c4009
	    {
Packit 6c4009
	      /* Don't accept hidden symbols.  */
Packit 6c4009
	      if ((verstab[symidx] & 0x8000) == 0
Packit 6c4009
		  && (*num_versions)++ == 0)
Packit 6c4009
		/* No version so far.  */
Packit 6c4009
		*versioned_sym = sym;
Packit 6c4009
Packit 6c4009
	      return NULL;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* There cannot be another entry for this symbol so stop here.  */
Packit 6c4009
  return sym;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Utility function for do_lookup_unique.  Add a symbol to TABLE.  */
Packit 6c4009
static void
Packit 6c4009
enter_unique_sym (struct unique_sym *table, size_t size,
Packit 6c4009
                  unsigned int hash, const char *name,
Packit 6c4009
                  const ElfW(Sym) *sym, const struct link_map *map)
Packit 6c4009
{
Packit 6c4009
  size_t idx = hash % size;
Packit 6c4009
  size_t hash2 = 1 + hash % (size - 2);
Packit 6c4009
  while (table[idx].name != NULL)
Packit 6c4009
    {
Packit 6c4009
      idx += hash2;
Packit 6c4009
      if (idx >= size)
Packit 6c4009
        idx -= size;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  table[idx].hashval = hash;
Packit 6c4009
  table[idx].name = name;
Packit 6c4009
  table[idx].sym = sym;
Packit 6c4009
  table[idx].map = map;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Utility function for do_lookup_x. Lookup an STB_GNU_UNIQUE symbol
Packit 6c4009
   in the unique symbol table, creating a new entry if necessary.
Packit 6c4009
   Return the matching symbol in RESULT.  */
Packit 6c4009
static void
Packit 6c4009
do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
Packit 6c4009
		  const struct link_map *map, struct sym_val *result,
Packit 6c4009
		  int type_class, const ElfW(Sym) *sym, const char *strtab,
Packit 6c4009
		  const ElfW(Sym) *ref, const struct link_map *undef_map)
Packit 6c4009
{
Packit 6c4009
  /* We have to determine whether we already found a symbol with this
Packit 6c4009
     name before.  If not then we have to add it to the search table.
Packit 6c4009
     If we already found a definition we have to use it.  */
Packit 6c4009
Packit 6c4009
  struct unique_sym_table *tab
Packit 6c4009
    = &GL(dl_ns)[map->l_ns]._ns_unique_sym_table;
Packit 6c4009
Packit 6c4009
  __rtld_lock_lock_recursive (tab->lock);
Packit 6c4009
Packit 6c4009
  struct unique_sym *entries = tab->entries;
Packit 6c4009
  size_t size = tab->size;
Packit 6c4009
  if (entries != NULL)
Packit 6c4009
    {
Packit 6c4009
      size_t idx = new_hash % size;
Packit 6c4009
      size_t hash2 = 1 + new_hash % (size - 2);
Packit 6c4009
      while (1)
Packit 6c4009
	{
Packit 6c4009
	  if (entries[idx].hashval == new_hash
Packit 6c4009
	      && strcmp (entries[idx].name, undef_name) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      if ((type_class & ELF_RTYPE_CLASS_COPY) != 0)
Packit 6c4009
		{
Packit 6c4009
		  /* We possibly have to initialize the central
Packit 6c4009
		     copy from the copy addressed through the
Packit 6c4009
		     relocation.  */
Packit 6c4009
		  result->s = sym;
Packit 6c4009
		  result->m = (struct link_map *) map;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  result->s = entries[idx].sym;
Packit 6c4009
		  result->m = (struct link_map *) entries[idx].map;
Packit 6c4009
		}
Packit 6c4009
	      __rtld_lock_unlock_recursive (tab->lock);
Packit 6c4009
	      return;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (entries[idx].name == NULL)
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
	  idx += hash2;
Packit 6c4009
	  if (idx >= size)
Packit 6c4009
	    idx -= size;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (size * 3 <= tab->n_elements * 4)
Packit 6c4009
	{
Packit 6c4009
	  /* Expand the table.  */
Packit 6c4009
#ifdef RTLD_CHECK_FOREIGN_CALL
Packit 6c4009
	  /* This must not happen during runtime relocations.  */
Packit 6c4009
	  assert (!RTLD_CHECK_FOREIGN_CALL);
Packit 6c4009
#endif
Packit 6c4009
	  size_t newsize = _dl_higher_prime_number (size + 1);
Packit 6c4009
	  struct unique_sym *newentries
Packit 6c4009
	    = calloc (sizeof (struct unique_sym), newsize);
Packit 6c4009
	  if (newentries == NULL)
Packit 6c4009
	    {
Packit 6c4009
	    nomem:
Packit 6c4009
	      __rtld_lock_unlock_recursive (tab->lock);
Packit 6c4009
	      _dl_fatal_printf ("out of memory\n");
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  for (idx = 0; idx < size; ++idx)
Packit 6c4009
	    if (entries[idx].name != NULL)
Packit 6c4009
	      enter_unique_sym (newentries, newsize, entries[idx].hashval,
Packit 6c4009
                                entries[idx].name, entries[idx].sym,
Packit 6c4009
                                entries[idx].map);
Packit 6c4009
Packit 6c4009
	  tab->free (entries);
Packit 6c4009
	  tab->size = newsize;
Packit 6c4009
	  size = newsize;
Packit 6c4009
	  entries = tab->entries = newentries;
Packit 6c4009
	  tab->free = free;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
#ifdef RTLD_CHECK_FOREIGN_CALL
Packit 6c4009
      /* This must not happen during runtime relocations.  */
Packit 6c4009
      assert (!RTLD_CHECK_FOREIGN_CALL);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
      /* If tab->entries is NULL, but tab->size is not, it means
Packit 6c4009
	 this is the second, conflict finding, lookup for
Packit 6c4009
	 LD_TRACE_PRELINKING in _dl_debug_bindings.  Don't
Packit 6c4009
	 allocate anything and don't enter anything into the
Packit 6c4009
	 hash table.  */
Packit 6c4009
      if (__glibc_unlikely (tab->size))
Packit 6c4009
	{
Packit 6c4009
	  assert (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK);
Packit 6c4009
	  goto success;
Packit 6c4009
	}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#define INITIAL_NUNIQUE_SYM_TABLE 31
Packit 6c4009
      size = INITIAL_NUNIQUE_SYM_TABLE;
Packit 6c4009
      entries = calloc (sizeof (struct unique_sym), size);
Packit 6c4009
      if (entries == NULL)
Packit 6c4009
	goto nomem;
Packit 6c4009
Packit 6c4009
      tab->entries = entries;
Packit 6c4009
      tab->size = size;
Packit 6c4009
      tab->free = free;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if ((type_class & ELF_RTYPE_CLASS_COPY) != 0)
Packit 6c4009
    enter_unique_sym (entries, size, new_hash, strtab + sym->st_name, ref,
Packit 6c4009
	   undef_map);
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      enter_unique_sym (entries, size,
Packit 6c4009
                        new_hash, strtab + sym->st_name, sym, map);
Packit 6c4009
Packit 6c4009
      if (map->l_type == lt_loaded)
Packit 6c4009
	/* Make sure we don't unload this object by
Packit 6c4009
	   setting the appropriate flag.  */
Packit 6c4009
	((struct link_map *) map)->l_flags_1 |= DF_1_NODELETE;
Packit 6c4009
    }
Packit 6c4009
  ++tab->n_elements;
Packit 6c4009
Packit 6c4009
#ifdef SHARED
Packit 6c4009
 success:
Packit 6c4009
#endif
Packit 6c4009
  __rtld_lock_unlock_recursive (tab->lock);
Packit 6c4009
Packit 6c4009
  result->s = sym;
Packit 6c4009
  result->m = (struct link_map *) map;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Inner part of the lookup functions.  We return a value > 0 if we
Packit 6c4009
   found the symbol, the value 0 if nothing is found and < 0 if
Packit 6c4009
   something bad happened.  */
Packit 6c4009
static int
Packit 6c4009
__attribute_noinline__
Packit 6c4009
do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
Packit 6c4009
	     unsigned long int *old_hash, const ElfW(Sym) *ref,
Packit 6c4009
	     struct sym_val *result, struct r_scope_elem *scope, size_t i,
Packit 6c4009
	     const struct r_found_version *const version, int flags,
Packit 6c4009
	     struct link_map *skip, int type_class, struct link_map *undef_map)
Packit 6c4009
{
Packit 6c4009
  size_t n = scope->r_nlist;
Packit 6c4009
  /* Make sure we read the value before proceeding.  Otherwise we
Packit 6c4009
     might use r_list pointing to the initial scope and r_nlist being
Packit 6c4009
     the value after a resize.  That is the only path in dl-open.c not
Packit 6c4009
     protected by GSCOPE.  A read barrier here might be to expensive.  */
Packit 6c4009
  __asm volatile ("" : "+r" (n), "+m" (scope->r_list));
Packit 6c4009
  struct link_map **list = scope->r_list;
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      const struct link_map *map = list[i]->l_real;
Packit 6c4009
Packit 6c4009
      /* Here come the extra test needed for `_dl_lookup_symbol_skip'.  */
Packit 6c4009
      if (map == skip)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* Don't search the executable when resolving a copy reloc.  */
Packit 6c4009
      if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* Do not look into objects which are going to be removed.  */
Packit 6c4009
      if (map->l_removed)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* Print some debugging info if wanted.  */
Packit 6c4009
      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS))
Packit 6c4009
	_dl_debug_printf ("symbol=%s;  lookup in file=%s [%lu]\n",
Packit 6c4009
			  undef_name, DSO_FILENAME (map->l_name),
Packit 6c4009
			  map->l_ns);
Packit 6c4009
Packit 6c4009
      /* If the hash table is empty there is nothing to do here.  */
Packit 6c4009
      if (map->l_nbuckets == 0)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      Elf_Symndx symidx;
Packit 6c4009
      int num_versions = 0;
Packit 6c4009
      const ElfW(Sym) *versioned_sym = NULL;
Packit 6c4009
Packit 6c4009
      /* The tables for this map.  */
Packit 6c4009
      const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
Packit 6c4009
      const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
Packit 6c4009
Packit 6c4009
      const ElfW(Sym) *sym;
Packit 6c4009
      const ElfW(Addr) *bitmask = map->l_gnu_bitmask;
Packit 6c4009
      if (__glibc_likely (bitmask != NULL))
Packit 6c4009
	{
Packit 6c4009
	  ElfW(Addr) bitmask_word
Packit 6c4009
	    = bitmask[(new_hash / __ELF_NATIVE_CLASS)
Packit 6c4009
		      & map->l_gnu_bitmask_idxbits];
Packit 6c4009
Packit 6c4009
	  unsigned int hashbit1 = new_hash & (__ELF_NATIVE_CLASS - 1);
Packit 6c4009
	  unsigned int hashbit2 = ((new_hash >> map->l_gnu_shift)
Packit 6c4009
				   & (__ELF_NATIVE_CLASS - 1));
Packit 6c4009
Packit 6c4009
	  if (__glibc_unlikely ((bitmask_word >> hashbit1)
Packit 6c4009
				& (bitmask_word >> hashbit2) & 1))
Packit 6c4009
	    {
Packit 6c4009
	      Elf32_Word bucket = map->l_gnu_buckets[new_hash
Packit 6c4009
						     % map->l_nbuckets];
Packit 6c4009
	      if (bucket != 0)
Packit 6c4009
		{
Packit 6c4009
		  const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket];
Packit 6c4009
Packit 6c4009
		  do
Packit 6c4009
		    if (((*hasharr ^ new_hash) >> 1) == 0)
Packit 6c4009
		      {
Packit 6c4009
			symidx = hasharr - map->l_gnu_chain_zero;
Packit 6c4009
			sym = check_match (undef_name, ref, version, flags,
Packit 6c4009
					   type_class, &symtab[symidx], symidx,
Packit 6c4009
					   strtab, map, &versioned_sym,
Packit 6c4009
					   &num_versions);
Packit 6c4009
			if (sym != NULL)
Packit 6c4009
			  goto found_it;
Packit 6c4009
		      }
Packit 6c4009
		  while ((*hasharr++ & 1u) == 0);
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  /* No symbol found.  */
Packit 6c4009
	  symidx = SHN_UNDEF;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  if (*old_hash == 0xffffffff)
Packit 6c4009
	    *old_hash = _dl_elf_hash (undef_name);
Packit 6c4009
Packit 6c4009
	  /* Use the old SysV-style hash table.  Search the appropriate
Packit 6c4009
	     hash bucket in this object's symbol table for a definition
Packit 6c4009
	     for the same symbol name.  */
Packit 6c4009
	  for (symidx = map->l_buckets[*old_hash % map->l_nbuckets];
Packit 6c4009
	       symidx != STN_UNDEF;
Packit 6c4009
	       symidx = map->l_chain[symidx])
Packit 6c4009
	    {
Packit 6c4009
	      sym = check_match (undef_name, ref, version, flags,
Packit 6c4009
				 type_class, &symtab[symidx], symidx,
Packit 6c4009
				 strtab, map, &versioned_sym,
Packit 6c4009
				 &num_versions);
Packit 6c4009
	      if (sym != NULL)
Packit 6c4009
		goto found_it;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* If we have seen exactly one versioned symbol while we are
Packit 6c4009
	 looking for an unversioned symbol and the version is not the
Packit 6c4009
	 default version we still accept this symbol since there are
Packit 6c4009
	 no possible ambiguities.  */
Packit 6c4009
      sym = num_versions == 1 ? versioned_sym : NULL;
Packit 6c4009
Packit 6c4009
      if (sym != NULL)
Packit 6c4009
	{
Packit 6c4009
	found_it:
Packit 6c4009
	  /* When UNDEF_MAP is NULL, which indicates we are called from
Packit 6c4009
	     do_lookup_x on relocation against protected data, we skip
Packit 6c4009
	     the data definion in the executable from copy reloc.  */
Packit 6c4009
	  if (ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA
Packit 6c4009
	      && undef_map == NULL
Packit 6c4009
	      && map->l_type == lt_executable
Packit 6c4009
	      && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA)
Packit 6c4009
	    {
Packit 6c4009
	      const ElfW(Sym) *s;
Packit 6c4009
	      unsigned int i;
Packit 6c4009
Packit 6c4009
#if ! ELF_MACHINE_NO_RELA
Packit 6c4009
	      if (map->l_info[DT_RELA] != NULL
Packit 6c4009
		  && map->l_info[DT_RELASZ] != NULL
Packit 6c4009
		  && map->l_info[DT_RELASZ]->d_un.d_val != 0)
Packit 6c4009
		{
Packit 6c4009
		  const ElfW(Rela) *rela
Packit 6c4009
		    = (const ElfW(Rela) *) D_PTR (map, l_info[DT_RELA]);
Packit 6c4009
		  unsigned int rela_count
Packit 6c4009
		    = map->l_info[DT_RELASZ]->d_un.d_val / sizeof (*rela);
Packit 6c4009
Packit 6c4009
		  for (i = 0; i < rela_count; i++, rela++)
Packit 6c4009
		    if (elf_machine_type_class (ELFW(R_TYPE) (rela->r_info))
Packit 6c4009
			== ELF_RTYPE_CLASS_COPY)
Packit 6c4009
		      {
Packit 6c4009
			s = &symtab[ELFW(R_SYM) (rela->r_info)];
Packit 6c4009
			if (!strcmp (strtab + s->st_name, undef_name))
Packit 6c4009
			  goto skip;
Packit 6c4009
		      }
Packit 6c4009
		}
Packit 6c4009
#endif
Packit 6c4009
#if ! ELF_MACHINE_NO_REL
Packit 6c4009
	      if (map->l_info[DT_REL] != NULL
Packit 6c4009
		  && map->l_info[DT_RELSZ] != NULL
Packit 6c4009
		  && map->l_info[DT_RELSZ]->d_un.d_val != 0)
Packit 6c4009
		{
Packit 6c4009
		  const ElfW(Rel) *rel
Packit 6c4009
		    = (const ElfW(Rel) *) D_PTR (map, l_info[DT_REL]);
Packit 6c4009
		  unsigned int rel_count
Packit 6c4009
		    = map->l_info[DT_RELSZ]->d_un.d_val / sizeof (*rel);
Packit 6c4009
Packit 6c4009
		  for (i = 0; i < rel_count; i++, rel++)
Packit 6c4009
		    if (elf_machine_type_class (ELFW(R_TYPE) (rel->r_info))
Packit 6c4009
			== ELF_RTYPE_CLASS_COPY)
Packit 6c4009
		      {
Packit 6c4009
			s = &symtab[ELFW(R_SYM) (rel->r_info)];
Packit 6c4009
			if (!strcmp (strtab + s->st_name, undef_name))
Packit 6c4009
			  goto skip;
Packit 6c4009
		      }
Packit 6c4009
		}
Packit 6c4009
#endif
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Hidden and internal symbols are local, ignore them.  */
Packit 6c4009
	  if (__glibc_unlikely (dl_symbol_visibility_binds_local_p (sym)))
Packit 6c4009
	    goto skip;
Packit 6c4009
Packit 6c4009
	  switch (ELFW(ST_BIND) (sym->st_info))
Packit 6c4009
	    {
Packit 6c4009
	    case STB_WEAK:
Packit 6c4009
	      /* Weak definition.  Use this value if we don't find another.  */
Packit 6c4009
	      if (__glibc_unlikely (GLRO(dl_dynamic_weak)))
Packit 6c4009
		{
Packit 6c4009
		  if (! result->s)
Packit 6c4009
		    {
Packit 6c4009
		      result->s = sym;
Packit 6c4009
		      result->m = (struct link_map *) map;
Packit 6c4009
		    }
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
	      /* FALLTHROUGH */
Packit 6c4009
	    case STB_GLOBAL:
Packit 6c4009
	      /* Global definition.  Just what we need.  */
Packit 6c4009
	      result->s = sym;
Packit 6c4009
	      result->m = (struct link_map *) map;
Packit 6c4009
	      return 1;
Packit 6c4009
Packit 6c4009
	    case STB_GNU_UNIQUE:;
Packit 6c4009
	      do_lookup_unique (undef_name, new_hash, map, result, type_class,
Packit 6c4009
				sym, strtab, ref, undef_map);
Packit 6c4009
	      return 1;
Packit 6c4009
Packit 6c4009
	    default:
Packit 6c4009
	      /* Local symbols are ignored.  */
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
skip:
Packit 6e6197
      ;
Packit 6c4009
    }
Packit 6c4009
  while (++i < n);
Packit 6c4009
Packit 6c4009
  /* We have not found anything until now.  */
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static uint_fast32_t
Packit 6c4009
dl_new_hash (const char *s)
Packit 6c4009
{
Packit 6c4009
  uint_fast32_t h = 5381;
Packit 6c4009
  for (unsigned char c = *s; c != '\0'; c = *++s)
Packit 6c4009
    h = h * 33 + c;
Packit 6c4009
  return h & 0xffffffff;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Add extra dependency on MAP to UNDEF_MAP.  */
Packit 6c4009
static int
Packit 6c4009
add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
Packit 6c4009
{
Packit 6c4009
  struct link_map *runp;
Packit 6c4009
  unsigned int i;
Packit 6c4009
  int result = 0;
Packit 6c4009
Packit 6c4009
  /* Avoid self-references and references to objects which cannot be
Packit 6c4009
     unloaded anyway.  */
Packit 6c4009
  if (undef_map == map)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  /* Avoid references to objects which cannot be unloaded anyway.  */
Packit 6c4009
  assert (map->l_type == lt_loaded);
Packit 6c4009
  if ((map->l_flags_1 & DF_1_NODELETE) != 0)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  struct link_map_reldeps *l_reldeps
Packit 6c4009
    = atomic_forced_read (undef_map->l_reldeps);
Packit 6c4009
Packit 6c4009
  /* Make sure l_reldeps is read before l_initfini.  */
Packit 6c4009
  atomic_read_barrier ();
Packit 6c4009
Packit 6c4009
  /* Determine whether UNDEF_MAP already has a reference to MAP.  First
Packit 6c4009
     look in the normal dependencies.  */
Packit 6c4009
  struct link_map **l_initfini = atomic_forced_read (undef_map->l_initfini);
Packit 6c4009
  if (l_initfini != NULL)
Packit 6c4009
    {
Packit 6c4009
      for (i = 0; l_initfini[i] != NULL; ++i)
Packit 6c4009
	if (l_initfini[i] == map)
Packit 6c4009
	  return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* No normal dependency.  See whether we already had to add it
Packit 6c4009
     to the special list of dynamic dependencies.  */
Packit 6c4009
  unsigned int l_reldepsact = 0;
Packit 6c4009
  if (l_reldeps != NULL)
Packit 6c4009
    {
Packit 6c4009
      struct link_map **list = &l_reldeps->list[0];
Packit 6c4009
      l_reldepsact = l_reldeps->act;
Packit 6c4009
      for (i = 0; i < l_reldepsact; ++i)
Packit 6c4009
	if (list[i] == map)
Packit 6c4009
	  return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Save serial number of the target MAP.  */
Packit 6c4009
  unsigned long long serial = map->l_serial;
Packit 6c4009
Packit 6c4009
  /* Make sure nobody can unload the object while we are at it.  */
Packit 6c4009
  if (__glibc_unlikely (flags & DL_LOOKUP_GSCOPE_LOCK))
Packit 6c4009
    {
Packit 6c4009
      /* We can't just call __rtld_lock_lock_recursive (GL(dl_load_lock))
Packit 6c4009
	 here, that can result in ABBA deadlock.  */
Packit 6c4009
      THREAD_GSCOPE_RESET_FLAG ();
Packit 6c4009
      __rtld_lock_lock_recursive (GL(dl_load_lock));
Packit 6c4009
      /* While MAP value won't change, after THREAD_GSCOPE_RESET_FLAG ()
Packit 6c4009
	 it can e.g. point to unallocated memory.  So avoid the optimizer
Packit 6c4009
	 treating the above read from MAP->l_serial as ensurance it
Packit 6c4009
	 can safely dereference it.  */
Packit 6c4009
      map = atomic_forced_read (map);
Packit 6c4009
Packit 6c4009
      /* From this point on it is unsafe to dereference MAP, until it
Packit 6c4009
	 has been found in one of the lists.  */
Packit 6c4009
Packit 6c4009
      /* Redo the l_initfini check in case undef_map's l_initfini
Packit 6c4009
	 changed in the mean time.  */
Packit 6c4009
      if (undef_map->l_initfini != l_initfini
Packit 6c4009
	  && undef_map->l_initfini != NULL)
Packit 6c4009
	{
Packit 6c4009
	  l_initfini = undef_map->l_initfini;
Packit 6c4009
	  for (i = 0; l_initfini[i] != NULL; ++i)
Packit 6c4009
	    if (l_initfini[i] == map)
Packit 6c4009
	      goto out_check;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Redo the l_reldeps check if undef_map's l_reldeps changed in
Packit 6c4009
	 the mean time.  */
Packit 6c4009
      if (undef_map->l_reldeps != NULL)
Packit 6c4009
	{
Packit 6c4009
	  if (undef_map->l_reldeps != l_reldeps)
Packit 6c4009
	    {
Packit 6c4009
	      struct link_map **list = &undef_map->l_reldeps->list[0];
Packit 6c4009
	      l_reldepsact = undef_map->l_reldeps->act;
Packit 6c4009
	      for (i = 0; i < l_reldepsact; ++i)
Packit 6c4009
		if (list[i] == map)
Packit 6c4009
		  goto out_check;
Packit 6c4009
	    }
Packit 6c4009
	  else if (undef_map->l_reldeps->act > l_reldepsact)
Packit 6c4009
	    {
Packit 6c4009
	      struct link_map **list
Packit 6c4009
		= &undef_map->l_reldeps->list[0];
Packit 6c4009
	      i = l_reldepsact;
Packit 6c4009
	      l_reldepsact = undef_map->l_reldeps->act;
Packit 6c4009
	      for (; i < l_reldepsact; ++i)
Packit 6c4009
		if (list[i] == map)
Packit 6c4009
		  goto out_check;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    __rtld_lock_lock_recursive (GL(dl_load_lock));
Packit 6c4009
Packit 6c4009
  /* The object is not yet in the dependency list.  Before we add
Packit 6c4009
     it make sure just one more time the object we are about to
Packit 6c4009
     reference is still available.  There is a brief period in
Packit 6c4009
     which the object could have been removed since we found the
Packit 6c4009
     definition.  */
Packit 6c4009
  runp = GL(dl_ns)[undef_map->l_ns]._ns_loaded;
Packit 6c4009
  while (runp != NULL && runp != map)
Packit 6c4009
    runp = runp->l_next;
Packit 6c4009
Packit 6c4009
  if (runp != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* The object is still available.  */
Packit 6c4009
Packit 6c4009
      /* MAP could have been dlclosed, freed and then some other dlopened
Packit 6c4009
	 library could have the same link_map pointer.  */
Packit 6c4009
      if (map->l_serial != serial)
Packit 6c4009
	goto out_check;
Packit 6c4009
Packit 6c4009
      /* Redo the NODELETE check, as when dl_load_lock wasn't held
Packit 6c4009
	 yet this could have changed.  */
Packit 6c4009
      if ((map->l_flags_1 & DF_1_NODELETE) != 0)
Packit 6c4009
	goto out;
Packit 6c4009
Packit 6c4009
      /* If the object with the undefined reference cannot be removed ever
Packit 6c4009
	 just make sure the same is true for the object which contains the
Packit 6c4009
	 definition.  */
Packit 6c4009
      if (undef_map->l_type != lt_loaded
Packit 6c4009
	  || (undef_map->l_flags_1 & DF_1_NODELETE) != 0)
Packit 6c4009
	{
Packit 6c4009
	  map->l_flags_1 |= DF_1_NODELETE;
Packit 6c4009
	  goto out;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Add the reference now.  */
Packit 6c4009
      if (__glibc_unlikely (l_reldepsact >= undef_map->l_reldepsmax))
Packit 6c4009
	{
Packit 6c4009
	  /* Allocate more memory for the dependency list.  Since this
Packit 6c4009
	     can never happen during the startup phase we can use
Packit 6c4009
	     `realloc'.  */
Packit 6c4009
	  struct link_map_reldeps *newp;
Packit 6c4009
	  unsigned int max
Packit 6c4009
	    = undef_map->l_reldepsmax ? undef_map->l_reldepsmax * 2 : 10;
Packit 6c4009
Packit 6c4009
#ifdef RTLD_PREPARE_FOREIGN_CALL
Packit 6c4009
	  RTLD_PREPARE_FOREIGN_CALL;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	  newp = malloc (sizeof (*newp) + max * sizeof (struct link_map *));
Packit 6c4009
	  if (newp == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* If we didn't manage to allocate memory for the list this is
Packit 6c4009
		 no fatal problem.  We simply make sure the referenced object
Packit 6c4009
		 cannot be unloaded.  This is semantically the correct
Packit 6c4009
		 behavior.  */
Packit 6c4009
	      map->l_flags_1 |= DF_1_NODELETE;
Packit 6c4009
	      goto out;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      if (l_reldepsact)
Packit 6c4009
		memcpy (&newp->list[0], &undef_map->l_reldeps->list[0],
Packit 6c4009
			l_reldepsact * sizeof (struct link_map *));
Packit 6c4009
	      newp->list[l_reldepsact] = map;
Packit 6c4009
	      newp->act = l_reldepsact + 1;
Packit 6c4009
	      atomic_write_barrier ();
Packit 6c4009
	      void *old = undef_map->l_reldeps;
Packit 6c4009
	      undef_map->l_reldeps = newp;
Packit 6c4009
	      undef_map->l_reldepsmax = max;
Packit 6c4009
	      if (old)
Packit 6c4009
		_dl_scope_free (old);
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  undef_map->l_reldeps->list[l_reldepsact] = map;
Packit 6c4009
	  atomic_write_barrier ();
Packit 6c4009
	  undef_map->l_reldeps->act = l_reldepsact + 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Display information if we are debugging.  */
Packit 6c4009
      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
Packit 6c4009
	_dl_debug_printf ("\
Packit 6c4009
\nfile=%s [%lu];  needed by %s [%lu] (relocation dependency)\n\n",
Packit 6c4009
			  DSO_FILENAME (map->l_name),
Packit 6c4009
			  map->l_ns,
Packit 6c4009
			  DSO_FILENAME (undef_map->l_name),
Packit 6c4009
			  undef_map->l_ns);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    /* Whoa, that was bad luck.  We have to search again.  */
Packit 6c4009
    result = -1;
Packit 6c4009
Packit 6c4009
 out:
Packit 6c4009
  /* Release the lock.  */
Packit 6c4009
  __rtld_lock_unlock_recursive (GL(dl_load_lock));
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (flags & DL_LOOKUP_GSCOPE_LOCK))
Packit 6c4009
    THREAD_GSCOPE_SET_FLAG ();
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
Packit 6c4009
 out_check:
Packit 6c4009
  if (map->l_serial != serial)
Packit 6c4009
    result = -1;
Packit 6c4009
  goto out;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
_dl_debug_bindings (const char *undef_name, struct link_map *undef_map,
Packit 6c4009
		    const ElfW(Sym) **ref, struct sym_val *value,
Packit 6c4009
		    const struct r_found_version *version, int type_class,
Packit 6c4009
		    int protected);
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Search loaded objects' symbol tables for a definition of the symbol
Packit 6c4009
   UNDEF_NAME, perhaps with a requested version for the symbol.
Packit 6c4009
Packit 6c4009
   We must never have calls to the audit functions inside this function
Packit 6c4009
   or in any function which gets called.  If this would happen the audit
Packit 6c4009
   code might create a thread which can throw off all the scope locking.  */
Packit 6c4009
lookup_t
Packit 6c4009
_dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
Packit 6c4009
		     const ElfW(Sym) **ref,
Packit 6c4009
		     struct r_scope_elem *symbol_scope[],
Packit 6c4009
		     const struct r_found_version *version,
Packit 6c4009
		     int type_class, int flags, struct link_map *skip_map)
Packit 6c4009
{
Packit 6c4009
  const uint_fast32_t new_hash = dl_new_hash (undef_name);
Packit 6c4009
  unsigned long int old_hash = 0xffffffff;
Packit 6c4009
  struct sym_val current_value = { NULL, NULL };
Packit 6c4009
  struct r_scope_elem **scope = symbol_scope;
Packit 6c4009
Packit 6c4009
  bump_num_relocations ();
Packit 6c4009
Packit 6c4009
  /* No other flag than DL_LOOKUP_ADD_DEPENDENCY or DL_LOOKUP_GSCOPE_LOCK
Packit 6c4009
     is allowed if we look up a versioned symbol.  */
Packit 6c4009
  assert (version == NULL
Packit 6c4009
	  || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK))
Packit 6c4009
	     == 0);
Packit 6c4009
Packit 6c4009
  size_t i = 0;
Packit 6c4009
  if (__glibc_unlikely (skip_map != NULL))
Packit 6c4009
    /* Search the relevant loaded objects for a definition.  */
Packit 6c4009
    while ((*scope)->r_list[i] != skip_map)
Packit 6c4009
      ++i;
Packit 6c4009
Packit 6c4009
  /* Search the relevant loaded objects for a definition.  */
Packit 6c4009
  for (size_t start = i; *scope != NULL; start = 0, ++scope)
Packit 6e6197
    if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
Packit 6e6197
		     &current_value, *scope, start, version, flags,
Packit 6e6197
		     skip_map, type_class, undef_map) != 0)
Packit 6e6197
      break;
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (current_value.s == NULL))
Packit 6c4009
    {
Packit 6c4009
      if ((*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
Packit 6c4009
	  && !(GLRO(dl_debug_mask) & DL_DEBUG_UNUSED))
Packit 6c4009
	{
Packit 6c4009
	  /* We could find no value for a strong reference.  */
Packit 6c4009
	  const char *reference_name = undef_map ? undef_map->l_name : "";
Packit 6c4009
	  const char *versionstr = version ? ", version " : "";
Packit 6c4009
	  const char *versionname = (version && version->name
Packit 6c4009
				     ? version->name : "");
Packit 6c4009
	  struct dl_exception exception;
Packit 6c4009
	  /* XXX We cannot translate the message.  */
Packit 6c4009
	  _dl_exception_create_format
Packit 6c4009
	    (&exception, DSO_FILENAME (reference_name),
Packit 6c4009
	     "undefined symbol: %s%s%s",
Packit 6c4009
	     undef_name, versionstr, versionname);
Packit 6c4009
	  _dl_signal_cexception (0, &exception, N_("symbol lookup error"));
Packit 6c4009
	  _dl_exception_free (&exception);
Packit 6c4009
	}
Packit 6c4009
      *ref = NULL;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  int protected = (*ref
Packit 6c4009
		   && ELFW(ST_VISIBILITY) ((*ref)->st_other) == STV_PROTECTED);
Packit 6c4009
  if (__glibc_unlikely (protected != 0))
Packit 6c4009
    {
Packit 6c4009
      /* It is very tricky.  We need to figure out what value to
Packit 6c4009
	 return for the protected symbol.  */
Packit 6c4009
      if (type_class == ELF_RTYPE_CLASS_PLT)
Packit 6c4009
	{
Packit 6c4009
	  if (current_value.s != NULL && current_value.m != undef_map)
Packit 6c4009
	    {
Packit 6c4009
	      current_value.s = *ref;
Packit 6c4009
	      current_value.m = undef_map;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  struct sym_val protected_value = { NULL, NULL };
Packit 6c4009
Packit 6c4009
	  for (scope = symbol_scope; *scope != NULL; i = 0, ++scope)
Packit 6c4009
	    if (do_lookup_x (undef_name, new_hash, &old_hash, *ref,
Packit 6c4009
			     &protected_value, *scope, i, version, flags,
Packit 6c4009
			     skip_map,
Packit 6c4009
			     (ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA
Packit 6c4009
			      && ELFW(ST_TYPE) ((*ref)->st_info) == STT_OBJECT
Packit 6c4009
			      && type_class == ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA)
Packit 6c4009
			     ? ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA
Packit 6c4009
			     : ELF_RTYPE_CLASS_PLT, NULL) != 0)
Packit 6c4009
	      break;
Packit 6c4009
Packit 6c4009
	  if (protected_value.s != NULL && protected_value.m != undef_map)
Packit 6c4009
	    {
Packit 6c4009
	      current_value.s = *ref;
Packit 6c4009
	      current_value.m = undef_map;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We have to check whether this would bind UNDEF_MAP to an object
Packit 6c4009
     in the global scope which was dynamically loaded.  In this case
Packit 6c4009
     we have to prevent the latter from being unloaded unless the
Packit 6c4009
     UNDEF_MAP object is also unloaded.  */
Packit 6c4009
  if (__glibc_unlikely (current_value.m->l_type == lt_loaded)
Packit 6c4009
      /* Don't do this for explicit lookups as opposed to implicit
Packit 6c4009
	 runtime lookups.  */
Packit 6c4009
      && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0
Packit 6c4009
      /* Add UNDEF_MAP to the dependencies.  */
Packit 6c4009
      && add_dependency (undef_map, current_value.m, flags) < 0)
Packit 6c4009
      /* Something went wrong.  Perhaps the object we tried to reference
Packit 6c4009
	 was just removed.  Try finding another definition.  */
Packit 6c4009
      return _dl_lookup_symbol_x (undef_name, undef_map, ref,
Packit 6c4009
				  (flags & DL_LOOKUP_GSCOPE_LOCK)
Packit 6c4009
				  ? undef_map->l_scope : symbol_scope,
Packit 6c4009
				  version, type_class, flags, skip_map);
Packit 6c4009
Packit 6c4009
  /* The object is used.  */
Packit 6c4009
  if (__glibc_unlikely (current_value.m->l_used == 0))
Packit 6c4009
    current_value.m->l_used = 1;
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (GLRO(dl_debug_mask)
Packit 6c4009
			& (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK)))
Packit 6c4009
    _dl_debug_bindings (undef_name, undef_map, ref,
Packit 6c4009
			&current_value, version, type_class, protected);
Packit 6c4009
Packit 6c4009
  *ref = current_value.s;
Packit 6c4009
  return LOOKUP_VALUE (current_value.m);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Cache the location of MAP's hash table.  */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_dl_setup_hash (struct link_map *map)
Packit 6c4009
{
Packit 6c4009
  Elf_Symndx *hash;
Packit 6c4009
Packit 6c4009
  if (__glibc_likely (map->l_info[ADDRIDX (DT_GNU_HASH)] != NULL))
Packit 6c4009
    {
Packit 6c4009
      Elf32_Word *hash32
Packit 6c4009
	= (void *) D_PTR (map, l_info[ADDRIDX (DT_GNU_HASH)]);
Packit 6c4009
      map->l_nbuckets = *hash32++;
Packit 6c4009
      Elf32_Word symbias = *hash32++;
Packit 6c4009
      Elf32_Word bitmask_nwords = *hash32++;
Packit 6c4009
      /* Must be a power of two.  */
Packit 6c4009
      assert ((bitmask_nwords & (bitmask_nwords - 1)) == 0);
Packit 6c4009
      map->l_gnu_bitmask_idxbits = bitmask_nwords - 1;
Packit 6c4009
      map->l_gnu_shift = *hash32++;
Packit 6c4009
Packit 6c4009
      map->l_gnu_bitmask = (ElfW(Addr) *) hash32;
Packit 6c4009
      hash32 += __ELF_NATIVE_CLASS / 32 * bitmask_nwords;
Packit 6c4009
Packit 6c4009
      map->l_gnu_buckets = hash32;
Packit 6c4009
      hash32 += map->l_nbuckets;
Packit 6c4009
      map->l_gnu_chain_zero = hash32 - symbias;
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (!map->l_info[DT_HASH])
Packit 6c4009
    return;
Packit 6c4009
  hash = (void *) D_PTR (map, l_info[DT_HASH]);
Packit 6c4009
Packit 6c4009
  map->l_nbuckets = *hash++;
Packit 6c4009
  /* Skip nchain.  */
Packit 6c4009
  hash++;
Packit 6c4009
  map->l_buckets = hash;
Packit 6c4009
  hash += map->l_nbuckets;
Packit 6c4009
  map->l_chain = hash;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
_dl_debug_bindings (const char *undef_name, struct link_map *undef_map,
Packit 6c4009
		    const ElfW(Sym) **ref, struct sym_val *value,
Packit 6c4009
		    const struct r_found_version *version, int type_class,
Packit 6c4009
		    int protected)
Packit 6c4009
{
Packit 6c4009
  const char *reference_name = undef_map->l_name;
Packit 6c4009
Packit 6c4009
  if (GLRO(dl_debug_mask) & DL_DEBUG_BINDINGS)
Packit 6c4009
    {
Packit 6c4009
      _dl_debug_printf ("binding file %s [%lu] to %s [%lu]: %s symbol `%s'",
Packit 6c4009
			DSO_FILENAME (reference_name),
Packit 6c4009
			undef_map->l_ns,
Packit 6c4009
			DSO_FILENAME (value->m->l_name),
Packit 6c4009
			value->m->l_ns,
Packit 6c4009
			protected ? "protected" : "normal", undef_name);
Packit 6c4009
      if (version)
Packit 6c4009
	_dl_debug_printf_c (" [%s]\n", version->name);
Packit 6c4009
      else
Packit 6c4009
	_dl_debug_printf_c ("\n");
Packit 6c4009
    }
Packit 6c4009
#ifdef SHARED
Packit 6c4009
  if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
Packit 6c4009
    {
Packit 6c4009
/* ELF_RTYPE_CLASS_XXX must match RTYPE_CLASS_XXX used by prelink with
Packit 6c4009
   LD_TRACE_PRELINKING.  */
Packit 6c4009
#define RTYPE_CLASS_VALID	8
Packit 6c4009
#define RTYPE_CLASS_PLT		(8|1)
Packit 6c4009
#define RTYPE_CLASS_COPY	(8|2)
Packit 6c4009
#define RTYPE_CLASS_TLS		(8|4)
Packit 6c4009
#if ELF_RTYPE_CLASS_PLT != 0 && ELF_RTYPE_CLASS_PLT != 1
Packit 6c4009
# error ELF_RTYPE_CLASS_PLT must be 0 or 1!
Packit 6c4009
#endif
Packit 6c4009
#if ELF_RTYPE_CLASS_COPY != 0 && ELF_RTYPE_CLASS_COPY != 2
Packit 6c4009
# error ELF_RTYPE_CLASS_COPY must be 0 or 2!
Packit 6c4009
#endif
Packit 6c4009
      int conflict = 0;
Packit 6c4009
      struct sym_val val = { NULL, NULL };
Packit 6c4009
Packit 6c4009
      if ((GLRO(dl_trace_prelink_map) == NULL
Packit 6c4009
	   || GLRO(dl_trace_prelink_map) == GL(dl_ns)[LM_ID_BASE]._ns_loaded)
Packit 6c4009
	  && undef_map != GL(dl_ns)[LM_ID_BASE]._ns_loaded)
Packit 6c4009
	{
Packit 6c4009
	  const uint_fast32_t new_hash = dl_new_hash (undef_name);
Packit 6c4009
	  unsigned long int old_hash = 0xffffffff;
Packit 6c4009
	  struct unique_sym *saved_entries
Packit 6c4009
	    = GL(dl_ns)[LM_ID_BASE]._ns_unique_sym_table.entries;
Packit 6c4009
Packit 6c4009
	  GL(dl_ns)[LM_ID_BASE]._ns_unique_sym_table.entries = NULL;
Packit 6c4009
	  do_lookup_x (undef_name, new_hash, &old_hash, *ref, &val,
Packit 6c4009
		       undef_map->l_local_scope[0], 0, version, 0, NULL,
Packit 6c4009
		       type_class, undef_map);
Packit 6c4009
	  if (val.s != value->s || val.m != value->m)
Packit 6c4009
	    conflict = 1;
Packit 6c4009
	  else if (__glibc_unlikely (undef_map->l_symbolic_in_local_scope)
Packit 6c4009
		   && val.s
Packit 6c4009
		   && __glibc_unlikely (ELFW(ST_BIND) (val.s->st_info)
Packit 6c4009
					== STB_GNU_UNIQUE))
Packit 6c4009
	    {
Packit 6c4009
	      /* If it is STB_GNU_UNIQUE and undef_map's l_local_scope
Packit 6c4009
		 contains any DT_SYMBOLIC libraries, unfortunately there
Packit 6c4009
		 can be conflicts even if the above is equal.  As symbol
Packit 6c4009
		 resolution goes from the last library to the first and
Packit 6c4009
		 if a STB_GNU_UNIQUE symbol is found in some late DT_SYMBOLIC
Packit 6c4009
		 library, it would be the one that is looked up.  */
Packit 6c4009
	      struct sym_val val2 = { NULL, NULL };
Packit 6c4009
	      size_t n;
Packit 6c4009
	      struct r_scope_elem *scope = undef_map->l_local_scope[0];
Packit 6c4009
Packit 6c4009
	      for (n = 0; n < scope->r_nlist; n++)
Packit 6c4009
		if (scope->r_list[n] == val.m)
Packit 6c4009
		  break;
Packit 6c4009
Packit 6c4009
	      for (n++; n < scope->r_nlist; n++)
Packit 6c4009
		if (scope->r_list[n]->l_info[DT_SYMBOLIC] != NULL
Packit 6c4009
		    && do_lookup_x (undef_name, new_hash, &old_hash, *ref,
Packit 6c4009
				    &val2,
Packit 6c4009
				    &scope->r_list[n]->l_symbolic_searchlist,
Packit 6c4009
				    0, version, 0, NULL, type_class,
Packit 6c4009
				    undef_map) > 0)
Packit 6c4009
		  {
Packit 6c4009
		    conflict = 1;
Packit 6c4009
		    val = val2;
Packit 6c4009
		    break;
Packit 6c4009
		  }
Packit 6c4009
	    }
Packit 6c4009
	  GL(dl_ns)[LM_ID_BASE]._ns_unique_sym_table.entries = saved_entries;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (value->s)
Packit 6c4009
	{
Packit 6c4009
	  /* Keep only ELF_RTYPE_CLASS_PLT and ELF_RTYPE_CLASS_COPY
Packit 6c4009
	     bits since since prelink only uses them.  */
Packit 6c4009
	  type_class &= ELF_RTYPE_CLASS_PLT | ELF_RTYPE_CLASS_COPY;
Packit 6c4009
	  if (__glibc_unlikely (ELFW(ST_TYPE) (value->s->st_info)
Packit 6c4009
				== STT_TLS))
Packit 6c4009
	    /* Clear the RTYPE_CLASS_VALID bit in RTYPE_CLASS_TLS.  */
Packit 6c4009
	    type_class = RTYPE_CLASS_TLS & ~RTYPE_CLASS_VALID;
Packit 6c4009
	  else if (__glibc_unlikely (ELFW(ST_TYPE) (value->s->st_info)
Packit 6c4009
				     == STT_GNU_IFUNC))
Packit 6c4009
	    /* Set the RTYPE_CLASS_VALID bit.  */
Packit 6c4009
	    type_class |= RTYPE_CLASS_VALID;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (conflict
Packit 6c4009
	  || GLRO(dl_trace_prelink_map) == undef_map
Packit 6c4009
	  || GLRO(dl_trace_prelink_map) == NULL
Packit 6c4009
	  || type_class >= 4)
Packit 6c4009
	{
Packit 6c4009
	  _dl_printf ("%s 0x%0*Zx 0x%0*Zx -> 0x%0*Zx 0x%0*Zx ",
Packit 6c4009
		      conflict ? "conflict" : "lookup",
Packit 6c4009
		      (int) sizeof (ElfW(Addr)) * 2,
Packit 6c4009
		      (size_t) undef_map->l_map_start,
Packit 6c4009
		      (int) sizeof (ElfW(Addr)) * 2,
Packit 6c4009
		      (size_t) (((ElfW(Addr)) *ref) - undef_map->l_map_start),
Packit 6c4009
		      (int) sizeof (ElfW(Addr)) * 2,
Packit 6c4009
		      (size_t) (value->s ? value->m->l_map_start : 0),
Packit 6c4009
		      (int) sizeof (ElfW(Addr)) * 2,
Packit 6c4009
		      (size_t) (value->s ? value->s->st_value : 0));
Packit 6c4009
Packit 6c4009
	  if (conflict)
Packit 6c4009
	    _dl_printf ("x 0x%0*Zx 0x%0*Zx ",
Packit 6c4009
			(int) sizeof (ElfW(Addr)) * 2,
Packit 6c4009
			(size_t) (val.s ? val.m->l_map_start : 0),
Packit 6c4009
			(int) sizeof (ElfW(Addr)) * 2,
Packit 6c4009
			(size_t) (val.s ? val.s->st_value : 0));
Packit 6c4009
Packit 6c4009
	  _dl_printf ("/%x %s\n", type_class, undef_name);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
}