_dl_check_map_versions): Skip objects with no strtabs.
[kopensolaris-gnu/glibc.git] / elf / dl-version.c
1 /* Handle symbol and library versioning.
2    Copyright (C) 1997 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <elf.h>
22 #include <errno.h>
23 #include <link.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include <stdio-common/_itoa.h>
29
30
31 /* Set in rtld.c at startup.  */
32 extern char **_dl_argv;
33
34 #define VERSTAG(tag)    (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
35
36
37 #define make_string(string, rest...) \
38   ({                                                                          \
39     const char *all[] = { string, ## rest };                                  \
40     size_t len, cnt;                                                          \
41     char *result, *cp;                                                        \
42                                                                               \
43     len = 1;                                                                  \
44     for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)                \
45       len += strlen (all[cnt]);                                               \
46                                                                               \
47     cp = result = alloca (len);                                               \
48     for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)                \
49       cp = stpcpy (cp, all[cnt]);                                             \
50                                                                               \
51     result;                                                                   \
52   })
53
54
55 static inline struct link_map *
56 find_needed (const char *name, struct link_map *map)
57 {
58   unsigned int n;
59
60   for (n = 0; n < _dl_loaded->l_nsearchlist; ++n)
61     if (_dl_name_match_p (name, _dl_loaded->l_searchlist[n]))
62       return _dl_loaded->l_searchlist[n];
63
64   /* The required object is not in the global scope, look to see if it is
65      a dependency of the current object.  */
66   for (n = 0; n < map->l_nsearchlist; n++)
67     if (_dl_name_match_p (name, map->l_searchlist[n]))
68       return map->l_searchlist[n];
69
70   /* Should never happen.  */
71   return NULL;
72 }
73
74
75 static int
76 match_symbol (const char *name, ElfW(Word) hash, const char *string,
77               struct link_map *map, int verbose, int weak)
78 {
79   const char *strtab = (const char *) (map->l_addr
80                                        + map->l_info[DT_STRTAB]->d_un.d_ptr);
81   ElfW(Addr) def_offset;
82   ElfW(Verdef) *def;
83
84   if (map->l_info[VERSTAG (DT_VERDEF)] == NULL)
85     {
86       /* The file has no symbol versioning.  I.e., the dependent
87          object was linked against another version of this file.  We
88          only print a message if verbose output is requested.  */
89       if (verbose)
90         _dl_signal_error (0, map->l_name, make_string ("\
91 no version information available (required by ",
92                                                        name, ")"));
93       return 0;
94     }
95
96   def_offset = map->l_info[VERSTAG (DT_VERDEF)]->d_un.d_ptr;
97   assert (def_offset != 0);
98
99   def = (ElfW(Verdef) *) ((char *) map->l_addr + def_offset);
100   while (1)
101     {
102       /* Currently the version number of the definition entry is 1.
103          Make sure all we see is this version.  */
104       if (def->vd_version  != 1)
105         {
106           char buf[20];
107           buf[sizeof (buf) - 1] = '\0';
108           _dl_signal_error (0, map->l_name,
109                             make_string ("unsupported version ",
110                                          _itoa_word (def->vd_version,
111                                                      &buf[sizeof (buf) - 1],
112                                                      10, 0),
113                                          " of Verdef record"));
114           return 1;
115         }
116
117       /* Compare the hash values.  */
118       if (hash == def->vd_hash)
119         {
120           ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
121
122           /* To be safe, compare the string as well.  */
123           if (strcmp (string, strtab + aux->vda_name) == 0)
124             /* Bingo!  */
125             return 0;
126         }
127
128       /* If no more definitions we failed to find what we want.  */
129       if (def->vd_next == 0)
130         break;
131
132       /* Next definition.  */
133       def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
134     }
135
136   /* Symbol not found.  If it was a weak reference it is not fatal.  */
137   if (weak)
138     {
139       if (verbose)
140         _dl_signal_error (0, map->l_name,
141                           make_string ("weak version `", string,
142                                        "' not found (required by ", name,
143                                        ")"));
144       return 0;
145     }
146
147   _dl_signal_error (0, map->l_name,
148                     make_string ("version `", string,
149                                  "' not found (required by ", name, ")"));
150   return 1;
151 }
152
153
154 int
155 _dl_check_map_versions (struct link_map *map, int verbose)
156 {
157   int result = 0;
158   const char *strtab;
159   /* Pointer to section with needed versions.  */
160   ElfW(Dyn) *dyn;
161   /* Pointer to dynamic section with definitions.  */
162   ElfW(Dyn) *def;
163   /* We need to find out which is the highest version index used
164     in a dependecy.  */
165   unsigned int ndx_high = 0;
166
167   /* If we don't have a string table, we must be ok.  */
168   if (map->l_info[DT_STRTAB] == NULL)
169     return 0;
170   strtab = (const char *) (map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
171
172   dyn = map->l_info[VERSTAG (DT_VERNEED)];
173   def = map->l_info[VERSTAG (DT_VERDEF)];
174
175   if (dyn != NULL)
176     {
177       /* This file requires special versions from its dependencies.  */
178       ElfW(Verneed) *ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
179
180       /* Currently the version number of the needed entry is 1.
181          Make sure all we see is this version.  */
182       if (ent->vn_version  != 1)
183         {
184           char buf[20];
185           buf[sizeof (buf) - 1] = '\0';
186           _dl_signal_error (0, (*map->l_name ? map->l_name : _dl_argv[0]),
187                             make_string ("unsupported version ",
188                                          _itoa_word (ent->vn_version,
189                                                      &buf[sizeof (buf) - 1],
190                                                      10, 0),
191                                          " of Verneed record\n"));
192           return 1;
193         }
194
195       while (1)
196         {
197           ElfW(Vernaux) *aux;
198           struct link_map *needed = find_needed (strtab + ent->vn_file, map);
199
200           /* If NEEDED is NULL this means a dependency was not found
201              and no stub entry was created.  This should never happen.  */
202           assert (needed != NULL);
203
204           /* NEEDED is the map for the file we need.  Now look for the
205              dependency symbols.  */
206           aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
207           while (1)
208             {
209               /* Match the symbol.  */
210               result |= match_symbol ((*map->l_name
211                                        ? map->l_name : _dl_argv[0]),
212                                       aux->vna_hash,
213                                       strtab + aux->vna_name,
214                                       needed, verbose,
215                                       aux->vna_flags & VER_FLG_WEAK);
216
217               /* Compare the version index.  */
218               if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
219                 ndx_high = aux->vna_other & 0x7fff;
220
221               if (aux->vna_next == 0)
222                 /* No more symbols.  */
223                 break;
224
225               /* Next symbol.  */
226               aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
227             }
228
229           if (ent->vn_next == 0)
230             /* No more dependencies.  */
231             break;
232
233           /* Next dependency.  */
234           ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
235         }
236     }
237
238   /* We also must store the names of the defined versions.  Determine
239      the maximum index here as well.
240
241      XXX We could avoid the loop by just taking the number of definitions
242      as an upper bound of new indeces.  */
243   if (def != NULL)
244     {
245       ElfW(Verdef) *ent;
246       ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
247       while (1)
248         {
249           if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
250             ndx_high = ent->vd_ndx & 0x7fff;
251
252           if (ent->vd_next == 0)
253             /* No more definitions.  */
254             break;
255
256           ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
257         }
258     }
259
260   if (ndx_high > 0)
261     {
262       /* Now we are ready to build the array with the version names
263          which can be indexed by the version index in the VERSYM
264          section.  */
265       map->l_versions = (struct r_found_version *)
266         calloc (ndx_high + 1, sizeof (*map->l_versions));
267       if (map->l_versions == NULL)
268         {
269           _dl_signal_error (ENOMEM, (*map->l_name ? map->l_name : _dl_argv[0]),
270                             "cannot allocate version reference table");
271           result = 1;
272         }
273       else
274         {
275           /* Store the number of available symbols.  */
276           map->l_nversions = ndx_high + 1;
277
278           if (dyn != NULL)
279             {
280               ElfW(Verneed) *ent;
281               ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
282               while (1)
283                 {
284                   ElfW(Vernaux) *aux;
285                   aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
286                   while (1)
287                     {
288                       ElfW(Half) ndx = aux->vna_other & 0x7fff;
289                       map->l_versions[ndx].hash = aux->vna_hash;
290                       map->l_versions[ndx].hidden = aux->vna_other & 0x8000;
291                       map->l_versions[ndx].name = &strtab[aux->vna_name];
292                       map->l_versions[ndx].filename = &strtab[ent->vn_file];
293
294                       if (aux->vna_next == 0)
295                         /* No more symbols.  */
296                         break;
297
298                       /* Advance to next symbol.  */
299                       aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
300                     }
301
302                   if (ent->vn_next == 0)
303                     /* No more dependencies.  */
304                     break;
305
306                   /* Advance to next dependency.  */
307                   ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
308                 }
309             }
310
311           /* And insert the defined versions.  */
312           if (def != NULL)
313             {
314               ElfW(Verdef) *ent;
315               ent = (ElfW(Verdef)  *) (map->l_addr + def->d_un.d_ptr);
316               while (1)
317                 {
318                   ElfW(Verdaux) *aux;
319                   aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
320
321                   if ((ent->vd_flags & VER_FLG_BASE) == 0)
322                     {
323                       /* The name of the base version should not be
324                          available for matching a versioned symbol.  */
325                       ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
326                       map->l_versions[ndx].hash = ent->vd_hash;
327                       map->l_versions[ndx].name = &strtab[aux->vda_name];
328                       map->l_versions[ndx].filename = NULL;
329                     }
330
331                   if (ent->vd_next == 0)
332                     /* No more definitions.  */
333                     break;
334
335                   ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
336                 }
337             }
338         }
339     }
340
341   return result;
342 }
343
344
345 int
346 _dl_check_all_versions (struct link_map *map, int verbose)
347 {
348   struct link_map *l;
349   int result = 0;
350
351   for (l = map; l != NULL; l = l->l_next)
352     result |= l->l_opencount != 0 && _dl_check_map_versions (l, verbose);
353
354   return result;
355 }