1 /* Look up a symbol in a single specified object.
2    Copyright (C) 1995-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <ldsodefs.h>
20 #include <string.h>
21 #include <elf_machine_sym_no_match.h>
22 #include <dl-hash.h>
23 
24 /* This function corresponds to do_lookup_x in elf/dl-lookup.c.  The
25    variant here is simplified because it requires symbol
26    versioning.  */
ElfW(Sym)27 static const ElfW(Sym) *
28 check_match (const struct link_map *const map, const char *const undef_name,
29              const char *version, uint32_t version_hash,
30              const Elf_Symndx symidx)
31 {
32   const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
33   const ElfW(Sym) *sym = &symtab[symidx];
34 
35   unsigned int stt = ELFW(ST_TYPE) (sym->st_info);
36   if (__glibc_unlikely ((sym->st_value == 0 /* No value.  */
37                          && sym->st_shndx != SHN_ABS
38                          && stt != STT_TLS)
39                         || elf_machine_sym_no_match (sym)))
40     return NULL;
41 
42   /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC,
43      STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no
44      code/data definitions.  */
45 #define ALLOWED_STT \
46   ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \
47    | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC))
48   if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0))
49     return NULL;
50 
51   const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
52 
53   if (strcmp (strtab + sym->st_name, undef_name) != 0)
54     /* Not the symbol we are looking for.  */
55     return NULL;
56 
57   ElfW(Half) ndx = map->l_versyms[symidx] & 0x7fff;
58   if (map->l_versions[ndx].hash != version_hash
59       || strcmp (map->l_versions[ndx].name, version) != 0)
60     /* It's not the version we want.  */
61     return NULL;
62 
63   return sym;
64 }
65 
66 
67 /* This function corresponds to do_lookup_x in elf/dl-lookup.c.  The
68    variant here is simplified because it does not search object
69    dependencies.  It is optimized for a successful lookup.  */
ElfW(Sym)70 const ElfW(Sym) *
71 _dl_lookup_direct (struct link_map *map,
72                    const char *undef_name, uint32_t new_hash,
73                    const char *version, uint32_t version_hash)
74 {
75   const ElfW(Addr) *bitmask = map->l_gnu_bitmask;
76   if (__glibc_likely (bitmask != NULL))
77     {
78       Elf32_Word bucket = map->l_gnu_buckets[new_hash % map->l_nbuckets];
79       if (bucket != 0)
80         {
81           const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket];
82 
83           do
84             if (((*hasharr ^ new_hash) >> 1) == 0)
85               {
86                 Elf_Symndx symidx = ELF_MACHINE_HASH_SYMIDX (map, hasharr);
87                 const ElfW(Sym) *sym = check_match (map, undef_name,
88                                                     version, version_hash,
89                                                     symidx);
90                 if (sym != NULL)
91                   return sym;
92               }
93           while ((*hasharr++ & 1u) == 0);
94         }
95     }
96   else
97     {
98       /* Fallback code for lack of GNU_HASH support.  */
99       uint32_t old_hash = _dl_elf_hash (undef_name);
100 
101       /* Use the old SysV-style hash table.  Search the appropriate
102          hash bucket in this object's symbol table for a definition
103          for the same symbol name.  */
104       for (Elf_Symndx symidx = map->l_buckets[old_hash % map->l_nbuckets];
105            symidx != STN_UNDEF;
106            symidx = map->l_chain[symidx])
107         {
108           const ElfW(Sym) *sym = check_match (map, undef_name,
109                                               version, version_hash, symidx);
110           if (sym != NULL)
111             return sym;
112         }
113     }
114 
115   return NULL;
116 }
117