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