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