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