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