Update.
[glibc.git] / elf / dl-lookup.c
blob16173c9068e91f8aed05c9539413203e8433f1f6
1 /* Look up a symbol in the loaded objects.
2 Copyright (C) 1995, 1996, 1997, 1998, 1999 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; \
59 /* Statistics function. */
60 unsigned long int _dl_num_relocations;
63 /* We have two different situations when looking up a simple: with or
64 without versioning. gcc is not able to optimize a single function
65 definition serving for both purposes so we define two functions. */
66 #define VERSIONED 0
67 #include "do-lookup.h"
69 #define VERSIONED 1
70 #include "do-lookup.h"
73 /* Search loaded objects' symbol tables for a definition of the symbol
74 UNDEF_NAME. */
76 ElfW(Addr)
77 internal_function
78 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
79 struct r_scope_elem *symbol_scope[],
80 const char *reference_name,
81 int reloc_type)
83 const unsigned long int hash = _dl_elf_hash (undef_name);
84 struct sym_val current_value = { NULL, NULL };
85 struct r_scope_elem **scope;
87 ++_dl_num_relocations;
89 /* Search the relevant loaded objects for a definition. */
90 for (scope = symbol_scope; *scope; ++scope)
91 if (do_lookup (undef_name, hash, *ref, &current_value,
92 *scope, 0, reference_name, NULL, reloc_type))
93 break;
95 if (current_value.s == NULL)
97 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
98 /* We could find no value for a strong reference. */
99 _dl_signal_cerror (0, (reference_name && reference_name[0]
100 ? reference_name
101 : (_dl_argv[0] ?: "<main program>")),
102 make_string (undefined_msg, undef_name));
103 *ref = NULL;
104 return 0;
107 if (_dl_debug_bindings)
108 _dl_debug_message (1, "binding file ",
109 (reference_name && reference_name[0]
110 ? reference_name
111 : (_dl_argv[0] ?: "<main program>")),
112 " to ", current_value.m->l_name[0]
113 ? current_value.m->l_name : _dl_argv[0],
114 ": symbol `", undef_name, "'\n", NULL);
116 *ref = current_value.s;
117 return current_value.m->l_addr;
121 /* This function is nearly the same as `_dl_lookup_symbol' but it
122 skips in the first list all objects until SKIP_MAP is found. I.e.,
123 it only considers objects which were loaded after the described
124 object. If there are more search lists the object described by
125 SKIP_MAP is only skipped. */
126 ElfW(Addr)
127 internal_function
128 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
129 struct r_scope_elem *symbol_scope[],
130 const char *reference_name,
131 struct link_map *skip_map)
133 const unsigned long int hash = _dl_elf_hash (undef_name);
134 struct sym_val current_value = { NULL, NULL };
135 struct r_scope_elem **scope;
136 size_t i;
138 ++_dl_num_relocations;
140 /* Search the relevant loaded objects for a definition. */
141 scope = symbol_scope;
142 for (i = 0; (*scope)->r_duplist[i] != skip_map; ++i)
143 assert (i < (*scope)->r_nduplist);
145 if (i >= (*scope)->r_nlist
146 || ! do_lookup (undef_name, hash, *ref, &current_value,
147 *scope, i, reference_name, skip_map, 0))
148 while (*++scope)
149 if (do_lookup (undef_name, hash, *ref, &current_value,
150 *scope, 0, reference_name, skip_map, 0))
151 break;
153 if (current_value.s == NULL)
155 *ref = NULL;
156 return 0;
159 if (_dl_debug_bindings)
160 _dl_debug_message (1, "binding file ",
161 (reference_name && reference_name[0]
162 ? reference_name
163 : (_dl_argv[0] ?: "<main program>")),
164 " to ", current_value.m->l_name[0]
165 ? current_value.m->l_name : _dl_argv[0],
166 ": symbol `", undef_name, "' (skip)\n", NULL);
168 *ref = current_value.s;
169 return current_value.m->l_addr;
173 /* This function works like _dl_lookup_symbol but it takes an
174 additional arguement with the version number of the requested
175 symbol.
177 XXX We'll see whether we need this separate function. */
178 ElfW(Addr)
179 internal_function
180 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
181 struct r_scope_elem *symbol_scope[],
182 const char *reference_name,
183 const struct r_found_version *version,
184 int reloc_type)
186 const unsigned long int hash = _dl_elf_hash (undef_name);
187 struct sym_val current_value = { NULL, NULL };
188 struct r_scope_elem **scope;
190 ++_dl_num_relocations;
192 /* Search the relevant loaded objects for a definition. */
193 for (scope = symbol_scope; *scope; ++scope)
195 int res = do_lookup_versioned (undef_name, hash, *ref, &current_value,
196 *scope, 0, reference_name, version, NULL,
197 reloc_type);
198 if (res > 0)
199 break;
201 if (res < 0)
203 /* Oh, oh. The file named in the relocation entry does not
204 contain the needed symbol. */
205 _dl_signal_cerror (0, (reference_name && reference_name[0]
206 ? reference_name
207 : (_dl_argv[0] ?: "<main program>")),
208 make_string ("symbol ", undef_name, ", version ",
209 version->name,
210 " not defined in file ",
211 version->filename,
212 " with link time reference",
213 res == -2
214 ? " (no version symbols)" : ""));
215 *ref = NULL;
216 return 0;
220 if (current_value.s == NULL)
222 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
223 /* We could find no value for a strong reference. */
224 _dl_signal_cerror (0, (reference_name && reference_name[0]
225 ? reference_name
226 : (_dl_argv[0] ?: "<main program>")),
227 make_string (undefined_msg, undef_name,
228 ", version ", version->name ?: NULL));
229 *ref = NULL;
230 return 0;
233 if (_dl_debug_bindings)
234 _dl_debug_message (1, "binding file ",
235 (reference_name && reference_name[0]
236 ? reference_name
237 : (_dl_argv[0] ?: "<main program>")),
238 " to ", current_value.m->l_name[0]
239 ? current_value.m->l_name : _dl_argv[0],
240 ": symbol `", undef_name, "' [", version->name,
241 "]\n", NULL);
243 *ref = current_value.s;
244 return current_value.m->l_addr;
248 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
249 with the version we are looking for. */
250 ElfW(Addr)
251 internal_function
252 _dl_lookup_versioned_symbol_skip (const char *undef_name,
253 const ElfW(Sym) **ref,
254 struct r_scope_elem *symbol_scope[],
255 const char *reference_name,
256 const struct r_found_version *version,
257 struct link_map *skip_map)
259 const unsigned long int hash = _dl_elf_hash (undef_name);
260 struct sym_val current_value = { NULL, NULL };
261 struct r_scope_elem **scope;
262 size_t i;
264 ++_dl_num_relocations;
266 /* Search the relevant loaded objects for a definition. */
267 scope = symbol_scope;
268 for (i = 0; (*scope)->r_duplist[i] != skip_map; ++i)
269 assert (i < (*scope)->r_nduplist);
271 if (i >= (*scope)->r_nlist
272 || ! do_lookup_versioned (undef_name, hash, *ref, &current_value, *scope,
273 i, reference_name, version, skip_map, 0))
274 while (*++scope)
275 if (do_lookup_versioned (undef_name, hash, *ref, &current_value, *scope,
276 0, reference_name, version, skip_map, 0))
277 break;
279 if (current_value.s == NULL)
281 if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
283 /* We could find no value for a strong reference. */
284 const size_t len = strlen (undef_name);
285 char buf[sizeof undefined_msg + len];
286 __mempcpy (__mempcpy (buf, undefined_msg, sizeof undefined_msg - 1),
287 undef_name, len + 1);
288 _dl_signal_cerror (0, (reference_name && reference_name[0]
289 ? reference_name
290 : (_dl_argv[0] ?: "<main program>")), buf);
292 *ref = NULL;
293 return 0;
296 if (_dl_debug_bindings)
297 _dl_debug_message (1, "binding file ",
298 (reference_name && reference_name[0]
299 ? reference_name
300 : (_dl_argv[0] ?: "<main program>")),
301 " to ",
302 current_value.m->l_name[0]
303 ? current_value.m->l_name : _dl_argv[0],
304 ": symbol `", undef_name, "' [", version->name,
305 "] (skip)\n", NULL);
307 *ref = current_value.s;
308 return current_value.m->l_addr;
312 /* Cache the location of MAP's hash table. */
314 void
315 internal_function
316 _dl_setup_hash (struct link_map *map)
318 ElfW(Symndx) *hash;
319 ElfW(Symndx) nchain;
321 if (!map->l_info[DT_HASH])
322 return;
323 hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
325 map->l_nbuckets = *hash++;
326 nchain = *hash++;
327 map->l_buckets = hash;
328 hash += map->l_nbuckets;
329 map->l_chain = hash;