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.
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.
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.
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/>. */
29 static inline struct link_map
*
30 __attribute ((always_inline
))
31 find_needed (const char *name
, struct link_map
*map
)
33 struct link_map
*tmap
;
36 for (tmap
= GL(dl_ns
)[map
->l_ns
]._ns_loaded
; tmap
!= NULL
;
38 if (_dl_name_match_p (name
, tmap
))
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
, map
->l_searchlist
.r_list
[n
]))
45 return map
->l_searchlist
.r_list
[n
];
47 /* Should never happen. */
53 match_symbol (const char *name
, Lmid_t ns
, ElfW(Word
) hash
, const char *string
,
54 struct link_map
*map
, int verbose
, int weak
)
56 const char *strtab
= (const void *) D_PTR (map
, l_info
[DT_STRTAB
]);
57 ElfW(Addr
) def_offset
;
59 /* Initialize to make the compiler happy. */
61 struct dl_exception exception
;
63 /* Display information about what we are doing while debugging. */
64 if (__glibc_unlikely (GLRO(dl_debug_mask
) & DL_DEBUG_VERSIONS
))
66 checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
67 string
, DSO_FILENAME (map
->l_name
),
70 if (__glibc_unlikely (map
->l_info
[VERSYMIDX (DT_VERDEF
)] == NULL
))
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. */
77 /* XXX We cannot translate the messages. */
78 _dl_exception_create_format
79 (&exception
, DSO_FILENAME (map
->l_name
),
80 "no version information available (required by %s)", name
);
86 def_offset
= map
->l_info
[VERSYMIDX (DT_VERDEF
)]->d_un
.d_ptr
;
87 assert (def_offset
!= 0);
89 def
= (ElfW(Verdef
) *) ((char *) map
->l_addr
+ def_offset
);
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)
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 "unsupported version %s of Verdef record",
102 _itoa (def
->vd_version
, &buf
[sizeof (buf
) - 1], 10, 0));
107 /* Compare the hash values. */
108 if (hash
== def
->vd_hash
)
110 ElfW(Verdaux
) *aux
= (ElfW(Verdaux
) *) ((char *) def
+ def
->vd_aux
);
112 /* To be safe, compare the string as well. */
113 if (__builtin_expect (strcmp (string
, strtab
+ aux
->vda_name
), 0)
119 /* If no more definitions we failed to find what we want. */
120 if (def
->vd_next
== 0)
123 /* Next definition. */
124 def
= (ElfW(Verdef
) *) ((char *) def
+ def
->vd_next
);
127 /* Symbol not found. If it was a weak reference it is not fatal. */
128 if (__glibc_likely (weak
))
132 /* XXX We cannot translate the message. */
133 _dl_exception_create_format
134 (&exception
, DSO_FILENAME (map
->l_name
),
135 "weak version `%s' not found (required by %s)", string
, name
);
141 /* XXX We cannot translate the message. */
142 _dl_exception_create_format
143 (&exception
, DSO_FILENAME (map
->l_name
),
144 "version `%s' not found (required by %s)", string
, name
);
147 _dl_signal_cexception (0, &exception
, N_("version lookup error"));
148 _dl_exception_free (&exception
);
154 _dl_check_map_versions (struct link_map
*map
, int verbose
, int trace_mode
)
158 /* Pointer to section with needed versions. */
160 /* Pointer to dynamic section with definitions. */
162 /* We need to find out which is the highest version index used
164 unsigned int ndx_high
= 0;
165 struct dl_exception exception
;
166 /* Initialize to make the compiler happy. */
169 /* If we don't have a string table, we must be ok. */
170 if (map
->l_info
[DT_STRTAB
] == NULL
)
172 strtab
= (const void *) D_PTR (map
, l_info
[DT_STRTAB
]);
174 dyn
= map
->l_info
[VERSYMIDX (DT_VERNEED
)];
175 def
= map
->l_info
[VERSYMIDX (DT_VERDEF
)];
179 /* This file requires special versions from its dependencies. */
180 ElfW(Verneed
) *ent
= (ElfW(Verneed
) *) (map
->l_addr
+ dyn
->d_un
.d_ptr
);
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)
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 "unsupported version %s of Verneed record",
192 _itoa (ent
->vn_version
, &buf
[sizeof (buf
) - 1], 10, 0));
194 _dl_signal_exception (errval
, &exception
, NULL
);
200 struct link_map
*needed
= find_needed (strtab
+ ent
->vn_file
, map
);
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
);
206 /* Make sure this is no stub we created because of a missing
208 if (__builtin_expect (! trace_mode
, 1)
209 || ! __builtin_expect (needed
->l_faked
, 0))
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
);
216 /* Match the symbol. */
217 const char *string
= strtab
+ aux
->vna_name
;
218 result
|= match_symbol (DSO_FILENAME (map
->l_name
),
219 map
->l_ns
, aux
->vna_hash
,
220 string
, needed
->l_real
, verbose
,
221 aux
->vna_flags
& VER_FLG_WEAK
);
223 /* 0xfd0e42: _dl_elf_hash ("GLIBC_ABI_DT_RELR"). */
224 if (aux
->vna_hash
== 0xfd0e42
225 && __glibc_likely (strcmp (string
,
228 map
->l_dt_relr_ref
= 1;
230 /* Compare the version index. */
231 if ((unsigned int) (aux
->vna_other
& 0x7fff) > ndx_high
)
232 ndx_high
= aux
->vna_other
& 0x7fff;
234 if (aux
->vna_next
== 0)
235 /* No more symbols. */
239 aux
= (ElfW(Vernaux
) *) ((char *) aux
+ aux
->vna_next
);
243 if (ent
->vn_next
== 0)
244 /* No more dependencies. */
247 /* Next dependency. */
248 ent
= (ElfW(Verneed
) *) ((char *) ent
+ ent
->vn_next
);
252 /* We also must store the names of the defined versions. Determine
253 the maximum index here as well.
255 XXX We could avoid the loop by just taking the number of definitions
256 as an upper bound of new indices. */
260 ent
= (ElfW(Verdef
) *) (map
->l_addr
+ def
->d_un
.d_ptr
);
263 if ((unsigned int) (ent
->vd_ndx
& 0x7fff) > ndx_high
)
264 ndx_high
= ent
->vd_ndx
& 0x7fff;
266 if (ent
->vd_next
== 0)
267 /* No more definitions. */
270 ent
= (ElfW(Verdef
) *) ((char *) ent
+ ent
->vd_next
);
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
279 map
->l_versions
= (struct r_found_version
*)
280 calloc (ndx_high
+ 1, sizeof (*map
->l_versions
));
281 if (__glibc_unlikely (map
->l_versions
== NULL
))
284 (&exception
, DSO_FILENAME (map
->l_name
),
285 N_("cannot allocate version reference table"));
290 /* Store the number of available symbols. */
291 map
->l_nversions
= ndx_high
+ 1;
293 /* Compute the pointer to the version symbols. */
294 map
->l_versyms
= (void *) D_PTR (map
, l_info
[VERSYMIDX (DT_VERSYM
)]);
299 ent
= (ElfW(Verneed
) *) (map
->l_addr
+ dyn
->d_un
.d_ptr
);
303 aux
= (ElfW(Vernaux
) *) ((char *) ent
+ ent
->vn_aux
);
306 ElfW(Half
) ndx
= aux
->vna_other
& 0x7fff;
307 /* In trace mode, dependencies may be missing. */
308 if (__glibc_likely (ndx
< map
->l_nversions
))
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
];
316 if (aux
->vna_next
== 0)
317 /* No more symbols. */
320 /* Advance to next symbol. */
321 aux
= (ElfW(Vernaux
) *) ((char *) aux
+ aux
->vna_next
);
324 if (ent
->vn_next
== 0)
325 /* No more dependencies. */
328 /* Advance to next dependency. */
329 ent
= (ElfW(Verneed
) *) ((char *) ent
+ ent
->vn_next
);
333 /* And insert the defined versions. */
337 ent
= (ElfW(Verdef
) *) (map
->l_addr
+ def
->d_un
.d_ptr
);
341 aux
= (ElfW(Verdaux
) *) ((char *) ent
+ ent
->vd_aux
);
343 if ((ent
->vd_flags
& VER_FLG_BASE
) == 0)
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
;
353 if (ent
->vd_next
== 0)
354 /* No more definitions. */
357 ent
= (ElfW(Verdef
) *) ((char *) ent
+ ent
->vd_next
);
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
366 && map
->l_info
[DT_NEEDED
] != NULL
367 && map
->l_info
[DT_RELR
] != NULL
368 && __glibc_unlikely (!map
->l_dt_relr_ref
))
370 const char *strtab
= (const void *) D_PTR (map
, l_info
[DT_STRTAB
]);
372 for (d
= map
->l_ld
; d
->d_tag
!= DT_NULL
; ++d
)
373 if (d
->d_tag
== DT_NEEDED
)
375 const char *name
= strtab
+ d
->d_un
.d_val
;
376 if (strncmp (name
, "libc.so.", 8) == 0)
379 (&exception
, DSO_FILENAME (map
->l_name
),
380 N_("DT_RELR without GLIBC_ABI_DT_RELR dependency"));
391 _dl_check_all_versions (struct link_map
*map
, int verbose
, int trace_mode
)
396 for (l
= map
; l
!= NULL
; l
= l
->l_next
)
397 result
|= (! l
->l_faked
398 && _dl_check_map_versions (l
, verbose
, trace_mode
));