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