Update.
[glibc.git] / elf / dl-lookup.c
blobf85aa373c2e04c25cc6ae1284d38ac2654b99304
1 /* Look up a symbol in the loaded objects.
2 Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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. */
20 #include <stddef.h>
21 #include <link.h>
22 #include <assert.h>
23 #include <string.h>
25 #include "dl-hash.h"
26 #include <dl-machine.h>
27 #include "../stdio-common/_itoa.h"
29 #define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
31 struct sym_val
33 ElfW(Addr) a;
34 const ElfW(Sym) *s;
38 #define make_string(string, rest...) \
39 ({ \
40 const char *all[] = { string, ## rest }; \
41 size_t len, cnt; \
42 char *result, *cp; \
44 len = 1; \
45 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
46 len += strlen (all[cnt]); \
48 cp = result = alloca (len); \
49 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
50 cp = stpcpy (cp, all[cnt]); \
52 result; \
56 /* Inner part of the lookup functions. We return a value > 0 if we
57 found the symbol, the value 0 if nothing is found and < 0 if
58 something bad happened. */
59 static inline int
60 do_lookup (const char *undef_name, unsigned long int hash,
61 const ElfW(Sym) *ref, struct sym_val *result,
62 struct link_map *list[], size_t i, size_t n,
63 const char *reference_name, const struct r_found_version *version,
64 struct link_map *skip, int reloc_type)
66 struct link_map *map;
68 for (; i < n; ++i)
70 const ElfW(Sym) *symtab;
71 const char *strtab;
72 const ElfW(Half) *verstab;
73 ElfW(Symndx) symidx;
75 map = list[i];
77 /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */
78 if (skip != NULL && map == skip)
79 continue;
81 /* Don't search the executable when resolving a copy reloc. */
82 if (elf_machine_lookup_noexec_p (reloc_type) &&
83 map->l_type == lt_executable)
84 continue;
86 symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
87 strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
88 if (map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL)
89 verstab = ((void *) map->l_addr
90 + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr);
91 else
92 verstab = NULL;
94 /* Search the appropriate hash bucket in this object's symbol table
95 for a definition for the same symbol name. */
96 for (symidx = map->l_buckets[hash % map->l_nbuckets];
97 symidx != STN_UNDEF;
98 symidx = map->l_chain[symidx])
100 const ElfW(Sym) *sym = &symtab[symidx];
102 if (sym->st_value == 0 || /* No value. */
103 (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry. */
104 && sym->st_shndx == SHN_UNDEF))
105 continue;
107 switch (ELFW(ST_TYPE) (sym->st_info))
109 case STT_NOTYPE:
110 case STT_FUNC:
111 case STT_OBJECT:
112 break;
113 default:
114 /* Not a code/data definition. */
115 continue;
118 if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
119 /* Not the symbol we are looking for. */
120 continue;
122 if (version == NULL)
124 /* No specific version is selected. When the object
125 file also does not define a version we have a match.
126 Otherwise we only accept the default version, i.e.,
127 the version which name is "". */
128 if (verstab != NULL)
130 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
131 if (map->l_versions[ndx].hash != 0)
132 continue;
135 else
137 if (verstab == NULL)
139 /* We need a versioned system but haven't found any.
140 If this is the object which is referenced in the
141 verneed entry it is a bug in the library since a
142 symbol must not simply disappear. */
143 if (version->filename != NULL
144 && _dl_name_match_p (version->filename, map))
145 return -1;
146 /* Otherwise we accept the symbol. */
148 else
150 /* We can match the version information. */
151 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
152 if (map->l_versions[ndx].hash != version->hash
153 || strcmp (map->l_versions[ndx].name, version->name))
154 /* It's not the version we want. */
155 continue;
159 switch (ELFW(ST_BIND) (sym->st_info))
161 case STB_GLOBAL:
162 /* Global definition. Just what we need. */
163 result->s = sym;
164 result->a = map->l_addr;
165 return 1;
166 case STB_WEAK:
167 /* Weak definition. Use this value if we don't find
168 another. */
169 if (! result->s)
171 result->s = sym;
172 result->a = map->l_addr;
174 break;
175 default:
176 /* Local symbols are ignored. */
177 break;
181 /* If this current is the one mentioned in the verneed entry it
182 is a bug. */
183 if (version != NULL && version->filename != NULL
184 && _dl_name_match_p (version->filename, map))
185 return -1;
188 /* We have not found anything until now. */
189 return 0;
192 /* Search loaded objects' symbol tables for a definition of the symbol
193 UNDEF_NAME. */
195 ElfW(Addr)
196 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
197 struct link_map *symbol_scope[],
198 const char *reference_name,
199 int reloc_type)
201 const unsigned long int hash = _dl_elf_hash (undef_name);
202 struct sym_val current_value = { 0, NULL };
203 struct link_map **scope;
205 /* Search the relevant loaded objects for a definition. */
206 for (scope = symbol_scope; *scope; ++scope)
207 if (do_lookup (undef_name, hash, *ref, &current_value,
208 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
209 reference_name, NULL, NULL, reloc_type))
210 break;
212 if (current_value.s == NULL &&
213 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
214 /* We could find no value for a strong reference. */
215 _dl_signal_error (0, reference_name,
216 make_string ("undefined symbol: ", undef_name));
218 *ref = current_value.s;
219 return current_value.a;
223 /* This function is nearly the same as `_dl_lookup_symbol' but it
224 skips in the first list all objects until SKIP_MAP is found. I.e.,
225 it only considers objects which were loaded after the described
226 object. If there are more search lists the object described by
227 SKIP_MAP is only skipped. */
228 ElfW(Addr)
229 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
230 struct link_map *symbol_scope[],
231 const char *reference_name,
232 struct link_map *skip_map)
234 const unsigned long int hash = _dl_elf_hash (undef_name);
235 struct sym_val current_value = { 0, NULL };
236 struct link_map **scope;
237 size_t i;
239 /* Search the relevant loaded objects for a definition. */
240 scope = symbol_scope;
241 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
242 assert (i < (*scope)->l_ndupsearchlist);
244 if (! do_lookup (undef_name, hash, *ref, &current_value,
245 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
246 reference_name, NULL, skip_map, 0))
247 while (*++scope)
248 if (do_lookup (undef_name, hash, *ref, &current_value,
249 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
250 reference_name, NULL, skip_map, 0))
251 break;
253 *ref = current_value.s;
254 return current_value.a;
258 /* This function works like _dl_lookup_symbol but it takes an
259 additional arguement with the version number of the requested
260 symbol.
262 XXX We'll see whether we need this separate function. */
263 ElfW(Addr)
264 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
265 struct link_map *symbol_scope[],
266 const char *reference_name,
267 const struct r_found_version *version,
268 int reloc_type)
270 const unsigned long int hash = _dl_elf_hash (undef_name);
271 struct sym_val current_value = { 0, NULL };
272 struct link_map **scope;
274 /* Search the relevant loaded objects for a definition. */
275 for (scope = symbol_scope; *scope; ++scope)
277 int res = do_lookup (undef_name, hash, *ref, &current_value,
278 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
279 reference_name, version, NULL, reloc_type);
280 if (res > 0)
281 break;
283 if (res < 0)
284 /* Oh, oh. The file named in the relocation entry does not
285 contain the needed symbol. */
286 _dl_signal_error (0, *reference_name ? reference_name : NULL,
287 make_string ("symbol ", undef_name, ", version ",
288 version->name,
289 " not defined in file ",
290 version->filename,
291 " with link time reference"));
294 if (current_value.s == NULL &&
295 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
296 /* We could find no value for a strong reference. */
297 _dl_signal_error (0, reference_name,
298 make_string ("undefined symbol: ", undef_name,
299 ", version ", version->name ?: NULL));
301 *ref = current_value.s;
302 return current_value.a;
306 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
307 with the version we are looking for. */
308 ElfW(Addr)
309 _dl_lookup_versioned_symbol_skip (const char *undef_name,
310 const ElfW(Sym) **ref,
311 struct link_map *symbol_scope[],
312 const char *reference_name,
313 const struct r_found_version *version,
314 struct link_map *skip_map)
316 const unsigned long int hash = _dl_elf_hash (undef_name);
317 struct sym_val current_value = { 0, NULL };
318 struct link_map **scope;
319 size_t i;
321 /* Search the relevant loaded objects for a definition. */
322 scope = symbol_scope;
323 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
324 assert (i < (*scope)->l_ndupsearchlist);
326 if (! do_lookup (undef_name, hash, *ref, &current_value,
327 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
328 reference_name, version, skip_map, 0))
329 while (*++scope)
330 if (do_lookup (undef_name, hash, *ref, &current_value,
331 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
332 reference_name, version, skip_map, 0))
333 break;
335 *ref = current_value.s;
336 return current_value.a;
340 /* Cache the location of MAP's hash table. */
342 void
343 _dl_setup_hash (struct link_map *map)
345 ElfW(Symndx) *hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
346 ElfW(Symndx) nchain;
347 map->l_nbuckets = *hash++;
348 nchain = *hash++;
349 map->l_buckets = hash;
350 hash += map->l_nbuckets;
351 map->l_chain = hash;