]>
Commit | Line | Data |
---|---|---|
d66e34cd | 1 | /* Look up a symbol in the loaded objects. |
da832465 | 2 | Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. |
afd4eb37 | 3 | This file is part of the GNU C Library. |
d66e34cd | 4 | |
afd4eb37 UD |
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. | |
d66e34cd | 9 | |
afd4eb37 UD |
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. | |
d66e34cd | 14 | |
afd4eb37 UD |
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. */ | |
d66e34cd | 19 | |
f671aeab | 20 | #include <alloca.h> |
d66e34cd | 21 | #include <link.h> |
a1a9d215 | 22 | #include <assert.h> |
8d6468d0 | 23 | #include <string.h> |
3db52d94 | 24 | #include <unistd.h> |
d66e34cd | 25 | |
bc9f6000 UD |
26 | #include "dl-hash.h" |
27 | #include <dl-machine.h> | |
c84142e8 UD |
28 | |
29 | #define VERSTAG(tag) (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag)) | |
266180eb | 30 | |
714a562f UD |
31 | /* We need this string more than once. */ |
32 | static const char undefined_msg[] = "undefined symbol: "; | |
33 | ||
34 | ||
84384f5b UD |
35 | struct sym_val |
36 | { | |
84384f5b | 37 | const ElfW(Sym) *s; |
0c367d92 | 38 | struct link_map *m; |
84384f5b UD |
39 | }; |
40 | ||
41 | ||
1fb05e3d UD |
42 | #define make_string(string, rest...) \ |
43 | ({ \ | |
44 | const char *all[] = { string, ## rest }; \ | |
45 | size_t len, cnt; \ | |
46 | char *result, *cp; \ | |
47 | \ | |
48 | len = 1; \ | |
49 | for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ | |
50 | len += strlen (all[cnt]); \ | |
51 | \ | |
52 | cp = result = alloca (len); \ | |
53 | for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt) \ | |
da832465 | 54 | cp = __stpcpy (cp, all[cnt]); \ |
1fb05e3d UD |
55 | \ |
56 | result; \ | |
57 | }) | |
58 | ||
59 | ||
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 | |
84384f5b | 64 | do_lookup (const char *undef_name, unsigned long int hash, |
bc9f6000 | 65 | const ElfW(Sym) *ref, struct sym_val *result, |
de100ca7 UD |
66 | struct link_map *scope, size_t i, const char *reference_name, |
67 | const struct r_found_version *version, struct link_map *skip, | |
68 | int reloc_type) | |
84384f5b | 69 | { |
de100ca7 UD |
70 | struct link_map **list = scope->l_searchlist; |
71 | size_t n = scope->l_nsearchlist; | |
84384f5b UD |
72 | struct link_map *map; |
73 | ||
74 | for (; i < n; ++i) | |
75 | { | |
76 | const ElfW(Sym) *symtab; | |
77 | const char *strtab; | |
c84142e8 | 78 | const ElfW(Half) *verstab; |
84384f5b UD |
79 | ElfW(Symndx) symidx; |
80 | ||
81 | map = list[i]; | |
82 | ||
83 | /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */ | |
84 | if (skip != NULL && map == skip) | |
85 | continue; | |
86 | ||
5107cf1d UD |
87 | /* Skip objects that could not be opened, which can occur in trace |
88 | mode. */ | |
89 | if (map->l_opencount == 0) | |
90 | continue; | |
91 | ||
84384f5b | 92 | /* Don't search the executable when resolving a copy reloc. */ |
0c367d92 UD |
93 | if (elf_machine_lookup_noexec_p (reloc_type) |
94 | && map->l_type == lt_executable) | |
84384f5b UD |
95 | continue; |
96 | ||
f41c8091 UD |
97 | /* Skip objects without symbol tables. */ |
98 | if (map->l_info[DT_SYMTAB] == NULL) | |
99 | continue; | |
100 | ||
de100ca7 UD |
101 | /* Print some debugging info if wanted. */ |
102 | if (_dl_debug_symbols) | |
8193034b | 103 | _dl_debug_message (1, "symbol=", undef_name, "; lookup in file=", |
de100ca7 UD |
104 | map->l_name[0] ? map->l_name : _dl_argv[0], |
105 | "\n", NULL); | |
106 | ||
84384f5b UD |
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); | |
0c367d92 | 109 | verstab = map->l_versyms; |
84384f5b UD |
110 | |
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]) | |
116 | { | |
117 | const ElfW(Sym) *sym = &symtab[symidx]; | |
118 | ||
119 | if (sym->st_value == 0 || /* No value. */ | |
bc9f6000 | 120 | (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry. */ |
84384f5b UD |
121 | && sym->st_shndx == SHN_UNDEF)) |
122 | continue; | |
123 | ||
714a562f UD |
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; | |
84384f5b | 128 | |
bc9f6000 | 129 | if (sym != ref && strcmp (strtab + sym->st_name, undef_name)) |
84384f5b UD |
130 | /* Not the symbol we are looking for. */ |
131 | continue; | |
132 | ||
1fb05e3d | 133 | if (version == NULL) |
c84142e8 | 134 | { |
1fb05e3d UD |
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) | |
140 | { | |
141 | ElfW(Half) ndx = verstab[symidx] & 0x7fff; | |
714a562f | 142 | if (ndx > 2) /* map->l_versions[ndx].hash != 0) */ |
1fb05e3d UD |
143 | continue; |
144 | } | |
145 | } | |
146 | else | |
147 | { | |
148 | if (verstab == NULL) | |
149 | { | |
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)) | |
714a562f | 156 | return -2; |
1fb05e3d UD |
157 | /* Otherwise we accept the symbol. */ |
158 | } | |
159 | else | |
160 | { | |
f9a73ae1 | 161 | /* We can match the version information or use the |
6973fc01 | 162 | default one if it is not hidden. */ |
1fb05e3d | 163 | ElfW(Half) ndx = verstab[symidx] & 0x7fff; |
f9a73ae1 UD |
164 | if ((map->l_versions[ndx].hash != version->hash |
165 | || strcmp (map->l_versions[ndx].name, version->name)) | |
6973fc01 UD |
166 | && (version->hidden || map->l_versions[ndx].hash |
167 | || (verstab[symidx] & 0x8000))) | |
1fb05e3d UD |
168 | /* It's not the version we want. */ |
169 | continue; | |
170 | } | |
c84142e8 UD |
171 | } |
172 | ||
84384f5b UD |
173 | switch (ELFW(ST_BIND) (sym->st_info)) |
174 | { | |
175 | case STB_GLOBAL: | |
176 | /* Global definition. Just what we need. */ | |
177 | result->s = sym; | |
0c367d92 | 178 | result->m = map; |
84384f5b UD |
179 | return 1; |
180 | case STB_WEAK: | |
181 | /* Weak definition. Use this value if we don't find | |
182 | another. */ | |
183 | if (! result->s) | |
184 | { | |
185 | result->s = sym; | |
0c367d92 | 186 | result->m = map; |
84384f5b UD |
187 | } |
188 | break; | |
189 | default: | |
190 | /* Local symbols are ignored. */ | |
191 | break; | |
192 | } | |
714a562f UD |
193 | |
194 | /* There cannot be another entry for this symbol so stop here. */ | |
195 | break; | |
84384f5b | 196 | } |
1fb05e3d | 197 | |
714a562f UD |
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 | |
1fb05e3d UD |
201 | && _dl_name_match_p (version->filename, map)) |
202 | return -1; | |
84384f5b UD |
203 | } |
204 | ||
205 | /* We have not found anything until now. */ | |
206 | return 0; | |
207 | } | |
208 | ||
6c03c2cf | 209 | /* Search loaded objects' symbol tables for a definition of the symbol |
bc9f6000 | 210 | UNDEF_NAME. */ |
d66e34cd | 211 | |
266180eb RM |
212 | ElfW(Addr) |
213 | _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref, | |
ba79d61b | 214 | struct link_map *symbol_scope[], |
f2b0f935 | 215 | const char *reference_name, |
bc9f6000 | 216 | int reloc_type) |
d66e34cd | 217 | { |
266180eb | 218 | const unsigned long int hash = _dl_elf_hash (undef_name); |
0c367d92 | 219 | struct sym_val current_value = { NULL, NULL }; |
84384f5b | 220 | struct link_map **scope; |
d66e34cd RM |
221 | |
222 | /* Search the relevant loaded objects for a definition. */ | |
ba79d61b | 223 | for (scope = symbol_scope; *scope; ++scope) |
bc9f6000 | 224 | if (do_lookup (undef_name, hash, *ref, ¤t_value, |
de100ca7 | 225 | *scope, 0, reference_name, NULL, NULL, reloc_type)) |
84384f5b | 226 | break; |
ba79d61b | 227 | |
0c367d92 UD |
228 | if (current_value.s == NULL) |
229 | { | |
230 | if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK) | |
231 | /* We could find no value for a strong reference. */ | |
232 | _dl_signal_error (0, reference_name, | |
233 | make_string (undefined_msg, undef_name)); | |
234 | *ref = NULL; | |
235 | return 0; | |
236 | } | |
237 | ||
238 | if (_dl_debug_bindings) | |
8193034b | 239 | _dl_debug_message (1, "binding file ", reference_name, " to ", |
0c367d92 UD |
240 | current_value.m->l_name[0] |
241 | ? current_value.m->l_name : _dl_argv[0], | |
242 | ": symbol `", undef_name, "'\n", NULL); | |
d66e34cd | 243 | |
84384f5b | 244 | *ref = current_value.s; |
0c367d92 | 245 | return current_value.m->l_addr; |
84384f5b UD |
246 | } |
247 | ||
248 | ||
249 | /* This function is nearly the same as `_dl_lookup_symbol' but it | |
250 | skips in the first list all objects until SKIP_MAP is found. I.e., | |
251 | it only considers objects which were loaded after the described | |
252 | object. If there are more search lists the object described by | |
253 | SKIP_MAP is only skipped. */ | |
254 | ElfW(Addr) | |
255 | _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref, | |
256 | struct link_map *symbol_scope[], | |
257 | const char *reference_name, | |
bc9f6000 | 258 | struct link_map *skip_map) |
84384f5b | 259 | { |
84384f5b | 260 | const unsigned long int hash = _dl_elf_hash (undef_name); |
0c367d92 | 261 | struct sym_val current_value = { NULL, NULL }; |
84384f5b UD |
262 | struct link_map **scope; |
263 | size_t i; | |
264 | ||
265 | /* Search the relevant loaded objects for a definition. */ | |
266 | scope = symbol_scope; | |
267 | for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i) | |
268 | assert (i < (*scope)->l_ndupsearchlist); | |
269 | ||
bc9f6000 | 270 | if (! do_lookup (undef_name, hash, *ref, ¤t_value, |
de100ca7 | 271 | *scope, i, reference_name, NULL, skip_map, 0)) |
c84142e8 | 272 | while (*++scope) |
bc9f6000 | 273 | if (do_lookup (undef_name, hash, *ref, ¤t_value, |
de100ca7 | 274 | *scope, 0, reference_name, NULL, skip_map, 0)) |
c84142e8 UD |
275 | break; |
276 | ||
0c367d92 UD |
277 | if (current_value.s == NULL) |
278 | { | |
279 | *ref = NULL; | |
280 | return 0; | |
281 | } | |
282 | ||
283 | if (_dl_debug_bindings) | |
8193034b | 284 | _dl_debug_message (1, "binding file ", reference_name, " to ", |
0c367d92 UD |
285 | current_value.m->l_name[0] |
286 | ? current_value.m->l_name : _dl_argv[0], | |
287 | ": symbol `", undef_name, "'\n", NULL); | |
288 | ||
c84142e8 | 289 | *ref = current_value.s; |
0c367d92 | 290 | return current_value.m->l_addr; |
c84142e8 UD |
291 | } |
292 | ||
293 | ||
294 | /* This function works like _dl_lookup_symbol but it takes an | |
295 | additional arguement with the version number of the requested | |
296 | symbol. | |
297 | ||
298 | XXX We'll see whether we need this separate function. */ | |
299 | ElfW(Addr) | |
300 | _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref, | |
301 | struct link_map *symbol_scope[], | |
302 | const char *reference_name, | |
bc9f6000 UD |
303 | const struct r_found_version *version, |
304 | int reloc_type) | |
c84142e8 UD |
305 | { |
306 | const unsigned long int hash = _dl_elf_hash (undef_name); | |
0c367d92 | 307 | struct sym_val current_value = { NULL, NULL }; |
c84142e8 UD |
308 | struct link_map **scope; |
309 | ||
310 | /* Search the relevant loaded objects for a definition. */ | |
311 | for (scope = symbol_scope; *scope; ++scope) | |
1fb05e3d | 312 | { |
bc9f6000 | 313 | int res = do_lookup (undef_name, hash, *ref, ¤t_value, |
de100ca7 | 314 | *scope, 0, reference_name, version, NULL, reloc_type); |
1fb05e3d UD |
315 | if (res > 0) |
316 | break; | |
317 | ||
318 | if (res < 0) | |
319 | /* Oh, oh. The file named in the relocation entry does not | |
320 | contain the needed symbol. */ | |
1618c590 UD |
321 | _dl_signal_error (0, (*reference_name |
322 | ? reference_name | |
323 | : (_dl_argv[0] ?: "<main program>")), | |
1fb05e3d UD |
324 | make_string ("symbol ", undef_name, ", version ", |
325 | version->name, | |
326 | " not defined in file ", | |
327 | version->filename, | |
714a562f UD |
328 | " with link time reference", |
329 | res == -2 | |
330 | ? " (no version symbols)" : "")); | |
1fb05e3d | 331 | } |
c84142e8 | 332 | |
0c367d92 UD |
333 | if (current_value.s == NULL) |
334 | { | |
335 | if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK) | |
336 | /* We could find no value for a strong reference. */ | |
337 | _dl_signal_error (0, reference_name, | |
338 | make_string (undefined_msg, undef_name, | |
339 | ", version ", version->name ?: NULL)); | |
340 | *ref = NULL; | |
341 | return 0; | |
342 | } | |
343 | ||
344 | if (_dl_debug_bindings) | |
8193034b | 345 | _dl_debug_message (1, "binding file ", reference_name, " to ", |
0c367d92 UD |
346 | current_value.m->l_name[0] |
347 | ? current_value.m->l_name : _dl_argv[0], | |
db0b91a9 UD |
348 | ": symbol `", undef_name, "' [", version->name, |
349 | "]\n", NULL); | |
c84142e8 UD |
350 | |
351 | *ref = current_value.s; | |
0c367d92 | 352 | return current_value.m->l_addr; |
c84142e8 UD |
353 | } |
354 | ||
355 | ||
356 | /* Similar to _dl_lookup_symbol_skip but takes an additional argument | |
357 | with the version we are looking for. */ | |
358 | ElfW(Addr) | |
359 | _dl_lookup_versioned_symbol_skip (const char *undef_name, | |
360 | const ElfW(Sym) **ref, | |
361 | struct link_map *symbol_scope[], | |
362 | const char *reference_name, | |
1fb05e3d | 363 | const struct r_found_version *version, |
bc9f6000 | 364 | struct link_map *skip_map) |
c84142e8 UD |
365 | { |
366 | const unsigned long int hash = _dl_elf_hash (undef_name); | |
0c367d92 | 367 | struct sym_val current_value = { NULL, NULL }; |
c84142e8 | 368 | struct link_map **scope; |
c84142e8 UD |
369 | size_t i; |
370 | ||
c84142e8 UD |
371 | /* Search the relevant loaded objects for a definition. */ |
372 | scope = symbol_scope; | |
373 | for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i) | |
374 | assert (i < (*scope)->l_ndupsearchlist); | |
375 | ||
bc9f6000 | 376 | if (! do_lookup (undef_name, hash, *ref, ¤t_value, |
de100ca7 | 377 | *scope, i, reference_name, version, skip_map, 0)) |
84384f5b | 378 | while (*++scope) |
bc9f6000 | 379 | if (do_lookup (undef_name, hash, *ref, ¤t_value, |
de100ca7 | 380 | *scope, 0, reference_name, version, skip_map, 0)) |
84384f5b UD |
381 | break; |
382 | ||
0c367d92 | 383 | if (current_value.s == NULL) |
1f205a47 | 384 | { |
0c367d92 UD |
385 | if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK) |
386 | { | |
387 | /* We could find no value for a strong reference. */ | |
388 | const size_t len = strlen (undef_name); | |
389 | char buf[sizeof undefined_msg + len]; | |
390 | __mempcpy (__mempcpy (buf, undefined_msg, sizeof undefined_msg - 1), | |
391 | undef_name, len + 1); | |
392 | _dl_signal_error (0, reference_name, buf); | |
393 | } | |
394 | *ref = NULL; | |
395 | return 0; | |
1f205a47 UD |
396 | } |
397 | ||
0c367d92 | 398 | if (_dl_debug_bindings) |
8193034b | 399 | _dl_debug_message (1, "binding file ", reference_name, " to ", |
0c367d92 UD |
400 | current_value.m->l_name[0] |
401 | ? current_value.m->l_name : _dl_argv[0], | |
db0b91a9 UD |
402 | ": symbol `", undef_name, "' [", version->name, |
403 | "]\n", NULL); | |
0c367d92 | 404 | |
84384f5b | 405 | *ref = current_value.s; |
0c367d92 | 406 | return current_value.m->l_addr; |
d66e34cd RM |
407 | } |
408 | ||
409 | ||
410 | /* Cache the location of MAP's hash table. */ | |
411 | ||
412 | void | |
413 | _dl_setup_hash (struct link_map *map) | |
414 | { | |
f41c8091 | 415 | ElfW(Symndx) *hash; |
8d6468d0 | 416 | ElfW(Symndx) nchain; |
f41c8091 UD |
417 | |
418 | if (!map->l_info[DT_HASH]) | |
419 | return; | |
420 | hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr); | |
421 | ||
d66e34cd RM |
422 | map->l_nbuckets = *hash++; |
423 | nchain = *hash++; | |
424 | map->l_buckets = hash; | |
425 | hash += map->l_nbuckets; | |
426 | map->l_chain = hash; | |
427 | } |