fgetws implementation.
[kopensolaris-gnu/glibc.git] / elf / dl-lookup.c
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.
4
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.
9
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.
14
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.  */
19
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>
26
27 #include <assert.h>
28
29 #define VERSTAG(tag)    (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
30
31 /* We need this string more than once.  */
32 static const char undefined_msg[] = "undefined symbol: ";
33
34
35 struct sym_val
36   {
37     const ElfW(Sym) *s;
38     struct link_map *m;
39   };
40
41
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)                \
54       cp = __stpcpy (cp, all[cnt]);                                           \
55                                                                               \
56     result;                                                                   \
57   })
58
59 /* Statistics function.  */
60 unsigned long int _dl_num_relocations;
61
62
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"
68
69 #define VERSIONED       1
70 #include "do-lookup.h"
71
72
73 /* Search loaded objects' symbol tables for a definition of the symbol
74    UNDEF_NAME.  */
75
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)
82 {
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;
86
87   ++_dl_num_relocations;
88
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;
94
95   if (current_value.s == NULL)
96     {
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;
105     }
106
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);
115
116   *ref = current_value.s;
117   return current_value.m->l_addr;
118 }
119
120
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)
132 {
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;
137
138   ++_dl_num_relocations;
139
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);
144
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;
152
153   if (current_value.s == NULL)
154     {
155       *ref = NULL;
156       return 0;
157     }
158
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);
167
168   *ref = current_value.s;
169   return current_value.m->l_addr;
170 }
171
172
173 /* This function works like _dl_lookup_symbol but it takes an
174    additional arguement with the version number of the requested
175    symbol.
176
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)
185 {
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;
189
190   ++_dl_num_relocations;
191
192   /* Search the relevant loaded objects for a definition.  */
193   for (scope = symbol_scope; *scope; ++scope)
194     {
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;
200
201       if (res < 0)
202         {
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;
217         }
218     }
219
220   if (current_value.s == NULL)
221     {
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;
231     }
232
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);
242
243   *ref = current_value.s;
244   return current_value.m->l_addr;
245 }
246
247
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)
258 {
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;
263
264   ++_dl_num_relocations;
265
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);
270
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;
278
279   if (current_value.s == NULL)
280     {
281       if (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
282         {
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);
291         }
292       *ref = NULL;
293       return 0;
294     }
295
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);
306
307   *ref = current_value.s;
308   return current_value.m->l_addr;
309 }
310
311
312 /* Cache the location of MAP's hash table.  */
313
314 void
315 internal_function
316 _dl_setup_hash (struct link_map *map)
317 {
318   ElfW(Symndx) *hash;
319   ElfW(Symndx) nchain;
320
321   if (!map->l_info[DT_HASH])
322     return;
323   hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
324
325   map->l_nbuckets = *hash++;
326   nchain = *hash++;
327   map->l_buckets = hash;
328   hash += map->l_nbuckets;
329   map->l_chain = hash;
330 }