mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-24 14:00:30 +00:00
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.
This commit is contained in:
parent
7abf02f3ee
commit
82eef55f8f
25
ChangeLog
25
ChangeLog
@ -1,3 +1,28 @@
|
||||
2018-01-04 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
Add support for calling dlvsym from libc.so.
|
||||
* include/dlfcn.h (__libc_dlvsym): Declare.
|
||||
* elf/Makefile (tests-static-internal): Add
|
||||
tst-libc_dlvsym-static.
|
||||
(tests-internal): Add tst-libc_dlvsym.
|
||||
(modules-names): Add tst-libc_dlvsym-dso.
|
||||
(tst-libc_dlvsym, tst-libc_dlvsym-static): Link with libdl.
|
||||
(tst-libc_dlvsym-dso.so): Link with libdl, libsupport.
|
||||
(tst-libc_dlvsym.out, tst-libc_dlvsym-static.out): The shared
|
||||
object tst-libc_dlvsym-dso.so needs to be built before running
|
||||
these tests.
|
||||
(tst-libc_dlvsym-static-ENV): Set LD_LIBRARY_PATH.
|
||||
* elf/Versions: Export __libc_dlvsym.
|
||||
* elf/dl-libc.c (struct do_dlvsym_args): New.
|
||||
(do_dlvsym, __libc_dlvsym): New functions.
|
||||
(struct dl_open_hook, _dl_open_hook): Add dlvsym member.
|
||||
(_dl_open_hook2): New variable.
|
||||
(__libc_register_dl_open_hook): Set it.
|
||||
* elf/tst-libc_dlvsym-dso.c: New file.
|
||||
* elf/tst-libc_dlvsym-static.c: Likewise.
|
||||
* elf/tst-libc_dlvsym.c: Likewise.
|
||||
* elf/tst-libc_dlvsym.h: Likewise.
|
||||
|
||||
2018-01-03 Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
|
||||
* support/support_enter_mount_namespace.c [!CLONE_NEWNS]: Do not
|
||||
|
16
elf/Makefile
16
elf/Makefile
@ -151,7 +151,7 @@ tests-static-normal := tst-leaks1-static tst-array1-static tst-array5-static \
|
||||
tst-linkall-static tst-env-setuid tst-env-setuid-tunables
|
||||
tests-static-internal := tst-tls1-static tst-tls2-static \
|
||||
tst-ptrguard1-static tst-stackguard1-static \
|
||||
tst-tls1-static-non-pie
|
||||
tst-tls1-static-non-pie tst-libc_dlvsym-static
|
||||
|
||||
CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
|
||||
tst-tls1-static-non-pie-no-pie = yes
|
||||
@ -192,7 +192,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
|
||||
tests-internal += loadtest unload unload2 circleload1 \
|
||||
neededtest neededtest2 neededtest3 neededtest4 \
|
||||
tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
|
||||
tst-ptrguard1 tst-stackguard1
|
||||
tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym
|
||||
ifeq ($(build-hardcoded-path-in-tests),yes)
|
||||
tests += tst-dlopen-aout
|
||||
tst-dlopen-aout-no-pie = yes
|
||||
@ -272,7 +272,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
|
||||
tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12 \
|
||||
tst-latepthreadmod $(tst-tls-many-dynamic-modules) \
|
||||
tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
|
||||
tst-main1mod
|
||||
tst-main1mod tst-libc_dlvsym-dso
|
||||
ifeq (yes,$(have-mtls-dialect-gnu2))
|
||||
tests += tst-gnu2-tls1
|
||||
modules-names += tst-gnu2-tls1mod
|
||||
@ -1436,3 +1436,13 @@ CRT-tst-main1 := $(csu-objpfx)crt1.o
|
||||
tst-main1-no-pie = yes
|
||||
LDLIBS-tst-main1 = $(libsupport)
|
||||
tst-main1mod.so-no-z-defs = yes
|
||||
|
||||
# Both the main program and the DSO for tst-libc_dlvsym need to link
|
||||
# against libdl.
|
||||
$(objpfx)tst-libc_dlvsym: $(libdl)
|
||||
$(objpfx)tst-libc_dlvsym-dso.so: $(libsupport) $(libdl)
|
||||
$(objpfx)tst-libc_dlvsym.out: $(objpfx)tst-libc_dlvsym-dso.so
|
||||
$(objpfx)tst-libc_dlvsym-static: $(common-objpfx)dlfcn/libdl.a
|
||||
tst-libc_dlvsym-static-ENV = \
|
||||
LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
|
||||
$(objpfx)tst-libc_dlvsym-static.out: $(objpfx)tst-libc_dlvsym-dso.so
|
||||
|
@ -23,9 +23,9 @@ libc {
|
||||
GLIBC_PRIVATE {
|
||||
# functions used in other libraries
|
||||
_dl_addr;
|
||||
_dl_open_hook;
|
||||
_dl_open_hook; _dl_open_hook2;
|
||||
_dl_sym; _dl_vsym;
|
||||
__libc_dlclose; __libc_dlopen_mode; __libc_dlsym;
|
||||
__libc_dlclose; __libc_dlopen_mode; __libc_dlsym; __libc_dlvsym;
|
||||
|
||||
# Internal error handling support. Interposes the functions in ld.so.
|
||||
_dl_signal_exception; _dl_catch_exception;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <dl-hash.h>
|
||||
|
||||
extern int __libc_argc attribute_hidden;
|
||||
extern char **__libc_argv attribute_hidden;
|
||||
@ -78,6 +79,15 @@ struct do_dlsym_args
|
||||
const ElfW(Sym) *ref;
|
||||
};
|
||||
|
||||
struct do_dlvsym_args
|
||||
{
|
||||
/* dlvsym is like dlsym. */
|
||||
struct do_dlsym_args dlsym;
|
||||
|
||||
/* But dlvsym needs a version as well. */
|
||||
struct r_found_version version;
|
||||
};
|
||||
|
||||
static void
|
||||
do_dlopen (void *ptr)
|
||||
{
|
||||
@ -98,6 +108,18 @@ do_dlsym (void *ptr)
|
||||
DL_LOOKUP_RETURN_NEWEST, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
do_dlvsym (void *ptr)
|
||||
{
|
||||
struct do_dlvsym_args *args = ptr;
|
||||
args->dlsym.ref = NULL;
|
||||
args->dlsym.loadbase
|
||||
= GLRO(dl_lookup_symbol_x) (args->dlsym.name, args->dlsym.map,
|
||||
&args->dlsym.ref,
|
||||
args->dlsym.map->l_local_scope,
|
||||
&args->version, 0, 0, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
do_dlclose (void *ptr)
|
||||
{
|
||||
@ -112,6 +134,7 @@ struct dl_open_hook
|
||||
void *(*dlopen_mode) (const char *name, int mode);
|
||||
void *(*dlsym) (void *map, const char *name);
|
||||
int (*dlclose) (void *map);
|
||||
void *(*dlvsym) (void *map, const char *name, const char *version);
|
||||
};
|
||||
|
||||
#ifdef SHARED
|
||||
@ -119,6 +142,15 @@ extern struct dl_open_hook *_dl_open_hook;
|
||||
libc_hidden_proto (_dl_open_hook);
|
||||
struct dl_open_hook *_dl_open_hook __attribute__ ((nocommon));
|
||||
libc_hidden_data_def (_dl_open_hook);
|
||||
|
||||
/* The dlvsym member was added retroactively to struct dl_open_hook.
|
||||
Static applications which have it will set _dl_open_hook2 in
|
||||
addition to _dl_open_hook. */
|
||||
extern struct dl_open_hook *_dl_open_hook2;
|
||||
libc_hidden_proto (_dl_open_hook2);
|
||||
struct dl_open_hook *_dl_open_hook2 __attribute__ ((nocommon));
|
||||
libc_hidden_data_def (_dl_open_hook2);
|
||||
|
||||
#else
|
||||
static void
|
||||
do_dlsym_private (void *ptr)
|
||||
@ -142,7 +174,8 @@ static struct dl_open_hook _dl_open_hook =
|
||||
{
|
||||
.dlopen_mode = __libc_dlopen_mode,
|
||||
.dlsym = __libc_dlsym,
|
||||
.dlclose = __libc_dlclose
|
||||
.dlclose = __libc_dlclose,
|
||||
.dlvsym = __libc_dlvsym,
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -192,6 +225,11 @@ __libc_register_dl_open_hook (struct link_map *map)
|
||||
hook = (struct dl_open_hook **) __libc_dlsym_private (map, "_dl_open_hook");
|
||||
if (hook != NULL)
|
||||
*hook = &_dl_open_hook;
|
||||
|
||||
/* For dlvsym support. */
|
||||
hook = (struct dl_open_hook **) __libc_dlsym_private (map, "_dl_open_hook2");
|
||||
if (hook != NULL)
|
||||
*hook = &_dl_open_hook;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -211,6 +249,39 @@ __libc_dlsym (void *map, const char *name)
|
||||
}
|
||||
libc_hidden_def (__libc_dlsym)
|
||||
|
||||
/* Replacement for dlvsym. MAP must be a real map. This function
|
||||
returns NULL without setting the dlerror value in case of static
|
||||
dlopen from an old binary. */
|
||||
void *
|
||||
__libc_dlvsym (void *map, const char *name, const char *version)
|
||||
{
|
||||
#ifdef SHARED
|
||||
if (!rtld_active ())
|
||||
{
|
||||
/* The static application is too old and does not provide the
|
||||
dlvsym hook. */
|
||||
if (_dl_open_hook2 == NULL)
|
||||
return NULL;
|
||||
return _dl_open_hook2->dlvsym (map, name, version);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct do_dlvsym_args args;
|
||||
args.dlsym.map = map;
|
||||
args.dlsym.name = name;
|
||||
|
||||
/* See _dl_vsym in dl-sym.c. */
|
||||
args.version.name = version;
|
||||
args.version.hidden = 1;
|
||||
args.version.hash = _dl_elf_hash (version);
|
||||
args.version.filename = NULL;
|
||||
|
||||
return (dlerror_run (do_dlvsym, &args) ? NULL
|
||||
: (void *) (DL_SYMBOL_ADDRESS (args.dlsym.loadbase,
|
||||
args.dlsym.ref)));
|
||||
}
|
||||
libc_hidden_def (__libc_dlvsym)
|
||||
|
||||
int
|
||||
__libc_dlclose (void *map)
|
||||
{
|
||||
|
25
elf/tst-libc_dlvsym-dso.c
Normal file
25
elf/tst-libc_dlvsym-dso.c
Normal file
@ -0,0 +1,25 @@
|
||||
/* Compare dlvsym and __libc_dlvsym results. Shared object 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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "tst-libc_dlvsym.h"
|
||||
|
||||
void
|
||||
compare_vsyms_global (void)
|
||||
{
|
||||
compare_vsyms ();
|
||||
}
|
32
elf/tst-libc_dlvsym-static.c
Normal file
32
elf/tst-libc_dlvsym-static.c
Normal file
@ -0,0 +1,32 @@
|
||||
/* Compare dlvsym and __libc_dlvsym results. Static version.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <support/xdlfcn.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
void *handle = xdlopen ("tst-libc_dlvsym-dso.so", RTLD_LAZY);
|
||||
void (*compare) (void) = xdlsym (handle, "compare_vsyms_global");
|
||||
compare ();
|
||||
xdlclose (handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
34
elf/tst-libc_dlvsym.c
Normal file
34
elf/tst-libc_dlvsym.c
Normal file
@ -0,0 +1,34 @@
|
||||
/* Compare dlvsym and __libc_dlvsym results. Dynamic version.
|
||||
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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "tst-libc_dlvsym.h"
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
compare_vsyms ();
|
||||
|
||||
void *handle = xdlopen ("tst-libc_dlvsym-dso.so", RTLD_LAZY);
|
||||
void (*compare) (void) = xdlsym (handle, "compare_vsyms_global");
|
||||
compare ();
|
||||
xdlclose (handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
125
elf/tst-libc_dlvsym.h
Normal file
125
elf/tst-libc_dlvsym.h
Normal file
@ -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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* 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 <array_length.h>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <support/check.h>
|
||||
#include <support/xdlfcn.h>
|
||||
|
||||
/* 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);
|
||||
}
|
@ -35,9 +35,11 @@ extern char **__libc_argv attribute_hidden;
|
||||
__libc_dlopen_mode (name, RTLD_LAZY | __RTLD_DLOPEN)
|
||||
extern void *__libc_dlopen_mode (const char *__name, int __mode);
|
||||
extern void *__libc_dlsym (void *__map, const char *__name);
|
||||
extern void *__libc_dlvsym (void *map, const char *name, const char *version);
|
||||
extern int __libc_dlclose (void *__map);
|
||||
libc_hidden_proto (__libc_dlopen_mode)
|
||||
libc_hidden_proto (__libc_dlsym)
|
||||
libc_hidden_proto (__libc_dlvsym)
|
||||
libc_hidden_proto (__libc_dlclose)
|
||||
|
||||
/* Locate shared object containing the given address. */
|
||||
|
Loading…
Reference in New Issue
Block a user