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