Blame elf/dl-lookup-direct.c

Packit Service da41ad
/* Look up a symbol in a single specified object.
Packit Service da41ad
   Copyright (C) 1995-2020 Free Software Foundation, Inc.
Packit Service da41ad
   This file is part of the GNU C Library.
Packit Service da41ad
Packit Service da41ad
   The GNU C Library is free software; you can redistribute it and/or
Packit Service da41ad
   modify it under the terms of the GNU Lesser General Public
Packit Service da41ad
   License as published by the Free Software Foundation; either
Packit Service da41ad
   version 2.1 of the License, or (at your option) any later version.
Packit Service da41ad
Packit Service da41ad
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service da41ad
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service da41ad
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service da41ad
   Lesser General Public License for more details.
Packit Service da41ad
Packit Service da41ad
   You should have received a copy of the GNU Lesser General Public
Packit Service da41ad
   License along with the GNU C Library; if not, see
Packit Service da41ad
   <https://www.gnu.org/licenses/>.  */
Packit Service da41ad
Packit Service da41ad
#include <ldsodefs.h>
Packit Service da41ad
#include <string.h>
Packit Service da41ad
#include <elf_machine_sym_no_match.h>
Packit Service da41ad
#include <dl-hash.h>
Packit Service da41ad
Packit Service da41ad
/* This function corresponds to do_lookup_x in elf/dl-lookup.c.  The
Packit Service da41ad
   variant here is simplified because it requires symbol
Packit Service da41ad
   versioning.  */
Packit Service da41ad
static const ElfW(Sym) *
Packit Service da41ad
check_match (const struct link_map *const map, const char *const undef_name,
Packit Service da41ad
             const char *version, uint32_t version_hash,
Packit Service da41ad
             const Elf_Symndx symidx)
Packit Service da41ad
{
Packit Service da41ad
  const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
Packit Service da41ad
  const ElfW(Sym) *sym = &symtab[symidx];
Packit Service da41ad
Packit Service da41ad
  unsigned int stt = ELFW(ST_TYPE) (sym->st_info);
Packit Service da41ad
  if (__glibc_unlikely ((sym->st_value == 0 /* No value.  */
Packit Service da41ad
                         && sym->st_shndx != SHN_ABS
Packit Service da41ad
                         && stt != STT_TLS)
Packit Service da41ad
                        || elf_machine_sym_no_match (sym)))
Packit Service da41ad
    return NULL;
Packit Service da41ad
Packit Service da41ad
  /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC,
Packit Service da41ad
     STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no
Packit Service da41ad
     code/data definitions.  */
Packit Service da41ad
#define ALLOWED_STT \
Packit Service da41ad
  ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \
Packit Service da41ad
   | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC))
Packit Service da41ad
  if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0))
Packit Service da41ad
    return NULL;
Packit Service da41ad
Packit Service da41ad
  const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
Packit Service da41ad
Packit Service da41ad
  if (strcmp (strtab + sym->st_name, undef_name) != 0)
Packit Service da41ad
    /* Not the symbol we are looking for.  */
Packit Service da41ad
    return NULL;
Packit Service da41ad
Packit Service da41ad
  ElfW(Half) ndx = map->l_versyms[symidx] & 0x7fff;
Packit Service da41ad
  if (map->l_versions[ndx].hash != version_hash
Packit Service da41ad
      || strcmp (map->l_versions[ndx].name, version) != 0)
Packit Service da41ad
    /* It's not the version we want.  */
Packit Service da41ad
    return NULL;
Packit Service da41ad
Packit Service da41ad
  return sym;
Packit Service da41ad
}
Packit Service da41ad
Packit Service da41ad
Packit Service da41ad
/* This function corresponds to do_lookup_x in elf/dl-lookup.c.  The
Packit Service da41ad
   variant here is simplified because it does not search object
Packit Service da41ad
   dependencies.  It is optimized for a successful lookup.  */
Packit Service da41ad
const ElfW(Sym) *
Packit Service da41ad
_dl_lookup_direct (struct link_map *map,
Packit Service da41ad
                   const char *undef_name, uint32_t new_hash,
Packit Service da41ad
                   const char *version, uint32_t version_hash)
Packit Service da41ad
{
Packit Service da41ad
  const ElfW(Addr) *bitmask = map->l_gnu_bitmask;
Packit Service da41ad
  if (__glibc_likely (bitmask != NULL))
Packit Service da41ad
    {
Packit Service da41ad
      Elf32_Word bucket = map->l_gnu_buckets[new_hash % map->l_nbuckets];
Packit Service da41ad
      if (bucket != 0)
Packit Service da41ad
        {
Packit Service da41ad
          const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket];
Packit Service da41ad
Packit Service da41ad
          do
Packit Service da41ad
            if (((*hasharr ^ new_hash) >> 1) == 0)
Packit Service da41ad
              {
Packit Service da41ad
                Elf_Symndx symidx = ELF_MACHINE_HASH_SYMIDX (map, hasharr);
Packit Service da41ad
                const ElfW(Sym) *sym = check_match (map, undef_name,
Packit Service da41ad
                                                    version, version_hash,
Packit Service da41ad
                                                    symidx);
Packit Service da41ad
                if (sym != NULL)
Packit Service da41ad
                  return sym;
Packit Service da41ad
              }
Packit Service da41ad
          while ((*hasharr++ & 1u) == 0);
Packit Service da41ad
        }
Packit Service da41ad
    }
Packit Service da41ad
  else
Packit Service da41ad
    {
Packit Service da41ad
      /* Fallback code for lack of GNU_HASH support.  */
Packit Service da41ad
      uint32_t old_hash = _dl_elf_hash (undef_name);
Packit Service da41ad
Packit Service da41ad
      /* Use the old SysV-style hash table.  Search the appropriate
Packit Service da41ad
         hash bucket in this object's symbol table for a definition
Packit Service da41ad
         for the same symbol name.  */
Packit Service da41ad
      for (Elf_Symndx symidx = map->l_buckets[old_hash % map->l_nbuckets];
Packit Service da41ad
           symidx != STN_UNDEF;
Packit Service da41ad
           symidx = map->l_chain[symidx])
Packit Service da41ad
        {
Packit Service da41ad
          const ElfW(Sym) *sym = check_match (map, undef_name,
Packit Service da41ad
                                              version, version_hash, symidx);
Packit Service da41ad
          if (sym != NULL)
Packit Service da41ad
            return sym;
Packit Service da41ad
        }
Packit Service da41ad
    }
Packit Service da41ad
Packit Service da41ad
  return NULL;
Packit Service da41ad
}