update from main archive 961203
[platform/upstream/glibc.git] / elf / dl-lookup.c
1 /* Look up a symbol in the loaded objects.
2    Copyright (C) 1995, 1996 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 Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <stddef.h>
21 #include <link.h>
22 #include <assert.h>
23 #include <string.h>
24
25
26 struct sym_val
27   {
28     ElfW(Addr) a;
29     const ElfW(Sym) *s;
30   };
31
32
33 /* This is the hashing function specified by the ELF ABI.  */
34 static inline unsigned
35 _dl_elf_hash (const char *name)
36 {
37   unsigned long int hash = 0;
38   while (*name != '\0')
39     {
40       unsigned long int hi;
41       hash = (hash << 4) + *name++;
42       hi = hash & 0xf0000000;
43       if (hi != 0)
44         {
45           hash ^= hi >> 24;
46           /* The ELF ABI says `hash &= ~hi', but this is equivalent
47              in this case and on some machines one insn instead of two.  */
48           hash ^= hi;
49         }
50     }
51   return hash;
52 }
53
54
55 /* Inner part of the lookup functions.  */
56 static inline ElfW(Addr)
57 do_lookup (const char *undef_name, unsigned long int hash,
58            const ElfW(Sym) **ref, struct sym_val *result,
59            struct link_map *list[], size_t i, size_t n,
60            const char *reference_name, struct link_map *skip, int flags)
61 {
62   struct link_map *map;
63
64   for (; i < n; ++i)
65     {
66       const ElfW(Sym) *symtab;
67       const char *strtab;
68       ElfW(Symndx) symidx;
69
70       map = list[i];
71
72       /* Here come the extra test needed for `_dl_lookup_symbol_skip'.  */
73       if (skip != NULL && map == skip)
74         continue;
75
76       /* Don't search the executable when resolving a copy reloc.  */
77       if (flags & DL_LOOKUP_NOEXEC && map->l_type == lt_executable)
78         continue;
79
80       symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
81       strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
82
83       /* Search the appropriate hash bucket in this object's symbol table
84          for a definition for the same symbol name.  */
85       for (symidx = map->l_buckets[hash % map->l_nbuckets];
86            symidx != STN_UNDEF;
87            symidx = map->l_chain[symidx])
88         {
89           const ElfW(Sym) *sym = &symtab[symidx];
90
91           if (sym->st_value == 0 || /* No value.  */
92               ((flags & DL_LOOKUP_NOPLT) != 0 /* Reject PLT entry.  */
93                && sym->st_shndx == SHN_UNDEF))
94             continue;
95
96           switch (ELFW(ST_TYPE) (sym->st_info))
97             {
98             case STT_NOTYPE:
99             case STT_FUNC:
100             case STT_OBJECT:
101               break;
102             default:
103               /* Not a code/data definition.  */
104               continue;
105             }
106
107           if (sym != *ref && strcmp (strtab + sym->st_name, undef_name))
108             /* Not the symbol we are looking for.  */
109             continue;
110
111           switch (ELFW(ST_BIND) (sym->st_info))
112             {
113             case STB_GLOBAL:
114               /* Global definition.  Just what we need.  */
115               result->s = sym;
116               result->a = map->l_addr;
117               return 1;
118             case STB_WEAK:
119               /* Weak definition.  Use this value if we don't find
120                  another.  */
121               if (! result->s)
122                 {
123                   result->s = sym;
124                   result->a = map->l_addr;
125                 }
126               break;
127             default:
128               /* Local symbols are ignored.  */
129               break;
130             }
131         }
132     }
133
134   /* We have not found anything until now.  */
135   return 0;
136 }
137
138 /* Search loaded objects' symbol tables for a definition of the symbol
139    UNDEF_NAME.  FLAGS is a set of flags.  If DL_LOOKUP_NOEXEC is set,
140    then don't search the executable for a definition; this used for
141    copy relocs.  If DL_LOOKUP_NOPLT is set, then a PLT entry cannot
142    satisfy the reference; some different binding must be found.  */
143
144 ElfW(Addr)
145 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
146                    struct link_map *symbol_scope[],
147                    const char *reference_name,
148                    int flags)
149 {
150   const unsigned long int hash = _dl_elf_hash (undef_name);
151   struct sym_val current_value = { 0, NULL };
152   struct link_map **scope;
153
154   /* Search the relevant loaded objects for a definition.  */
155   for (scope = symbol_scope; *scope; ++scope)
156     if (do_lookup (undef_name, hash, ref, &current_value,
157                    (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
158                    reference_name, NULL, flags))
159       break;
160
161   if (current_value.s == NULL &&
162       (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
163     {
164       /* We could find no value for a strong reference.  */
165       const char msg[] = "undefined symbol: ";
166       const size_t len = strlen (undef_name);
167       char buf[sizeof msg + len];
168       memcpy (buf, msg, sizeof msg - 1);
169       memcpy (&buf[sizeof msg - 1], undef_name, len + 1);
170       _dl_signal_error (0, reference_name, buf);
171     }
172
173   *ref = current_value.s;
174   return current_value.a;
175 }
176
177
178 /* This function is nearly the same as `_dl_lookup_symbol' but it
179    skips in the first list all objects until SKIP_MAP is found.  I.e.,
180    it only considers objects which were loaded after the described
181    object.  If there are more search lists the object described by
182    SKIP_MAP is only skipped.  */
183 ElfW(Addr)
184 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
185                         struct link_map *symbol_scope[],
186                         const char *reference_name,
187                         struct link_map *skip_map,
188                         int flags)
189 {
190   const unsigned long int hash = _dl_elf_hash (undef_name);
191   struct sym_val current_value = { 0, NULL };
192   struct link_map **scope;
193   size_t i;
194
195   /* Search the relevant loaded objects for a definition.  */
196   scope = symbol_scope;
197   for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
198     assert (i < (*scope)->l_ndupsearchlist);
199
200   if (! do_lookup (undef_name, hash, ref, &current_value,
201                    (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
202                    reference_name, skip_map, flags))
203     while (*++scope)
204       if (do_lookup (undef_name, hash, ref, &current_value,
205                      (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
206                      reference_name, skip_map, flags))
207         break;
208
209   *ref = current_value.s;
210   return current_value.a;
211 }
212
213
214 /* Cache the location of MAP's hash table.  */
215
216 void
217 _dl_setup_hash (struct link_map *map)
218 {
219   ElfW(Symndx) *hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
220   ElfW(Symndx) nchain;
221   map->l_nbuckets = *hash++;
222   nchain = *hash++;
223   map->l_buckets = hash;
224   hash += map->l_nbuckets;
225   map->l_chain = hash;
226 }