1/* Handle symbol and library versioning.
2 Copyright (C) 1997-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <elf.h>
20#include <errno.h>
21#include <libintl.h>
22#include <stdlib.h>
23#include <string.h>
24#include <ldsodefs.h>
25#include <_itoa.h>
26
27#include <assert.h>
28
29static inline struct link_map *
30__attribute ((always_inline))
31find_needed (const char *name, struct link_map *map)
32{
33 struct link_map *tmap;
34 unsigned int n;
35
36 for (tmap = GL(dl_ns)[map->l_ns]._ns_loaded; tmap != NULL;
37 tmap = tmap->l_next)
38 if (_dl_name_match_p (name: name, map: tmap))
39 return tmap;
40
41 /* The required object is not in the global scope, look to see if it is
42 a dependency of the current object. */
43 for (n = 0; n < map->l_searchlist.r_nlist; n++)
44 if (_dl_name_match_p (name: name, map: map->l_searchlist.r_list[n]))
45 return map->l_searchlist.r_list[n];
46
47 /* Should never happen. */
48 return NULL;
49}
50
51
52static int
53match_symbol (const char *name, Lmid_t ns, ElfW(Word) hash, const char *string,
54 struct link_map *map, int verbose, int weak)
55{
56 const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
57 ElfW(Addr) def_offset;
58 ElfW(Verdef) *def;
59 /* Initialize to make the compiler happy. */
60 int result = 0;
61 struct dl_exception exception;
62
63 /* Display information about what we are doing while debugging. */
64 if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS))
65 _dl_debug_printf (fmt: "\
66checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
67 string, DSO_FILENAME (map->l_name),
68 map->l_ns, name, ns);
69
70 if (__glibc_unlikely (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL))
71 {
72 /* The file has no symbol versioning. I.e., the dependent
73 object was linked against another version of this file. We
74 only print a message if verbose output is requested. */
75 if (verbose)
76 {
77 /* XXX We cannot translate the messages. */
78 _dl_exception_create_format
79 (&exception, DSO_FILENAME (map->l_name),
80 fmt: "no version information available (required by %s)", name);
81 goto call_cerror;
82 }
83 return 0;
84 }
85
86 def_offset = map->l_info[VERSYMIDX (DT_VERDEF)]->d_un.d_ptr;
87 assert (def_offset != 0);
88
89 def = (ElfW(Verdef) *) ((char *) map->l_addr + def_offset);
90 while (1)
91 {
92 /* Currently the version number of the definition entry is 1.
93 Make sure all we see is this version. */
94 if (__builtin_expect (def->vd_version, 1) != 1)
95 {
96 char buf[20];
97 buf[sizeof (buf) - 1] = '\0';
98 /* XXX We cannot translate the message. */
99 _dl_exception_create_format
100 (&exception, DSO_FILENAME (map->l_name),
101 fmt: "unsupported version %s of Verdef record",
102 _itoa (def->vd_version, &buf[sizeof (buf) - 1], 10, 0));
103 result = 1;
104 goto call_cerror;
105 }
106
107 /* Compare the hash values. */
108 if (hash == def->vd_hash)
109 {
110 ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
111
112 /* To be safe, compare the string as well. */
113 if (__builtin_expect (strcmp (string, strtab + aux->vda_name), 0)
114 == 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 (__glibc_likely (weak))
129 {
130 if (verbose)
131 {
132 /* XXX We cannot translate the message. */
133 _dl_exception_create_format
134 (&exception, DSO_FILENAME (map->l_name),
135 fmt: "weak version `%s' not found (required by %s)", string, name);
136 goto call_cerror;
137 }
138 return 0;
139 }
140
141 /* XXX We cannot translate the message. */
142 _dl_exception_create_format
143 (&exception, DSO_FILENAME (map->l_name),
144 fmt: "version `%s' not found (required by %s)", string, name);
145 result = 1;
146 call_cerror:
147 _dl_signal_cexception (errcode: 0, exception: &exception, N_("version lookup error"));
148 _dl_exception_free (&exception);
149 return result;
150}
151
152
153int
154_dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
155{
156 int result = 0;
157 const char *strtab;
158 /* Pointer to section with needed versions. */
159 ElfW(Dyn) *dyn;
160 /* Pointer to dynamic section with definitions. */
161 ElfW(Dyn) *def;
162 /* We need to find out which is the highest version index used
163 in a dependecy. */
164 unsigned int ndx_high = 0;
165 struct dl_exception exception;
166 /* Initialize to make the compiler happy. */
167 int errval = 0;
168
169 /* If we don't have a string table, we must be ok. */
170 if (map->l_info[DT_STRTAB] == NULL)
171 return 0;
172 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
173
174 dyn = map->l_info[VERSYMIDX (DT_VERNEED)];
175 def = map->l_info[VERSYMIDX (DT_VERDEF)];
176
177 if (dyn != NULL)
178 {
179 /* This file requires special versions from its dependencies. */
180 ElfW(Verneed) *ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
181
182 /* Currently the version number of the needed entry is 1.
183 Make sure all we see is this version. */
184 if (__builtin_expect (ent->vn_version, 1) != 1)
185 {
186 char buf[20];
187 buf[sizeof (buf) - 1] = '\0';
188 /* XXX We cannot translate the message. */
189 _dl_exception_create_format
190 (&exception, DSO_FILENAME (map->l_name),
191 fmt: "unsupported version %s of Verneed record",
192 _itoa (ent->vn_version, &buf[sizeof (buf) - 1], 10, 0));
193 call_error:
194 _dl_signal_exception (errval, &exception, NULL);
195 }
196
197 while (1)
198 {
199 ElfW(Vernaux) *aux;
200 struct link_map *needed = find_needed (name: strtab + ent->vn_file, map);
201
202 /* If NEEDED is NULL this means a dependency was not found
203 and no stub entry was created. This should never happen. */
204 assert (needed != NULL);
205
206 /* Make sure this is no stub we created because of a missing
207 dependency. */
208 if (__builtin_expect (! trace_mode, 1)
209 || ! __builtin_expect (needed->l_faked, 0))
210 {
211 /* NEEDED is the map for the file we need. Now look for the
212 dependency symbols. */
213 aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
214 while (1)
215 {
216 /* Match the symbol. */
217 result |= match_symbol (DSO_FILENAME (map->l_name),
218 ns: map->l_ns, hash: aux->vna_hash,
219 string: strtab + aux->vna_name,
220 map: needed->l_real, verbose,
221 weak: aux->vna_flags & VER_FLG_WEAK);
222
223 /* Compare the version index. */
224 if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
225 ndx_high = aux->vna_other & 0x7fff;
226
227 if (aux->vna_next == 0)
228 /* No more symbols. */
229 break;
230
231 /* Next symbol. */
232 aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
233 }
234 }
235
236 if (ent->vn_next == 0)
237 /* No more dependencies. */
238 break;
239
240 /* Next dependency. */
241 ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
242 }
243 }
244
245 /* We also must store the names of the defined versions. Determine
246 the maximum index here as well.
247
248 XXX We could avoid the loop by just taking the number of definitions
249 as an upper bound of new indices. */
250 if (def != NULL)
251 {
252 ElfW(Verdef) *ent;
253 ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
254 while (1)
255 {
256 if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
257 ndx_high = ent->vd_ndx & 0x7fff;
258
259 if (ent->vd_next == 0)
260 /* No more definitions. */
261 break;
262
263 ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
264 }
265 }
266
267 if (ndx_high > 0)
268 {
269 /* Now we are ready to build the array with the version names
270 which can be indexed by the version index in the VERSYM
271 section. */
272 map->l_versions = (struct r_found_version *)
273 calloc (nmemb: ndx_high + 1, size: sizeof (*map->l_versions));
274 if (__glibc_unlikely (map->l_versions == NULL))
275 {
276 _dl_exception_create
277 (&exception, DSO_FILENAME (map->l_name),
278 N_("cannot allocate version reference table"));
279 errval = ENOMEM;
280 goto call_error;
281 }
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 = (void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
288
289 if (dyn != NULL)
290 {
291 ElfW(Verneed) *ent;
292 ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
293 while (1)
294 {
295 ElfW(Vernaux) *aux;
296 aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
297 while (1)
298 {
299 ElfW(Half) ndx = aux->vna_other & 0x7fff;
300 /* In trace mode, dependencies may be missing. */
301 if (__glibc_likely (ndx < map->l_nversions))
302 {
303 map->l_versions[ndx].hash = aux->vna_hash;
304 map->l_versions[ndx].hidden = aux->vna_other & 0x8000;
305 map->l_versions[ndx].name = &strtab[aux->vna_name];
306 map->l_versions[ndx].filename = &strtab[ent->vn_file];
307 }
308
309 if (aux->vna_next == 0)
310 /* No more symbols. */
311 break;
312
313 /* Advance to next symbol. */
314 aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
315 }
316
317 if (ent->vn_next == 0)
318 /* No more dependencies. */
319 break;
320
321 /* Advance to next dependency. */
322 ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
323 }
324 }
325
326 /* And insert the defined versions. */
327 if (def != NULL)
328 {
329 ElfW(Verdef) *ent;
330 ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
331 while (1)
332 {
333 ElfW(Verdaux) *aux;
334 aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
335
336 if ((ent->vd_flags & VER_FLG_BASE) == 0)
337 {
338 /* The name of the base version should not be
339 available for matching a versioned symbol. */
340 ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
341 map->l_versions[ndx].hash = ent->vd_hash;
342 map->l_versions[ndx].name = &strtab[aux->vda_name];
343 map->l_versions[ndx].filename = NULL;
344 }
345
346 if (ent->vd_next == 0)
347 /* No more definitions. */
348 break;
349
350 ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
351 }
352 }
353 }
354
355 return result;
356}
357
358
359int
360_dl_check_all_versions (struct link_map *map, int verbose, int trace_mode)
361{
362 struct link_map *l;
363 int result = 0;
364
365 for (l = map; l != NULL; l = l->l_next)
366 result |= (! l->l_faked
367 && _dl_check_map_versions (map: l, verbose, trace_mode));
368
369 return result;
370}
371

source code of glibc/elf/dl-version.c