Update.
[glibc.git] / elf / dl-lookup.c
blobf2ce4c9c3cb61814e96c0c7c8ad2dba9cc9d3f8c
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 <alloca.h>
21 #include <stddef.h>
22 #include <link.h>
23 #include <assert.h>
24 #include <string.h>
26 #include "dl-hash.h"
27 #include <dl-machine.h>
28 #include <stdio-common/_itoa.h>
30 #define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
32 /* We need this string more than once. */
33 static const char undefined_msg[] = "undefined symbol: ";
36 struct sym_val
38 ElfW(Addr) a;
39 const ElfW(Sym) *s;
43 #define make_string(string, rest...) \
44 ({ \
45 const char *all[] = { string, ## rest }; \
46 size_t len, cnt; \
47 char *result, *cp; \
49 len = 1; \
50 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
51 len += strlen (all[cnt]); \
53 cp = result = alloca (len); \
54 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
55 cp = stpcpy (cp, all[cnt]); \
57 result; \
61 /* Inner part of the lookup functions. We return a value > 0 if we
62 found the symbol, the value 0 if nothing is found and < 0 if
63 something bad happened. */
64 static inline int
65 do_lookup (const char *undef_name, unsigned long int hash,
66 const ElfW(Sym) *ref, struct sym_val *result,
67 struct link_map *list[], size_t i, size_t n,
68 const char *reference_name, const struct r_found_version *version,
69 struct link_map *skip, int reloc_type)
71 struct link_map *map;
73 for (; i < n; ++i)
75 const ElfW(Sym) *symtab;
76 const char *strtab;
77 const ElfW(Half) *verstab;
78 ElfW(Symndx) symidx;
80 map = list[i];
82 /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */
83 if (skip != NULL && map == skip)
84 continue;
86 /* Don't search the executable when resolving a copy reloc. */
87 if (elf_machine_lookup_noexec_p (reloc_type) &&
88 map->l_type == lt_executable)
89 continue;
91 symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
92 strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
93 if (map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL)
94 verstab = ((void *) map->l_addr
95 + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr);
96 else
97 verstab = NULL;
99 /* Search the appropriate hash bucket in this object's symbol table
100 for a definition for the same symbol name. */
101 for (symidx = map->l_buckets[hash % map->l_nbuckets];
102 symidx != STN_UNDEF;
103 symidx = map->l_chain[symidx])
105 const ElfW(Sym) *sym = &symtab[symidx];
107 if (sym->st_value == 0 || /* No value. */
108 (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry. */
109 && sym->st_shndx == SHN_UNDEF))
110 continue;
112 if (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC)
113 /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC entries
114 since these are no code/data definitions. */
115 continue;
117 if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
118 /* Not the symbol we are looking for. */
119 continue;
121 if (version == NULL)
123 /* No specific version is selected. When the object
124 file also does not define a version we have a match.
125 Otherwise we only accept the default version, i.e.,
126 the version which name is "". */
127 if (verstab != NULL)
129 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
130 if (ndx > 2) /* map->l_versions[ndx].hash != 0) */
131 continue;
134 else
136 if (verstab == NULL)
138 /* We need a versioned system but haven't found any.
139 If this is the object which is referenced in the
140 verneed entry it is a bug in the library since a
141 symbol must not simply disappear. */
142 if (version->filename != NULL
143 && _dl_name_match_p (version->filename, map))
144 return -2;
145 /* Otherwise we accept the symbol. */
147 else
149 /* We can match the version information. */
150 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
151 if (map->l_versions[ndx].hash != version->hash
152 || strcmp (map->l_versions[ndx].name, version->name))
153 /* It's not the version we want. */
154 continue;
158 switch (ELFW(ST_BIND) (sym->st_info))
160 case STB_GLOBAL:
161 /* Global definition. Just what we need. */
162 result->s = sym;
163 result->a = map->l_addr;
164 return 1;
165 case STB_WEAK:
166 /* Weak definition. Use this value if we don't find
167 another. */
168 if (! result->s)
170 result->s = sym;
171 result->a = map->l_addr;
173 break;
174 default:
175 /* Local symbols are ignored. */
176 break;
179 /* There cannot be another entry for this symbol so stop here. */
180 break;
183 /* If this current map is the one mentioned in the verneed entry
184 and we have not found a weak entry, it is a bug. */
185 if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
186 && _dl_name_match_p (version->filename, map))
187 return -1;
190 /* We have not found anything until now. */
191 return 0;
194 /* Search loaded objects' symbol tables for a definition of the symbol
195 UNDEF_NAME. */
197 ElfW(Addr)
198 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
199 struct link_map *symbol_scope[],
200 const char *reference_name,
201 int reloc_type)
203 const unsigned long int hash = _dl_elf_hash (undef_name);
204 struct sym_val current_value = { 0, NULL };
205 struct link_map **scope;
207 /* Search the relevant loaded objects for a definition. */
208 for (scope = symbol_scope; *scope; ++scope)
209 if (do_lookup (undef_name, hash, *ref, &current_value,
210 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
211 reference_name, NULL, NULL, reloc_type))
212 break;
214 if (current_value.s == NULL &&
215 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
216 /* We could find no value for a strong reference. */
217 _dl_signal_error (0, reference_name,
218 make_string (undefined_msg, undef_name));
220 *ref = current_value.s;
221 return current_value.a;
225 /* This function is nearly the same as `_dl_lookup_symbol' but it
226 skips in the first list all objects until SKIP_MAP is found. I.e.,
227 it only considers objects which were loaded after the described
228 object. If there are more search lists the object described by
229 SKIP_MAP is only skipped. */
230 ElfW(Addr)
231 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
232 struct link_map *symbol_scope[],
233 const char *reference_name,
234 struct link_map *skip_map)
236 const unsigned long int hash = _dl_elf_hash (undef_name);
237 struct sym_val current_value = { 0, NULL };
238 struct link_map **scope;
239 size_t i;
241 /* Search the relevant loaded objects for a definition. */
242 scope = symbol_scope;
243 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
244 assert (i < (*scope)->l_ndupsearchlist);
246 if (! do_lookup (undef_name, hash, *ref, &current_value,
247 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
248 reference_name, NULL, skip_map, 0))
249 while (*++scope)
250 if (do_lookup (undef_name, hash, *ref, &current_value,
251 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
252 reference_name, NULL, skip_map, 0))
253 break;
255 *ref = current_value.s;
256 return current_value.a;
260 /* This function works like _dl_lookup_symbol but it takes an
261 additional arguement with the version number of the requested
262 symbol.
264 XXX We'll see whether we need this separate function. */
265 ElfW(Addr)
266 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
267 struct link_map *symbol_scope[],
268 const char *reference_name,
269 const struct r_found_version *version,
270 int reloc_type)
272 const unsigned long int hash = _dl_elf_hash (undef_name);
273 struct sym_val current_value = { 0, NULL };
274 struct link_map **scope;
276 /* Search the relevant loaded objects for a definition. */
277 for (scope = symbol_scope; *scope; ++scope)
279 int res = do_lookup (undef_name, hash, *ref, &current_value,
280 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
281 reference_name, version, NULL, reloc_type);
282 if (res > 0)
283 break;
285 if (res < 0)
286 /* Oh, oh. The file named in the relocation entry does not
287 contain the needed symbol. */
288 _dl_signal_error (0, *reference_name ? reference_name : NULL,
289 make_string ("symbol ", undef_name, ", version ",
290 version->name,
291 " not defined in file ",
292 version->filename,
293 " with link time reference",
294 res == -2
295 ? " (no version symbols)" : ""));
298 if (current_value.s == NULL &&
299 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
300 /* We could find no value for a strong reference. */
301 _dl_signal_error (0, reference_name,
302 make_string (undefined_msg, undef_name,
303 ", version ", version->name ?: NULL));
305 *ref = current_value.s;
306 return current_value.a;
310 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
311 with the version we are looking for. */
312 ElfW(Addr)
313 _dl_lookup_versioned_symbol_skip (const char *undef_name,
314 const ElfW(Sym) **ref,
315 struct link_map *symbol_scope[],
316 const char *reference_name,
317 const struct r_found_version *version,
318 struct link_map *skip_map)
320 const unsigned long int hash = _dl_elf_hash (undef_name);
321 struct sym_val current_value = { 0, NULL };
322 struct link_map **scope;
323 size_t i;
325 /* Search the relevant loaded objects for a definition. */
326 scope = symbol_scope;
327 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
328 assert (i < (*scope)->l_ndupsearchlist);
330 if (! do_lookup (undef_name, hash, *ref, &current_value,
331 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
332 reference_name, version, skip_map, 0))
333 while (*++scope)
334 if (do_lookup (undef_name, hash, *ref, &current_value,
335 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
336 reference_name, version, skip_map, 0))
337 break;
339 if (current_value.s == NULL &&
340 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
342 /* We could find no value for a strong reference. */
343 const size_t len = strlen (undef_name);
344 char buf[sizeof undefined_msg + len];
345 memcpy (buf, undefined_msg, sizeof undefined_msg - 1);
346 memcpy (&buf[sizeof undefined_msg - 1], undef_name, len + 1);
347 _dl_signal_error (0, reference_name, buf);
350 *ref = current_value.s;
351 return current_value.a;
355 /* Cache the location of MAP's hash table. */
357 void
358 _dl_setup_hash (struct link_map *map)
360 ElfW(Symndx) *hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
361 ElfW(Symndx) nchain;
362 map->l_nbuckets = *hash++;
363 nchain = *hash++;
364 map->l_buckets = hash;
365 hash += map->l_nbuckets;
366 map->l_chain = hash;