From 82eef55f8fad3e00c53050de5d6ebea08df488b3 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Thu, 4 Jan 2018 18:46:17 +0100 Subject: elf: Support dlvsym within libc.so This commit adds a new _dl_open_hook entry for dlvsym and implements the function using the existing dl_lookup_symbol_x function supplied by the dynamic loader. A new hook variable, _dl_open_hook2, is introduced, which should make this change suitable for backporting: For old statically linked binaries, __libc_dlvsym will always return NULL. --- elf/tst-libc_dlvsym.h | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 elf/tst-libc_dlvsym.h (limited to 'elf/tst-libc_dlvsym.h') diff --git a/elf/tst-libc_dlvsym.h b/elf/tst-libc_dlvsym.h new file mode 100644 index 0000000000..6b1d8d5b3a --- /dev/null +++ b/elf/tst-libc_dlvsym.h @@ -0,0 +1,125 @@ +/* Compare dlvsym and __libc_dlvsym results. Common code. + Copyright (C) 2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* compare_vsyms is the main entry point for these tests. + + Indirectly, It calls __libc_dlvsym (from libc.so; internal + interface) and dlvsym (from libdl.so; public interface) to compare + the results for a selected set of symbols in libc.so which + typically have more than one symbol version. The two functions are + implemented by somewhat different code, and this test checks that + their results are the same. + + The versions are generated to range from GLIBC_2.0 to GLIBC_2.Y, + with Y being the current __GLIBC_MINOR__ version plus two. In + addition, there is a list of special symbol versions of the form + GLIBC_2.Y.Z, which were used for some releases. + + Comparing the two dlvsym results at versions which do not actually + exist does not test much, but it will not contribute to false test + failures, either. */ + +#include +#include +#include +#include +#include +#include + +/* Run consistency check for versioned symbol NAME@VERSION. NB: We + may execute in a shared object, so exit on error for proper error + reporting. */ +static void +compare_vsyms_0 (void *libc_handle, const char *name, const char *version, + bool *pfound) +{ + void *dlvsym_address = dlvsym (libc_handle, name, version); + void *libc_dlvsym_address + = __libc_dlvsym (libc_handle, name, version); + if (dlvsym_address != libc_dlvsym_address) + FAIL_EXIT1 ("%s@%s mismatch: %p != %p", + name, version, dlvsym_address, libc_dlvsym_address); + if (dlvsym_address != NULL) + *pfound = true; +} + + +/* Run consistency check for versioned symbol NAME at multiple symbol + version. */ +static void +compare_vsyms_1 (void *libc_handle, const char *name) +{ + bool found = false; + + /* Historic versions which do not follow the usual GLIBC_2.Y + pattern, to increase test coverage. Not all architectures have + those, but probing additional versions does not hurt. */ + static const char special_versions[][12] = + { + "GLIBC_2.1.1", + "GLIBC_2.1.2", + "GLIBC_2.1.3", + "GLIBC_2.1.4", + "GLIBC_2.2.1", + "GLIBC_2.2.2", + "GLIBC_2.2.3", + "GLIBC_2.2.4", + "GLIBC_2.2.5", + "GLIBC_2.2.6", + "GLIBC_2.3.2", + "GLIBC_2.3.3", + "GLIBC_2.3.4", + }; + for (int i = 0; i < array_length (special_versions); ++i) + compare_vsyms_0 (libc_handle, name, special_versions[i], &found); + + /* Iterate to an out-of-range version, to cover some unused symbols + as well. */ + for (int minor_version = 0; minor_version <= __GLIBC_MINOR__ + 2; + ++minor_version) + { + char version[30]; + snprintf (version, sizeof (version), "GLIBC_%d.%d", + __GLIBC__, minor_version); + compare_vsyms_0 (libc_handle, name, version, &found); + } + + if (!found) + FAIL_EXIT1 ("symbol %s not found at any version", name); +} + +/* Run consistency checks for various symbols which usually have + multiple versions. */ +static void +compare_vsyms (void) +{ + /* The minor version loop in compare_vsyms_1 needs updating in case + we ever switch to glibc 3.0. */ + if (__GLIBC__ != 2) + FAIL_EXIT1 ("unexpected glibc major version: %d", __GLIBC__); + + /* __libc_dlvsym does not recognize the special RTLD_* handles, so + obtain an explicit handle for libc.so. */ + void *libc_handle = xdlopen (LIBC_SO, RTLD_LAZY | RTLD_NOLOAD); + + compare_vsyms_1 (libc_handle, "_sys_errlist"); + compare_vsyms_1 (libc_handle, "_sys_siglist"); + compare_vsyms_1 (libc_handle, "quick_exit"); + + xdlclose (libc_handle); +} -- cgit v1.2.3