(compare_entry): Partially undo patch made by Klaus Espenlaub. There
[glibc/history.git] / elf / dl-lookup.c
blob4cc1f2d468d9a42871bf02a4ba242799f2ba68e1
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 /* Skip objects that could not be opened, which can occur in trace
87 mode. */
88 if (map->l_opencount == 0)
89 continue;
91 /* Don't search the executable when resolving a copy reloc. */
92 if (elf_machine_lookup_noexec_p (reloc_type) &&
93 map->l_type == lt_executable)
94 continue;
96 symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
97 strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
98 if (map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL)
99 verstab = ((void *) map->l_addr
100 + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr);
101 else
102 verstab = NULL;
104 /* Search the appropriate hash bucket in this object's symbol table
105 for a definition for the same symbol name. */
106 for (symidx = map->l_buckets[hash % map->l_nbuckets];
107 symidx != STN_UNDEF;
108 symidx = map->l_chain[symidx])
110 const ElfW(Sym) *sym = &symtab[symidx];
112 if (sym->st_value == 0 || /* No value. */
113 (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry. */
114 && sym->st_shndx == SHN_UNDEF))
115 continue;
117 if (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC)
118 /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC entries
119 since these are no code/data definitions. */
120 continue;
122 if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
123 /* Not the symbol we are looking for. */
124 continue;
126 if (version == NULL)
128 /* No specific version is selected. When the object
129 file also does not define a version we have a match.
130 Otherwise we only accept the default version, i.e.,
131 the version which name is "". */
132 if (verstab != NULL)
134 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
135 if (ndx > 2) /* map->l_versions[ndx].hash != 0) */
136 continue;
139 else
141 if (verstab == NULL)
143 /* We need a versioned system but haven't found any.
144 If this is the object which is referenced in the
145 verneed entry it is a bug in the library since a
146 symbol must not simply disappear. */
147 if (version->filename != NULL
148 && _dl_name_match_p (version->filename, map))
149 return -2;
150 /* Otherwise we accept the symbol. */
152 else
154 /* We can match the version information or use the
155 default one. */
156 ElfW(Half) ndx = verstab[symidx] & 0x7fff;
157 if ((map->l_versions[ndx].hash != version->hash
158 || strcmp (map->l_versions[ndx].name, version->name))
159 && (version->hidden || map->l_versions[ndx].hash))
160 /* It's not the version we want. */
161 continue;
165 switch (ELFW(ST_BIND) (sym->st_info))
167 case STB_GLOBAL:
168 /* Global definition. Just what we need. */
169 result->s = sym;
170 result->a = map->l_addr;
171 return 1;
172 case STB_WEAK:
173 /* Weak definition. Use this value if we don't find
174 another. */
175 if (! result->s)
177 result->s = sym;
178 result->a = map->l_addr;
180 break;
181 default:
182 /* Local symbols are ignored. */
183 break;
186 /* There cannot be another entry for this symbol so stop here. */
187 break;
190 /* If this current map is the one mentioned in the verneed entry
191 and we have not found a weak entry, it is a bug. */
192 if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
193 && _dl_name_match_p (version->filename, map))
194 return -1;
197 /* We have not found anything until now. */
198 return 0;
201 /* Search loaded objects' symbol tables for a definition of the symbol
202 UNDEF_NAME. */
204 ElfW(Addr)
205 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
206 struct link_map *symbol_scope[],
207 const char *reference_name,
208 int reloc_type)
210 const unsigned long int hash = _dl_elf_hash (undef_name);
211 struct sym_val current_value = { 0, NULL };
212 struct link_map **scope;
214 /* Search the relevant loaded objects for a definition. */
215 for (scope = symbol_scope; *scope; ++scope)
216 if (do_lookup (undef_name, hash, *ref, &current_value,
217 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
218 reference_name, NULL, NULL, reloc_type))
219 break;
221 if (current_value.s == NULL &&
222 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
223 /* We could find no value for a strong reference. */
224 _dl_signal_error (0, reference_name,
225 make_string (undefined_msg, undef_name));
227 *ref = current_value.s;
228 return current_value.a;
232 /* This function is nearly the same as `_dl_lookup_symbol' but it
233 skips in the first list all objects until SKIP_MAP is found. I.e.,
234 it only considers objects which were loaded after the described
235 object. If there are more search lists the object described by
236 SKIP_MAP is only skipped. */
237 ElfW(Addr)
238 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
239 struct link_map *symbol_scope[],
240 const char *reference_name,
241 struct link_map *skip_map)
243 const unsigned long int hash = _dl_elf_hash (undef_name);
244 struct sym_val current_value = { 0, NULL };
245 struct link_map **scope;
246 size_t i;
248 /* Search the relevant loaded objects for a definition. */
249 scope = symbol_scope;
250 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
251 assert (i < (*scope)->l_ndupsearchlist);
253 if (! do_lookup (undef_name, hash, *ref, &current_value,
254 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
255 reference_name, NULL, skip_map, 0))
256 while (*++scope)
257 if (do_lookup (undef_name, hash, *ref, &current_value,
258 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
259 reference_name, NULL, skip_map, 0))
260 break;
262 *ref = current_value.s;
263 return current_value.a;
267 /* This function works like _dl_lookup_symbol but it takes an
268 additional arguement with the version number of the requested
269 symbol.
271 XXX We'll see whether we need this separate function. */
272 ElfW(Addr)
273 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
274 struct link_map *symbol_scope[],
275 const char *reference_name,
276 const struct r_found_version *version,
277 int reloc_type)
279 const unsigned long int hash = _dl_elf_hash (undef_name);
280 struct sym_val current_value = { 0, NULL };
281 struct link_map **scope;
283 /* Search the relevant loaded objects for a definition. */
284 for (scope = symbol_scope; *scope; ++scope)
286 int res = do_lookup (undef_name, hash, *ref, &current_value,
287 (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
288 reference_name, version, NULL, reloc_type);
289 if (res > 0)
290 break;
292 if (res < 0)
293 /* Oh, oh. The file named in the relocation entry does not
294 contain the needed symbol. */
295 _dl_signal_error (0, reference_name,
296 make_string ("symbol ", undef_name, ", version ",
297 version->name,
298 " not defined in file ",
299 version->filename,
300 " with link time reference",
301 res == -2
302 ? " (no version symbols)" : ""));
305 if (current_value.s == NULL &&
306 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
307 /* We could find no value for a strong reference. */
308 _dl_signal_error (0, reference_name,
309 make_string (undefined_msg, undef_name,
310 ", version ", version->name ?: NULL));
312 *ref = current_value.s;
313 return current_value.a;
317 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
318 with the version we are looking for. */
319 ElfW(Addr)
320 _dl_lookup_versioned_symbol_skip (const char *undef_name,
321 const ElfW(Sym) **ref,
322 struct link_map *symbol_scope[],
323 const char *reference_name,
324 const struct r_found_version *version,
325 struct link_map *skip_map)
327 const unsigned long int hash = _dl_elf_hash (undef_name);
328 struct sym_val current_value = { 0, NULL };
329 struct link_map **scope;
330 size_t i;
332 /* Search the relevant loaded objects for a definition. */
333 scope = symbol_scope;
334 for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
335 assert (i < (*scope)->l_ndupsearchlist);
337 if (! do_lookup (undef_name, hash, *ref, &current_value,
338 (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
339 reference_name, version, skip_map, 0))
340 while (*++scope)
341 if (do_lookup (undef_name, hash, *ref, &current_value,
342 (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
343 reference_name, version, skip_map, 0))
344 break;
346 if (current_value.s == NULL &&
347 (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
349 /* We could find no value for a strong reference. */
350 const size_t len = strlen (undef_name);
351 char buf[sizeof undefined_msg + len];
352 memcpy (buf, undefined_msg, sizeof undefined_msg - 1);
353 memcpy (&buf[sizeof undefined_msg - 1], undef_name, len + 1);
354 _dl_signal_error (0, reference_name, buf);
357 *ref = current_value.s;
358 return current_value.a;
362 /* Cache the location of MAP's hash table. */
364 void
365 _dl_setup_hash (struct link_map *map)
367 ElfW(Symndx) *hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
368 ElfW(Symndx) nchain;
369 map->l_nbuckets = *hash++;
370 nchain = *hash++;
371 map->l_buckets = hash;
372 hash += map->l_nbuckets;
373 map->l_chain = hash;