Update.
[glibc.git] / elf / dl-lookup.c
blob6c1c04b5f56c1306e9a0aa1cbb7809d697ff5d1a
1 /* Look up a symbol in the loaded objects.
2 Copyright (C) 1995, 1996, 1997, 1998 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>
29 #define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
31 /* We need this string more than once. */
32 static const char undefined_msg[] = "undefined symbol: ";
35 struct sym_val
37 ElfW(Addr) a;
38 const ElfW(Sym) *s;
42 #define make_string(string, rest...) \
43 ({ \
44 const char *all[] = { string, ## rest }; \
45 size_t len, cnt; \
46 char *result, *cp; \
48 len = 1; \
49 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
50 len += strlen (all[cnt]); \
52 cp = result = alloca (len); \
53 for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \
54 cp = __stpcpy (cp, all[cnt]); \
56 result; \
60 /* Inner part of the lookup functions. We return a value > 0 if we
61 found the symbol, the value 0 if nothing is found and < 0 if
62 something bad happened. */
63 static inline int
64 do_lookup (const char *undef_name, unsigned long int hash,
65 const ElfW(Sym) *ref, struct sym_val *result,
66 struct link_map *list[], size_t i, size_t n,
67 const char *reference_name, const struct r_found_version *version,
68 struct link_map *skip, int reloc_type)
70 struct link_map *map;
72 for (; i < n; ++i)
74 const ElfW(Sym) *symtab;
75 const char *strtab;
76 const ElfW(Half) *verstab;
77 ElfW(Symndx) symidx;
79 map = list[i];
81 /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */
82 if (skip != NULL && map == skip)
83 continue;
85 /* Skip objects that could not be opened, which can occur in trace
86 mode. */
87 if (map->l_opencount == 0)
88 continue;
90 /* Don't search the executable when resolving a copy reloc. */
91 if (elf_machine_lookup_noexec_p (reloc_type) &&
92 map->l_type == lt_executable)
93 continue;
95 /* Skip objects without symbol tables. */
96 if (map->l_info[DT_SYMTAB] == NULL)
97 continue;
99 symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
100 strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
101 if (map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL)
102 verstab = ((void *) map->l_addr
103 + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr);
104 else
105 verstab = NULL;
107 /* Search the appropriate hash bucket in this object's symbol table
108 for a definition for the same symbol name. */
109 for (symidx = map->l_buckets[hash % map->l_nbuckets];
110 symidx != STN_UNDEF;
111 symidx = map->l_chain[symidx])
113 const ElfW(Sym) *sym = &symtab[symidx];
115 if (sym->st_value == 0 || /* No value. */
116 (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry. */
117 && sym->st_shndx == SHN_UNDEF))
118 continue;
120 if (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC)
121 /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC entries
122 since these are no code/data definitions. */
123 continue;
125 if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
126 /* Not the symbol we are looking for. */
127 continue;
129 if (version == NULL)
131 /* No specific version is selected. When the object
132 file also does not define a version we have a match.
133 Otherwise we only accept the default version, i.e.,
134 the version which name is "". */
135 if (verstab != NULL)
137 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
138 if (ndx > 2) /* map->l_versions[ndx].hash != 0) */
139 continue;
142 else
144 if (verstab == NULL)
146 /* We need a versioned system but haven't found any.
147 If this is the object which is referenced in the
148 verneed entry it is a bug in the library since a
149 symbol must not simply disappear. */
150 if (version->filename != NULL
151 && _dl_name_match_p (version->filename, map))
152 return -2;
153 /* Otherwise we accept the symbol. */
155 else
157 /* We can match the version information or use the
158 default one if it is not hidden. */
159 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
160 if ((map->l_versions[ndx].hash != version->hash
161 || strcmp (map->l_versions[ndx].name, version->name))
162 && (version->hidden || map->l_versions[ndx].hash
163 || (verstab[symidx] & 0x8000)))
164 /* It's not the version we want. */
165 continue;
169 switch (ELFW(ST_BIND) (sym->st_info))
171 case STB_GLOBAL:
172 /* Global definition. Just what we need. */
173 result->s = sym;
174 result->a = map->l_addr;
175 return 1;
176 case STB_WEAK:
177 /* Weak definition. Use this value if we don't find
178 another. */
179 if (! result->s)
181 result->s = sym;
182 result->a = map->l_addr;
184 break;
185 default:
186 /* Local symbols are ignored. */
187 break;
190 /* There cannot be another entry for this symbol so stop here. */
191 break;
194 /* If this current map is the one mentioned in the verneed entry
195 and we have not found a weak entry, it is a bug. */
196 if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
197 && _dl_name_match_p (version->filename, map))
198 return -1;
201 /* We have not found anything until now. */
202 return 0;
205 /* Search loaded objects' symbol tables for a definition of the symbol
206 UNDEF_NAME. */
208 ElfW(Addr)
209 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
210 struct link_map *symbol_scope[],
211 const char *reference_name,
212 int reloc_type)
214 const unsigned long int hash = _dl_elf_hash (undef_name);
215 struct sym_val current_value = { 0, NULL };
216 struct link_map **scope;
218 /* Search the relevant loaded objects for a definition. */
219 for (scope = symbol_scope; *scope; ++scope)
220 if (do_lookup (undef_name, hash, *ref, &current_value,
221 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
222 reference_name, NULL, NULL, reloc_type))
223 break;
225 if (current_value.s == NULL &&
226 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
227 /* We could find no value for a strong reference. */
228 _dl_signal_error (0, reference_name,
229 make_string (undefined_msg, undef_name));
231 *ref = current_value.s;
232 return current_value.a;
236 /* This function is nearly the same as `_dl_lookup_symbol' but it
237 skips in the first list all objects until SKIP_MAP is found. I.e.,
238 it only considers objects which were loaded after the described
239 object. If there are more search lists the object described by
240 SKIP_MAP is only skipped. */
241 ElfW(Addr)
242 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
243 struct link_map *symbol_scope[],
244 const char *reference_name,
245 struct link_map *skip_map)
247 const unsigned long int hash = _dl_elf_hash (undef_name);
248 struct sym_val current_value = { 0, NULL };
249 struct link_map **scope;
250 size_t i;
252 /* Search the relevant loaded objects for a definition. */
253 scope = symbol_scope;
254 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
255 assert (i < (*scope)->l_ndupsearchlist);
257 if (! do_lookup (undef_name, hash, *ref, &current_value,
258 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
259 reference_name, NULL, skip_map, 0))
260 while (*++scope)
261 if (do_lookup (undef_name, hash, *ref, &current_value,
262 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
263 reference_name, NULL, skip_map, 0))
264 break;
266 *ref = current_value.s;
267 return current_value.a;
271 /* This function works like _dl_lookup_symbol but it takes an
272 additional arguement with the version number of the requested
273 symbol.
275 XXX We'll see whether we need this separate function. */
276 ElfW(Addr)
277 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
278 struct link_map *symbol_scope[],
279 const char *reference_name,
280 const struct r_found_version *version,
281 int reloc_type)
283 extern char **_dl_argv;
284 const unsigned long int hash = _dl_elf_hash (undef_name);
285 struct sym_val current_value = { 0, NULL };
286 struct link_map **scope;
288 /* Search the relevant loaded objects for a definition. */
289 for (scope = symbol_scope; *scope; ++scope)
291 int res = do_lookup (undef_name, hash, *ref, &current_value,
292 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
293 reference_name, version, NULL, reloc_type);
294 if (res > 0)
295 break;
297 if (res < 0)
298 /* Oh, oh. The file named in the relocation entry does not
299 contain the needed symbol. */
300 _dl_signal_error (0, (*reference_name
301 ? reference_name
302 : (_dl_argv[0] ?: "<main program>")),
303 make_string ("symbol ", undef_name, ", version ",
304 version->name,
305 " not defined in file ",
306 version->filename,
307 " with link time reference",
308 res == -2
309 ? " (no version symbols)" : ""));
312 if (current_value.s == NULL &&
313 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
314 /* We could find no value for a strong reference. */
315 _dl_signal_error (0, reference_name,
316 make_string (undefined_msg, undef_name,
317 ", version ", version->name ?: NULL));
319 *ref = current_value.s;
320 return current_value.a;
324 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
325 with the version we are looking for. */
326 ElfW(Addr)
327 _dl_lookup_versioned_symbol_skip (const char *undef_name,
328 const ElfW(Sym) **ref,
329 struct link_map *symbol_scope[],
330 const char *reference_name,
331 const struct r_found_version *version,
332 struct link_map *skip_map)
334 const unsigned long int hash = _dl_elf_hash (undef_name);
335 struct sym_val current_value = { 0, NULL };
336 struct link_map **scope;
337 size_t i;
339 /* Search the relevant loaded objects for a definition. */
340 scope = symbol_scope;
341 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
342 assert (i < (*scope)->l_ndupsearchlist);
344 if (! do_lookup (undef_name, hash, *ref, &current_value,
345 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
346 reference_name, version, skip_map, 0))
347 while (*++scope)
348 if (do_lookup (undef_name, hash, *ref, &current_value,
349 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
350 reference_name, version, skip_map, 0))
351 break;
353 if (current_value.s == NULL &&
354 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
356 /* We could find no value for a strong reference. */
357 const size_t len = strlen (undef_name);
358 char buf[sizeof undefined_msg + len];
359 __mempcpy (__mempcpy (buf, undefined_msg, sizeof undefined_msg - 1),
360 undef_name, len + 1);
361 _dl_signal_error (0, reference_name, buf);
364 *ref = current_value.s;
365 return current_value.a;
369 /* Cache the location of MAP's hash table. */
371 void
372 _dl_setup_hash (struct link_map *map)
374 ElfW(Symndx) *hash;
375 ElfW(Symndx) nchain;
377 if (!map->l_info[DT_HASH])
378 return;
379 hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
381 map->l_nbuckets = *hash++;
382 nchain = *hash++;
383 map->l_buckets = hash;
384 hash += map->l_nbuckets;
385 map->l_chain = hash;