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