Update.
[glibc/history.git] / elf / dl-lookup.c
blob5245c628d2a5b16bd4ed033e08248583af3b3774
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 <string.h>
22 #include <unistd.h>
23 #include <elf/ldsodefs.h>
24 #include "dl-hash.h"
25 #include <dl-machine.h>
27 #include <assert.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 const ElfW(Sym) *s;
38 struct link_map *m;
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 r_scope_elem *scope, size_t i, const char *reference_name,
67 const struct r_found_version *version, struct link_map *skip,
68 int reloc_type)
70 struct link_map **list = scope->r_list;
71 size_t n = scope->r_nlist;
72 struct link_map *map;
74 for (; i < n; ++i)
76 const ElfW(Sym) *symtab;
77 const char *strtab;
78 const ElfW(Half) *verstab;
79 ElfW(Symndx) symidx;
81 map = list[i];
83 /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */
84 if (skip != NULL && map == skip)
85 continue;
87 /* Skip objects that could not be opened, which can occur in trace
88 mode. */
89 if (map->l_opencount == 0)
90 continue;
92 /* Don't search the executable when resolving a copy reloc. */
93 if (elf_machine_lookup_noexec_p (reloc_type)
94 && map->l_type == lt_executable)
95 continue;
97 /* Skip objects without symbol tables. */
98 if (map->l_info[DT_SYMTAB] == NULL)
99 continue;
101 /* Print some debugging info if wanted. */
102 if (_dl_debug_symbols)
103 _dl_debug_message (1, "symbol=", undef_name, "; lookup in file=",
104 map->l_name[0] ? map->l_name : _dl_argv[0],
105 "\n", NULL);
107 symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
108 strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
109 verstab = map->l_versyms;
111 /* Search the appropriate hash bucket in this object's symbol table
112 for a definition for the same symbol name. */
113 for (symidx = map->l_buckets[hash % map->l_nbuckets];
114 symidx != STN_UNDEF;
115 symidx = map->l_chain[symidx])
117 const ElfW(Sym) *sym = &symtab[symidx];
119 if (sym->st_value == 0 || /* No value. */
120 (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry. */
121 && sym->st_shndx == SHN_UNDEF))
122 continue;
124 if (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC)
125 /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC entries
126 since these are no code/data definitions. */
127 continue;
129 if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
130 /* Not the symbol we are looking for. */
131 continue;
133 if (version == NULL)
135 /* No specific version is selected. When the object
136 file also does not define a version we have a match.
137 Otherwise we only accept the default version, i.e.,
138 the version which name is "". */
139 if (verstab != NULL)
141 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
142 if (ndx > 2) /* map->l_versions[ndx].hash != 0) */
143 continue;
146 else
148 if (verstab == NULL)
150 /* We need a versioned system but haven't found any.
151 If this is the object which is referenced in the
152 verneed entry it is a bug in the library since a
153 symbol must not simply disappear. */
154 if (version->filename != NULL
155 && _dl_name_match_p (version->filename, map))
156 return -2;
157 /* Otherwise we accept the symbol. */
159 else
161 /* We can match the version information or use the
162 default one if it is not hidden. */
163 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
164 if ((map->l_versions[ndx].hash != version->hash
165 || strcmp (map->l_versions[ndx].name, version->name))
166 && (version->hidden || map->l_versions[ndx].hash
167 || (verstab[symidx] & 0x8000)))
168 /* It's not the version we want. */
169 continue;
173 switch (ELFW(ST_BIND) (sym->st_info))
175 case STB_GLOBAL:
176 /* Global definition. Just what we need. */
177 result->s = sym;
178 result->m = map;
179 return 1;
180 case STB_WEAK:
181 /* Weak definition. Use this value if we don't find
182 another. */
183 if (! result->s)
185 result->s = sym;
186 result->m = map;
188 break;
189 default:
190 /* Local symbols are ignored. */
191 break;
194 /* There cannot be another entry for this symbol so stop here. */
195 break;
198 /* If this current map is the one mentioned in the verneed entry
199 and we have not found a weak entry, it is a bug. */
200 if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
201 && _dl_name_match_p (version->filename, map))
202 return -1;
205 /* We have not found anything until now. */
206 return 0;
209 /* Search loaded objects' symbol tables for a definition of the symbol
210 UNDEF_NAME. */
212 ElfW(Addr)
213 internal_function
214 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
215 struct r_scope_elem *symbol_scope[],
216 const char *reference_name,
217 int reloc_type)
219 const unsigned long int hash = _dl_elf_hash (undef_name);
220 struct sym_val current_value = { NULL, NULL };
221 struct r_scope_elem **scope;
223 /* Search the relevant loaded objects for a definition. */
224 for (scope = symbol_scope; *scope; ++scope)
225 if (do_lookup (undef_name, hash, *ref, &current_value,
226 *scope, 0, reference_name, NULL, NULL, reloc_type))
227 break;
229 if (current_value.s == NULL)
231 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
232 /* We could find no value for a strong reference. */
233 _dl_signal_error (0, (reference_name && reference_name[0]
234 ? reference_name
235 : (_dl_argv[0] ?: "<main program>")),
236 make_string (undefined_msg, undef_name));
237 *ref = NULL;
238 return 0;
241 if (_dl_debug_bindings)
242 _dl_debug_message (1, "binding file ",
243 (reference_name && reference_name[0]
244 ? reference_name
245 : (_dl_argv[0] ?: "<main program>")),
246 " to ", current_value.m->l_name[0]
247 ? current_value.m->l_name : _dl_argv[0],
248 ": symbol `", undef_name, "'\n", NULL);
250 *ref = current_value.s;
251 return current_value.m->l_addr;
255 /* This function is nearly the same as `_dl_lookup_symbol' but it
256 skips in the first list all objects until SKIP_MAP is found. I.e.,
257 it only considers objects which were loaded after the described
258 object. If there are more search lists the object described by
259 SKIP_MAP is only skipped. */
260 ElfW(Addr)
261 internal_function
262 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
263 struct r_scope_elem *symbol_scope[],
264 const char *reference_name,
265 struct link_map *skip_map)
267 const unsigned long int hash = _dl_elf_hash (undef_name);
268 struct sym_val current_value = { NULL, NULL };
269 struct r_scope_elem **scope;
270 size_t i;
272 /* Search the relevant loaded objects for a definition. */
273 scope = symbol_scope;
274 for (i = 0; (*scope)->r_duplist[i] != skip_map; ++i)
275 assert (i < (*scope)->r_nduplist);
277 if (! do_lookup (undef_name, hash, *ref, &current_value,
278 *scope, i, reference_name, NULL, skip_map, 0))
279 while (*++scope)
280 if (do_lookup (undef_name, hash, *ref, &current_value,
281 *scope, 0, reference_name, NULL, skip_map, 0))
282 break;
284 if (current_value.s == NULL)
286 *ref = NULL;
287 return 0;
290 if (_dl_debug_bindings)
291 _dl_debug_message (1, "binding file ",
292 (reference_name && reference_name[0]
293 ? reference_name
294 : (_dl_argv[0] ?: "<main program>")),
295 " to ", current_value.m->l_name[0]
296 ? current_value.m->l_name : _dl_argv[0],
297 ": symbol `", undef_name, "' (skip)\n", NULL);
299 *ref = current_value.s;
300 return current_value.m->l_addr;
304 /* This function works like _dl_lookup_symbol but it takes an
305 additional arguement with the version number of the requested
306 symbol.
308 XXX We'll see whether we need this separate function. */
309 ElfW(Addr)
310 internal_function
311 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
312 struct r_scope_elem *symbol_scope[],
313 const char *reference_name,
314 const struct r_found_version *version,
315 int reloc_type)
317 const unsigned long int hash = _dl_elf_hash (undef_name);
318 struct sym_val current_value = { NULL, NULL };
319 struct r_scope_elem **scope;
321 /* Search the relevant loaded objects for a definition. */
322 for (scope = symbol_scope; *scope; ++scope)
324 int res = do_lookup (undef_name, hash, *ref, &current_value,
325 *scope, 0, reference_name, version, NULL, reloc_type);
326 if (res > 0)
327 break;
329 if (res < 0)
330 /* Oh, oh. The file named in the relocation entry does not
331 contain the needed symbol. */
332 _dl_signal_error (0, (reference_name && reference_name[0]
333 ? reference_name
334 : (_dl_argv[0] ?: "<main program>")),
335 make_string ("symbol ", undef_name, ", version ",
336 version->name,
337 " not defined in file ",
338 version->filename,
339 " with link time reference",
340 res == -2
341 ? " (no version symbols)" : ""));
344 if (current_value.s == NULL)
346 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
347 /* We could find no value for a strong reference. */
348 _dl_signal_error (0, (reference_name && reference_name[0]
349 ? reference_name
350 : (_dl_argv[0] ?: "<main program>")),
351 make_string (undefined_msg, undef_name,
352 ", version ", version->name ?: NULL));
353 *ref = NULL;
354 return 0;
357 if (_dl_debug_bindings)
358 _dl_debug_message (1, "binding file ",
359 (reference_name && reference_name[0]
360 ? reference_name
361 : (_dl_argv[0] ?: "<main program>")),
362 " to ", current_value.m->l_name[0]
363 ? current_value.m->l_name : _dl_argv[0],
364 ": symbol `", undef_name, "' [", version->name,
365 "]\n", NULL);
367 *ref = current_value.s;
368 return current_value.m->l_addr;
372 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
373 with the version we are looking for. */
374 ElfW(Addr)
375 internal_function
376 _dl_lookup_versioned_symbol_skip (const char *undef_name,
377 const ElfW(Sym) **ref,
378 struct r_scope_elem *symbol_scope[],
379 const char *reference_name,
380 const struct r_found_version *version,
381 struct link_map *skip_map)
383 const unsigned long int hash = _dl_elf_hash (undef_name);
384 struct sym_val current_value = { NULL, NULL };
385 struct r_scope_elem **scope;
386 size_t i;
388 /* Search the relevant loaded objects for a definition. */
389 scope = symbol_scope;
390 for (i = 0; (*scope)->r_duplist[i] != skip_map; ++i)
391 assert (i < (*scope)->r_nduplist);
393 if (! do_lookup (undef_name, hash, *ref, &current_value,
394 *scope, i, reference_name, version, skip_map, 0))
395 while (*++scope)
396 if (do_lookup (undef_name, hash, *ref, &current_value,
397 *scope, 0, reference_name, version, skip_map, 0))
398 break;
400 if (current_value.s == NULL)
402 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
404 /* We could find no value for a strong reference. */
405 const size_t len = strlen (undef_name);
406 char buf[sizeof undefined_msg + len];
407 __mempcpy (__mempcpy (buf, undefined_msg, sizeof undefined_msg - 1),
408 undef_name, len + 1);
409 _dl_signal_error (0, (reference_name && reference_name[0]
410 ? reference_name
411 : (_dl_argv[0] ?: "<main program>")), buf);
413 *ref = NULL;
414 return 0;
417 if (_dl_debug_bindings)
418 _dl_debug_message (1, "binding file ",
419 (reference_name && reference_name[0]
420 ? reference_name
421 : (_dl_argv[0] ?: "<main program>")),
422 " to ",
423 current_value.m->l_name[0]
424 ? current_value.m->l_name : _dl_argv[0],
425 ": symbol `", undef_name, "' [", version->name,
426 "] (skip)\n", NULL);
428 *ref = current_value.s;
429 return current_value.m->l_addr;
433 /* Cache the location of MAP's hash table. */
435 void
436 internal_function
437 _dl_setup_hash (struct link_map *map)
439 ElfW(Symndx) *hash;
440 ElfW(Symndx) nchain;
442 if (!map->l_info[DT_HASH])
443 return;
444 hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
446 map->l_nbuckets = *hash++;
447 nchain = *hash++;
448 map->l_buckets = hash;
449 hash += map->l_nbuckets;
450 map->l_chain = hash;