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