Include unistd.h.
[kopensolaris-gnu/glibc.git] / elf / dl-lookup.c
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.
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 <link.h>
22 #include <assert.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "dl-hash.h"
27 #include <dl-machine.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     ElfW(Addr) a;
38     const ElfW(Sym) *s;
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
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 link_map *list[], size_t i, size_t n,
67            const char *reference_name, const struct r_found_version *version,
68            struct link_map *skip, int reloc_type)
69 {
70   struct link_map *map;
71
72   for (; i < n; ++i)
73     {
74       const ElfW(Sym) *symtab;
75       const char *strtab;
76       const ElfW(Half) *verstab;
77       ElfW(Symndx) symidx;
78
79       map = list[i];
80
81       /* Here come the extra test needed for `_dl_lookup_symbol_skip'.  */
82       if (skip != NULL && map == skip)
83         continue;
84
85       /* Skip objects that could not be opened, which can occur in trace
86          mode.  */
87       if (map->l_opencount == 0)
88         continue;
89
90       /* Don't search the executable when resolving a copy reloc.  */
91       if (elf_machine_lookup_noexec_p (reloc_type) &&
92           map->l_type == lt_executable)
93         continue;
94
95       /* Skip objects without symbol tables.  */
96       if (map->l_info[DT_SYMTAB] == NULL)
97         continue;
98
99       symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
100       strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
101       if (map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL)
102         verstab = ((void *) map->l_addr
103                    + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr);
104       else
105         verstab = NULL;
106
107       /* Search the appropriate hash bucket in this object's symbol table
108          for a definition for the same symbol name.  */
109       for (symidx = map->l_buckets[hash % map->l_nbuckets];
110            symidx != STN_UNDEF;
111            symidx = map->l_chain[symidx])
112         {
113           const ElfW(Sym) *sym = &symtab[symidx];
114
115           if (sym->st_value == 0 || /* No value.  */
116               (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry.  */
117                && sym->st_shndx == SHN_UNDEF))
118             continue;
119
120           if (ELFW(ST_TYPE) (sym->st_info) > STT_FUNC)
121             /* Ignore all but STT_NOTYPE, STT_OBJECT and STT_FUNC entries
122                since these are no code/data definitions.  */
123             continue;
124
125           if (sym != ref && strcmp (strtab + sym->st_name, undef_name))
126             /* Not the symbol we are looking for.  */
127             continue;
128
129           if (version == NULL)
130             {
131               /* No specific version is selected.  When the object
132                  file also does not define a version we have a match.
133                  Otherwise we only accept the default version, i.e.,
134                  the version which name is "".  */
135               if (verstab != NULL)
136                 {
137                   ElfW(Half) ndx = verstab[symidx] & 0x7fff;
138                   if (ndx > 2) /* map->l_versions[ndx].hash != 0) */
139                     continue;
140                 }
141             }
142           else
143             {
144               if (verstab == NULL)
145                 {
146                   /* We need a versioned system but haven't found any.
147                      If this is the object which is referenced in the
148                      verneed entry it is a bug in the library since a
149                      symbol must not simply disappear.  */
150                   if (version->filename != NULL
151                       && _dl_name_match_p (version->filename, map))
152                     return -2;
153                   /* Otherwise we accept the symbol.  */
154                 }
155               else
156                 {
157                   /* We can match the version information or use the
158                      default one if it is not hidden.  */
159                   ElfW(Half) ndx = verstab[symidx] & 0x7fff;
160                   if ((map->l_versions[ndx].hash != version->hash
161                        || strcmp (map->l_versions[ndx].name, version->name))
162                       && (version->hidden || map->l_versions[ndx].hash
163                           || (verstab[symidx] & 0x8000)))
164                     /* It's not the version we want.  */
165                     continue;
166                 }
167             }
168
169           switch (ELFW(ST_BIND) (sym->st_info))
170             {
171             case STB_GLOBAL:
172               /* Global definition.  Just what we need.  */
173               result->s = sym;
174               result->a = map->l_addr;
175               return 1;
176             case STB_WEAK:
177               /* Weak definition.  Use this value if we don't find
178                  another.  */
179               if (! result->s)
180                 {
181                   result->s = sym;
182                   result->a = map->l_addr;
183                 }
184               break;
185             default:
186               /* Local symbols are ignored.  */
187               break;
188             }
189
190           /* There cannot be another entry for this symbol so stop here.  */
191           break;
192         }
193
194       /* If this current map is the one mentioned in the verneed entry
195          and we have not found a weak entry, it is a bug.  */
196       if (symidx == STN_UNDEF && version != NULL && version->filename != NULL
197           && _dl_name_match_p (version->filename, map))
198         return -1;
199     }
200
201   /* We have not found anything until now.  */
202   return 0;
203 }
204
205 /* Search loaded objects' symbol tables for a definition of the symbol
206    UNDEF_NAME.  */
207
208 ElfW(Addr)
209 _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
210                    struct link_map *symbol_scope[],
211                    const char *reference_name,
212                    int reloc_type)
213 {
214   const unsigned long int hash = _dl_elf_hash (undef_name);
215   struct sym_val current_value = { 0, NULL };
216   struct link_map **scope;
217
218   /* Search the relevant loaded objects for a definition.  */
219   for (scope = symbol_scope; *scope; ++scope)
220     if (do_lookup (undef_name, hash, *ref, &current_value,
221                    (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
222                    reference_name, NULL, NULL, reloc_type))
223       break;
224
225   if (current_value.s == NULL &&
226       (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
227     /* We could find no value for a strong reference.  */
228     _dl_signal_error (0, reference_name,
229                       make_string (undefined_msg, undef_name));
230
231   *ref = current_value.s;
232   return current_value.a;
233 }
234
235
236 /* This function is nearly the same as `_dl_lookup_symbol' but it
237    skips in the first list all objects until SKIP_MAP is found.  I.e.,
238    it only considers objects which were loaded after the described
239    object.  If there are more search lists the object described by
240    SKIP_MAP is only skipped.  */
241 ElfW(Addr)
242 _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
243                         struct link_map *symbol_scope[],
244                         const char *reference_name,
245                         struct link_map *skip_map)
246 {
247   const unsigned long int hash = _dl_elf_hash (undef_name);
248   struct sym_val current_value = { 0, NULL };
249   struct link_map **scope;
250   size_t i;
251
252   /* Search the relevant loaded objects for a definition.  */
253   scope = symbol_scope;
254   for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
255     assert (i < (*scope)->l_ndupsearchlist);
256
257   if (! do_lookup (undef_name, hash, *ref, &current_value,
258                    (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
259                    reference_name, NULL, skip_map, 0))
260     while (*++scope)
261       if (do_lookup (undef_name, hash, *ref, &current_value,
262                      (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
263                      reference_name, NULL, skip_map, 0))
264         break;
265
266   *ref = current_value.s;
267   return current_value.a;
268 }
269
270
271 /* This function works like _dl_lookup_symbol but it takes an
272    additional arguement with the version number of the requested
273    symbol.
274
275    XXX We'll see whether we need this separate function.  */
276 ElfW(Addr)
277 _dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
278                              struct link_map *symbol_scope[],
279                              const char *reference_name,
280                              const struct r_found_version *version,
281                              int reloc_type)
282 {
283   const unsigned long int hash = _dl_elf_hash (undef_name);
284   struct sym_val current_value = { 0, NULL };
285   struct link_map **scope;
286
287   /* Search the relevant loaded objects for a definition.  */
288   for (scope = symbol_scope; *scope; ++scope)
289     {
290       int res = do_lookup (undef_name, hash, *ref, &current_value,
291                            (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
292                            reference_name, version, NULL, reloc_type);
293       if (res > 0)
294         break;
295
296       if (res < 0)
297         /* Oh, oh.  The file named in the relocation entry does not
298            contain the needed symbol.  */
299         _dl_signal_error (0, (*reference_name
300                               ? reference_name
301                               : (_dl_argv[0] ?: "<main program>")),
302                           make_string ("symbol ", undef_name, ", version ",
303                                        version->name,
304                                        " not defined in file ",
305                                        version->filename,
306                                        " with link time reference",
307                                        res == -2
308                                        ? " (no version symbols)" : ""));
309     }
310
311   if (current_value.s == NULL &&
312       (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
313     /* We could find no value for a strong reference.  */
314     _dl_signal_error (0, reference_name,
315                       make_string (undefined_msg, undef_name,
316                                    ", version ", version->name ?: NULL));
317
318   *ref = current_value.s;
319   return current_value.a;
320 }
321
322
323 /* Similar to _dl_lookup_symbol_skip but takes an additional argument
324    with the version we are looking for.  */
325 ElfW(Addr)
326 _dl_lookup_versioned_symbol_skip (const char *undef_name,
327                                   const ElfW(Sym) **ref,
328                                   struct link_map *symbol_scope[],
329                                   const char *reference_name,
330                                   const struct r_found_version *version,
331                                   struct link_map *skip_map)
332 {
333   const unsigned long int hash = _dl_elf_hash (undef_name);
334   struct sym_val current_value = { 0, NULL };
335   struct link_map **scope;
336   size_t i;
337
338   /* Search the relevant loaded objects for a definition.  */
339   scope = symbol_scope;
340   for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
341     assert (i < (*scope)->l_ndupsearchlist);
342
343   if (! do_lookup (undef_name, hash, *ref, &current_value,
344                    (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
345                    reference_name, version, skip_map, 0))
346     while (*++scope)
347       if (do_lookup (undef_name, hash, *ref, &current_value,
348                      (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
349                      reference_name, version, skip_map, 0))
350         break;
351
352   if (current_value.s == NULL &&
353       (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
354     {
355       /* We could find no value for a strong reference.  */
356       const size_t len = strlen (undef_name);
357       char buf[sizeof undefined_msg + len];
358       __mempcpy (__mempcpy (buf, undefined_msg, sizeof undefined_msg - 1),
359                  undef_name, len + 1);
360       _dl_signal_error (0, reference_name, buf);
361     }
362
363   *ref = current_value.s;
364   return current_value.a;
365 }
366
367
368 /* Cache the location of MAP's hash table.  */
369
370 void
371 _dl_setup_hash (struct link_map *map)
372 {
373   ElfW(Symndx) *hash;
374   ElfW(Symndx) nchain;
375
376   if (!map->l_info[DT_HASH])
377     return;
378   hash = (void *)(map->l_addr + map->l_info[DT_HASH]->d_un.d_ptr);
379
380   map->l_nbuckets = *hash++;
381   nchain = *hash++;
382   map->l_buckets = hash;
383   hash += map->l_nbuckets;
384   map->l_chain = hash;
385 }