]> git.ipfire.org Git - thirdparty/glibc.git/blame - elf/dl-version.c
libio: Convert __vswprintf_internal to buffers (bug 27857)
[thirdparty/glibc.git] / elf / dl-version.c
CommitLineData
c84142e8 1/* Handle symbol and library versioning.
581c785b 2 Copyright (C) 1997-2022 Free Software Foundation, Inc.
c84142e8 3 This file is part of the GNU C Library.
c84142e8
UD
4
5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
c84142e8
UD
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
41bdb6e2 13 Lesser General Public License for more details.
c84142e8 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6 16 License along with the GNU C Library; if not, see
5a82c748 17 <https://www.gnu.org/licenses/>. */
c84142e8
UD
18
19#include <elf.h>
20#include <errno.h>
8e17ea58 21#include <libintl.h>
c84142e8
UD
22#include <stdlib.h>
23#include <string.h>
a42195db 24#include <ldsodefs.h>
eb96ffb0 25#include <_itoa.h>
c84142e8 26
a853022c
UD
27#include <assert.h>
28
c84142e8 29static inline struct link_map *
dd9423a6 30__attribute ((always_inline))
762a2918 31find_needed (const char *name, struct link_map *map)
c84142e8 32{
26b4d766 33 struct link_map *tmap;
c84142e8
UD
34 unsigned int n;
35
c0f62c56
UD
36 for (tmap = GL(dl_ns)[map->l_ns]._ns_loaded; tmap != NULL;
37 tmap = tmap->l_next)
26b4d766
UD
38 if (_dl_name_match_p (name, tmap))
39 return tmap;
c84142e8 40
762a2918
UD
41 /* The required object is not in the global scope, look to see if it is
42 a dependency of the current object. */
be935610
UD
43 for (n = 0; n < map->l_searchlist.r_nlist; n++)
44 if (_dl_name_match_p (name, map->l_searchlist.r_list[n]))
45 return map->l_searchlist.r_list[n];
762a2918 46
c84142e8
UD
47 /* Should never happen. */
48 return NULL;
49}
50
51
52static int
75a142a2 53match_symbol (const char *name, Lmid_t ns, ElfW(Word) hash, const char *string,
c84142e8
UD
54 struct link_map *map, int verbose, int weak)
55{
a42195db 56 const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
714a562f 57 ElfW(Addr) def_offset;
c84142e8 58 ElfW(Verdef) *def;
6ed623f8 59 /* Initialize to make the compiler happy. */
6ed623f8 60 int result = 0;
2449ae7b 61 struct dl_exception exception;
c84142e8 62
8193034b 63 /* Display information about what we are doing while debugging. */
a1ffb40e 64 if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS))
154d10bd 65 _dl_debug_printf ("\
75a142a2 66checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
b9375348 67 string, DSO_FILENAME (map->l_name),
75a142a2 68 map->l_ns, name, ns);
8193034b 69
a1ffb40e 70 if (__glibc_unlikely (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL))
c84142e8
UD
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)
6ed623f8
UD
76 {
77 /* XXX We cannot translate the messages. */
2449ae7b
FW
78 _dl_exception_create_format
79 (&exception, DSO_FILENAME (map->l_name),
80 "no version information available (required by %s)", name);
6ed623f8
UD
81 goto call_cerror;
82 }
c84142e8
UD
83 return 0;
84 }
85
f420344c 86 def_offset = map->l_info[VERSYMIDX (DT_VERDEF)]->d_un.d_ptr;
714a562f
UD
87 assert (def_offset != 0);
88
89 def = (ElfW(Verdef) *) ((char *) map->l_addr + def_offset);
c84142e8
UD
90 while (1)
91 {
92 /* Currently the version number of the definition entry is 1.
93 Make sure all we see is this version. */
7b228b68 94 if (__builtin_expect (def->vd_version, 1) != 1)
c84142e8
UD
95 {
96 char buf[20];
97 buf[sizeof (buf) - 1] = '\0';
8e17ea58 98 /* XXX We cannot translate the message. */
2449ae7b
FW
99 _dl_exception_create_format
100 (&exception, DSO_FILENAME (map->l_name),
101 "unsupported version %s of Verdef record",
102 _itoa (def->vd_version, &buf[sizeof (buf) - 1], 10, 0));
6ed623f8
UD
103 result = 1;
104 goto call_cerror;
c84142e8
UD
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. */
7b228b68
UD
113 if (__builtin_expect (strcmp (string, strtab + aux->vda_name), 0)
114 == 0)
c84142e8
UD
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. */
a1ffb40e 128 if (__glibc_likely (weak))
c84142e8
UD
129 {
130 if (verbose)
6ed623f8
UD
131 {
132 /* XXX We cannot translate the message. */
2449ae7b
FW
133 _dl_exception_create_format
134 (&exception, DSO_FILENAME (map->l_name),
135 "weak version `%s' not found (required by %s)", string, name);
6ed623f8
UD
136 goto call_cerror;
137 }
c84142e8
UD
138 return 0;
139 }
140
8e17ea58 141 /* XXX We cannot translate the message. */
2449ae7b
FW
142 _dl_exception_create_format
143 (&exception, DSO_FILENAME (map->l_name),
144 "version `%s' not found (required by %s)", string, name);
6ed623f8
UD
145 result = 1;
146 call_cerror:
2449ae7b
FW
147 _dl_signal_cexception (0, &exception, N_("version lookup error"));
148 _dl_exception_free (&exception);
6ed623f8 149 return result;
c84142e8
UD
150}
151
152
153int
145b8413 154_dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
c84142e8
UD
155{
156 int result = 0;
f41c8091 157 const char *strtab;
c84142e8 158 /* Pointer to section with needed versions. */
f41c8091 159 ElfW(Dyn) *dyn;
c84142e8 160 /* Pointer to dynamic section with definitions. */
f41c8091 161 ElfW(Dyn) *def;
c84142e8
UD
162 /* We need to find out which is the highest version index used
163 in a dependecy. */
164 unsigned int ndx_high = 0;
2449ae7b 165 struct dl_exception exception;
6ed623f8 166 /* Initialize to make the compiler happy. */
6ed623f8 167 int errval = 0;
c84142e8 168
f41c8091
UD
169 /* If we don't have a string table, we must be ok. */
170 if (map->l_info[DT_STRTAB] == NULL)
171 return 0;
a42195db 172 strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
f41c8091 173
f420344c
UD
174 dyn = map->l_info[VERSYMIDX (DT_VERNEED)];
175 def = map->l_info[VERSYMIDX (DT_VERDEF)];
f41c8091 176
c84142e8
UD
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. */
7b228b68 184 if (__builtin_expect (ent->vn_version, 1) != 1)
c84142e8
UD
185 {
186 char buf[20];
187 buf[sizeof (buf) - 1] = '\0';
8e17ea58 188 /* XXX We cannot translate the message. */
2449ae7b
FW
189 _dl_exception_create_format
190 (&exception, DSO_FILENAME (map->l_name),
191 "unsupported version %s of Verneed record",
192 _itoa (ent->vn_version, &buf[sizeof (buf) - 1], 10, 0));
6ed623f8 193 call_error:
2449ae7b 194 _dl_signal_exception (errval, &exception, NULL);
c84142e8
UD
195 }
196
197 while (1)
198 {
199 ElfW(Vernaux) *aux;
762a2918 200 struct link_map *needed = find_needed (strtab + ent->vn_file, map);
c84142e8
UD
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
145b8413
UD
206 /* Make sure this is no stub we created because of a missing
207 dependency. */
7b228b68 208 if (__builtin_expect (! trace_mode, 1)
a881e0a0 209 || ! __builtin_expect (needed->l_faked, 0))
c84142e8 210 {
145b8413
UD
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. */
57292f57 217 const char *string = strtab + aux->vna_name;
b9375348 218 result |= match_symbol (DSO_FILENAME (map->l_name),
75a142a2 219 map->l_ns, aux->vna_hash,
57292f57 220 string, needed->l_real, verbose,
145b8413
UD
221 aux->vna_flags & VER_FLG_WEAK);
222
57292f57
L
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
145b8413
UD
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 }
c84142e8
UD
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
14ef9c18 256 as an upper bound of new indices. */
c84142e8
UD
257 if (def != NULL)
258 {
259 ElfW(Verdef) *ent;
714a562f 260 ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
c84142e8
UD
261 while (1)
262 {
c131718c 263 if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
c84142e8
UD
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. */
1fb05e3d 279 map->l_versions = (struct r_found_version *)
c131718c 280 calloc (ndx_high + 1, sizeof (*map->l_versions));
a1ffb40e 281 if (__glibc_unlikely (map->l_versions == NULL))
c84142e8 282 {
2449ae7b
FW
283 _dl_exception_create
284 (&exception, DSO_FILENAME (map->l_name),
285 N_("cannot allocate version reference table"));
6ed623f8
UD
286 errval = ENOMEM;
287 goto call_error;
c84142e8 288 }
c84142e8 289
6ed623f8
UD
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)]);
0c367d92 295
6ed623f8
UD
296 if (dyn != NULL)
297 {
298 ElfW(Verneed) *ent;
299 ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
300 while (1)
c84142e8 301 {
6ed623f8
UD
302 ElfW(Vernaux) *aux;
303 aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
c84142e8
UD
304 while (1)
305 {
6ed623f8 306 ElfW(Half) ndx = aux->vna_other & 0x7fff;
18a26b30 307 /* In trace mode, dependencies may be missing. */
a1ffb40e 308 if (__glibc_likely (ndx < map->l_nversions))
18a26b30
AS
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 }
6ed623f8
UD
315
316 if (aux->vna_next == 0)
317 /* No more symbols. */
c84142e8
UD
318 break;
319
6ed623f8
UD
320 /* Advance to next symbol. */
321 aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
c84142e8 322 }
6ed623f8
UD
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);
c84142e8 330 }
6ed623f8 331 }
c84142e8 332
6ed623f8
UD
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)
c84142e8 339 {
6ed623f8
UD
340 ElfW(Verdaux) *aux;
341 aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
c84142e8 342
6ed623f8
UD
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;
c84142e8 351 }
6ed623f8
UD
352
353 if (ent->vd_next == 0)
354 /* No more definitions. */
355 break;
356
357 ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
c84142e8
UD
358 }
359 }
360 }
361
57292f57
L
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
c84142e8
UD
386 return result;
387}
388
389
390int
145b8413 391_dl_check_all_versions (struct link_map *map, int verbose, int trace_mode)
c84142e8
UD
392{
393 struct link_map *l;
394 int result = 0;
395
396 for (l = map; l != NULL; l = l->l_next)
7969407a 397 result |= (! l->l_faked
154d10bd 398 && _dl_check_map_versions (l, verbose, trace_mode));
c84142e8
UD
399
400 return result;
401}