1/* Handle symbol and library versioning.
2 Copyright (C) 1997-2024 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 dependency. */
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 (errcode: 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 const char *string = strtab + aux->vna_name;
218 result |= match_symbol (DSO_FILENAME (map->l_name),
219 ns: map->l_ns, hash: aux->vna_hash,
220 string, map: needed->l_real, verbose,
221 weak: aux->vna_flags & VER_FLG_WEAK);
222
223 /* 0xfd0e42: _dl_elf_hash ("GLIBC_ABI_DT_RELR"). */
224 if (aux->vna_hash == 0xfd0e42
225 && __glibc_likely (strcmp (string,
226 "GLIBC_ABI_DT_RELR")
227 == 0))
228 map->l_dt_relr_ref = 1;
229
230 /* Compare the version index. */
231 if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
232 ndx_high = aux->vna_other & 0x7fff;
233
234 if (aux->vna_next == 0)
235 /* No more symbols. */
236 break;
237
238 /* Next symbol. */
239 aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
240 }
241 }
242
243 if (ent->vn_next == 0)
244 /* No more dependencies. */
245 break;
246
247 /* Next dependency. */
248 ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
249 }
250 }
251
252 /* We also must store the names of the defined versions. Determine
253 the maximum index here as well.
254
255 XXX We could avoid the loop by just taking the number of definitions
256 as an upper bound of new indices. */
257 if (def != NULL)
258 {
259 ElfW(Verdef) *ent;
260 ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
261 while (1)
262 {
263 if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
264 ndx_high = ent->vd_ndx & 0x7fff;
265
266 if (ent->vd_next == 0)
267 /* No more definitions. */
268 break;
269
270 ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
271 }
272 }
273
274 if (ndx_high > 0)
275 {
276 /* Now we are ready to build the array with the version names
277 which can be indexed by the version index in the VERSYM
278 section. */
279 map->l_versions = (struct r_found_version *)
280 calloc (nmemb: ndx_high + 1, size: sizeof (*map->l_versions));
281 if (__glibc_unlikely (map->l_versions == NULL))
282 {
283 _dl_exception_create
284 (&exception, DSO_FILENAME (map->l_name),
285 N_("cannot allocate version reference table"));
286 errval = ENOMEM;
287 goto call_error;
288 }
289
290 /* Store the number of available symbols. */
291 map->l_nversions = ndx_high + 1;
292
293 /* Compute the pointer to the version symbols. */
294 map->l_versyms = (void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
295
296 if (dyn != NULL)
297 {
298 ElfW(Verneed) *ent;
299 ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
300 while (1)
301 {
302 ElfW(Vernaux) *aux;
303 aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
304 while (1)
305 {
306 ElfW(Half) ndx = aux->vna_other & 0x7fff;
307 /* In trace mode, dependencies may be missing. */
308 if (__glibc_likely (ndx < map->l_nversions))
309 {
310 map->l_versions[ndx].hash = aux->vna_hash;
311 map->l_versions[ndx].hidden = aux->vna_other & 0x8000;
312 map->l_versions[ndx].name = &strtab[aux->vna_name];
313 map->l_versions[ndx].filename = &strtab[ent->vn_file];
314 }
315
316 if (aux->vna_next == 0)
317 /* No more symbols. */
318 break;
319
320 /* Advance to next symbol. */
321 aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
322 }
323
324 if (ent->vn_next == 0)
325 /* No more dependencies. */
326 break;
327
328 /* Advance to next dependency. */
329 ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
330 }
331 }
332
333 /* And insert the defined versions. */
334 if (def != NULL)
335 {
336 ElfW(Verdef) *ent;
337 ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
338 while (1)
339 {
340 ElfW(Verdaux) *aux;
341 aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
342
343 if ((ent->vd_flags & VER_FLG_BASE) == 0)
344 {
345 /* The name of the base version should not be
346 available for matching a versioned symbol. */
347 ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
348 map->l_versions[ndx].hash = ent->vd_hash;
349 map->l_versions[ndx].name = &strtab[aux->vda_name];
350 map->l_versions[ndx].filename = NULL;
351 }
352
353 if (ent->vd_next == 0)
354 /* No more definitions. */
355 break;
356
357 ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
358 }
359 }
360 }
361
362 /* When there is a DT_VERNEED entry with libc.so on DT_NEEDED, issue
363 an error if there is a DT_RELR entry without GLIBC_ABI_DT_RELR
364 dependency. */
365 if (dyn != NULL
366 && map->l_info[DT_NEEDED] != NULL
367 && map->l_info[DT_RELR] != NULL
368 && __glibc_unlikely (!map->l_dt_relr_ref))
369 {
370 const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
371 const ElfW(Dyn) *d;
372 for (d = map->l_ld; d->d_tag != DT_NULL; ++d)
373 if (d->d_tag == DT_NEEDED)
374 {
375 const char *name = strtab + d->d_un.d_val;
376 if (strncmp (name, "libc.so.", 8) == 0)
377 {
378 _dl_exception_create
379 (&exception, DSO_FILENAME (map->l_name),
380 N_("DT_RELR without GLIBC_ABI_DT_RELR dependency"));
381 goto call_error;
382 }
383 }
384 }
385
386 return result;
387}
388
389
390int
391_dl_check_all_versions (struct link_map *map, int verbose, int trace_mode)
392{
393 struct link_map *l;
394 int result = 0;
395
396 for (l = map; l != NULL; l = l->l_next)
397 result |= (! l->l_faked
398 && _dl_check_map_versions (map: l, verbose, trace_mode));
399
400 return result;
401}
402

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