update from main archive 970226
[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 ((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 ((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         malloc ((ndx_high + 1) * sizeof (*map->l_versions));
251       memset (map->l_versions, '\0',
252               (ndx_high + 1) * sizeof (*map->l_versions));
253       if (map->l_versions == NULL)
254         {
255           _dl_signal_error (ENOMEM, (*map->l_name ? map->l_name : _dl_argv[0]),
256                             "cannot allocate version reference table");
257           result = 1;
258         }
259       else
260         {
261           /* Store the number of available symbols.  */
262           map->l_nversions = ndx_high + 1;
263
264           if (dyn != NULL)
265             {
266               ElfW(Verneed) *ent;
267               ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
268               while (1)
269                 {
270                   ElfW(Vernaux) *aux;
271                   aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
272                   while (1)
273                     {
274                       ElfW(Half) ndx = aux->vna_other & 0x7fff;
275                       map->l_versions[ndx].hash = aux->vna_hash;
276                       map->l_versions[ndx].name = &strtab[aux->vna_name];
277                       map->l_versions[ndx].filename = &strtab[ent->vn_file];
278
279                       if (aux->vna_next == 0)
280                         /* No more symbols.  */
281                         break;
282
283                       /* Advance to next symbol.  */
284                       aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
285                     }
286
287                   if (ent->vn_next == 0)
288                     /* No more dependencies.  */
289                     break;
290
291                   /* Advance to next dependency.  */
292                   ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
293                 }
294             }
295
296           /* And insert the defined versions.  */
297           if (def != NULL)
298             {
299               ElfW(Verdef) *ent;
300               ent = (ElfW(Verdef)  *) (map->l_addr + def->d_un.d_ptr);
301               while (1)
302                 {
303                   ElfW(Verdaux) *aux;
304                   aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
305
306                   if ((ent->vd_flags & VER_FLG_BASE) == 0)
307                     {
308                       /* The name of the base version should not be
309                          available for matching a versioned symbol.  */
310                       ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
311                       map->l_versions[ndx].hash = ent->vd_hash;
312                       map->l_versions[ndx].name = &strtab[aux->vda_name];
313                       map->l_versions[ndx].filename = NULL;
314                     }
315
316                   if (ent->vd_next == 0)
317                     /* No more definitions.  */
318                     break;
319
320                   ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
321                 }
322             }
323         }
324     }
325
326   return result;
327 }
328
329
330 int
331 _dl_check_all_versions (struct link_map *map, int verbose)
332 {
333   struct link_map *l;
334   int result = 0;
335
336   for (l = map; l != NULL; l = l->l_next)
337     result |= l->l_opencount != 0 && _dl_check_map_versions (l, verbose);
338
339   return result;
340 }