Implement _dl_catch_error, _dl_signal_error in libc.so [BZ #16628]

This change moves the main implementation of _dl_catch_error,
_dl_signal_error to libc.so, where TLS variables can be used
directly.  This removes a writable function pointer from the
rtld_global variable.

For use during initial relocation, minimal implementations of these
functions are provided in ld.so.  These are eventually interposed
by the libc.so implementations.  This is implemented by compiling
elf/dl-error-skeleton.c twice, via elf/dl-error.c and
elf/dl-error-minimal.c.

As a side effect of this change, the static version of dl-error.c
no longer includes support for the
_dl_signal_cerror/_dl_receive_error mechanism because it is only
used in ld.so.
This commit is contained in:
Florian Weimer 2016-11-30 15:59:57 +01:00
parent 705a79f825
commit 9e78f6f6e7
37 changed files with 595 additions and 315 deletions

View File

@ -1,3 +1,81 @@
2016-11-30 Florian Weimer <fweimer@redhat.com>
[BZ #16628]
Implement _dl_catch_error, _dl_signal_error in libc.so.
* elf/dl-error-skeleton.c: Rename from elf/dl-error.c.
(catch_hook): Define as thread-local or regular variable,
depending on DL_ERROR_BOOTSTRAP.
(CATCH_HOOK): Remove.
(dl_signal_error, _dl_catch_error): Use
catch_hook. Add hidden definition for libc.
(_dl_receive_error, _dl_signal_cerror): Use catch_hook. Compile
for DL_ERROR_BOOTSTRAP only.
* elf/dl-error.c: New file.
* elf/dl-error-minimal.c: Likewise.
* elf/tst-latepthread.c: Likewise.
* elf/tst-latepthreadmod.c: Likewise.
* elf/Makefile (routines): Add dl-error.
(dl-routines): Remove dl-error.
(rtld-routines): Add dl-error-minimal.
[build-shared] (tests): Add tst-latepthread.
(module-names): Add tst-latepthreadmod.
(LDFLAGS-tst-latepthreadmod.so): Enable lazy binding to undefined
symbol.
(tst-latepthreadmod.so): Link against libpthread.
(tst-latepthread): Link against libdbl.
* elf/Versions [libc] (GLIBC_PRIVATE): Add _dl_signal_error,
_dl_catch_error.
[ld] (GLIBC_PRIVATE): Likewise.
* elf/dl-close.c (_dl_cose): Call _dl_signal_error directly.
* elf/dl-libc.c (dlerror_run): Call _dl_catch_error directly.
* elf/dl-sym.c (do_sym): Call _dl_signal_error, _dl_catch_error
directly.
* elf/dl-tsd.c: Remove file.
* elf/rtld.c (_rtld_global_ro): Remove initializers for
_dl_catch_error, _dl_signal_error.
(_dl_initial_error_catch_tsd): Remove definition.
(do_preload): Remove initialization of dl_error_catch_tsd.
* dlfcn/dlerror.c (_dlerror_run): Call _dl_catch_error directly.
* dlfcn/dlinfo.c (dlinfo_doit): Call _dl_signal_error directly.
* dlfcn/dlmopen.c (dlmopen_doit): Likewise.
* dlfcn/dlopen.c (dlopen_doit): Likewise.
* nptl/nptl-init.c (__pthread_initialize_minimal_internal): Do not
set dl_error_catch_tsd.
* sysdeps/generic/ldsodefs.h (struct rtld_global): Remove
_dl_error_catch_tsd member.
(struct rtld_global_ro): Remove _dl_catch_error, _dl_signal_error
members.
(_dl_initial_error_catch_tsd): Remove declaration.
(_dl_dprintf): Provide definition for use outside of ld.so.
[!rtld] (_dl_signal_cerror): Redirect to _dl_signal_error.
(_dl_signal_error, _dl_catch_error): Make public. Add hidden
prototype for libc.
* sysdeps/generic/localplt.data (ld.so): Add _dl_signal_error,
_dl_catch_error.
* sysdeps/unix/sysv/linux/aarch64/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/alpha/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/arm/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/hppa/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/i386/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/ia64/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/m68k/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/microblaze/localplt.data (ld.so):
Likewise.
* sysdeps/unix/sysv/linux/nios2/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
(ld.so): Likewise.
* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
(ld.so): Likewise.
* sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data (ld.so):
Likewise.
* sysdeps/unix/sysv/linux/s390/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/sh/localplt.data (ld.so): Likewise.
* sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data (ld.so):
Likewise.
* sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data (ld.so):
Likewise.
* sysdeps/x86_64/localplt.data (ld.so): Likewise.
2016-11-30 Florian Weimer <fweimer@redhat.com>
[BZ #4099]

View File

@ -160,8 +160,8 @@ _dlerror_run (void (*operate) (void *), void *args)
result->errstring = NULL;
}
result->errcode = GLRO(dl_catch_error) (&result->objname, &result->errstring,
&result->malloced, operate, args);
result->errcode = _dl_catch_error (&result->objname, &result->errstring,
&result->malloced, operate, args);
/* If no error we mark that no error string is available. */
result->returned = result->errstring == NULL;

View File

@ -60,7 +60,7 @@ dlinfo_doit (void *argsblock)
break;
if (l == NULL)
GLRO(dl_signal_error) (0, NULL, NULL, N_("\
_dl_signal_error (0, NULL, NULL, N_("\
RTLD_SELF used in code not dynamically loaded"));
}
# endif
@ -69,7 +69,7 @@ RTLD_SELF used in code not dynamically loaded"));
{
case RTLD_DI_CONFIGADDR:
default:
GLRO(dl_signal_error) (0, NULL, NULL, N_("unsupported dlinfo request"));
_dl_signal_error (0, NULL, NULL, N_("unsupported dlinfo request"));
break;
case RTLD_DI_LMID:

View File

@ -60,12 +60,12 @@ dlmopen_doit (void *a)
must be the main one. */
if (args->file == NULL)
# endif
GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid namespace"));
_dl_signal_error (EINVAL, NULL, NULL, N_("invalid namespace"));
/* It makes no sense to use RTLD_GLOBAL when loading a DSO into
a namespace other than the base namespace. */
if (__glibc_unlikely (args->mode & RTLD_GLOBAL))
GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid mode"));
_dl_signal_error (EINVAL, NULL, NULL, N_("invalid mode"));
}
args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN,

View File

@ -61,7 +61,7 @@ dlopen_doit (void *a)
if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND
| RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE
| __RTLD_SPROF))
GLRO(dl_signal_error) (0, NULL, NULL, _("invalid mode parameter"));
_dl_signal_error (0, NULL, NULL, _("invalid mode parameter"));
args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN,
args->caller,

View File

@ -24,12 +24,12 @@ include ../Makeconfig
headers = elf.h bits/elfclass.h link.h bits/link.h
routines = $(all-dl-routines) dl-support dl-iteratephdr \
dl-addr dl-addr-obj enbl-secure dl-profstub \
dl-origin dl-libc dl-sym dl-tsd dl-sysdep
dl-origin dl-libc dl-sym dl-sysdep dl-error
# The core dynamic linking functions are in libc for the static and
# profiled libraries.
dl-routines = $(addprefix dl-,load lookup object reloc deps hwcaps \
runtime error init fini debug misc \
runtime init fini debug misc \
version profile conflict tls origin scope \
execstack caller open close trampoline)
ifeq (yes,$(use-ldconfig))
@ -43,7 +43,8 @@ shared-only-routines += dl-caller
# ld.so uses those routines, plus some special stuff for being the program
# interpreter and operating independent of libc.
rtld-routines = rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal
rtld-routines = rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \
dl-error-minimal
all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables
@ -150,7 +151,8 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
tst-nodelete) \
tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \
tst-ptrguard1 tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \
tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload
tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
tst-latepthread
# reldep9
ifeq ($(build-hardcoded-path-in-tests),yes)
tests += tst-dlopen-aout
@ -224,7 +226,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
tst-array5dep tst-null-argv-lib \
tst-tlsalign-lib tst-nodelete-opened-lib tst-nodelete2mod \
tst-audit11mod1 tst-audit11mod2 tst-auditmod11 \
tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12
tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12 \
tst-latepthreadmod
ifeq (yes,$(have-mtls-dialect-gnu2))
tests += tst-gnu2-tls1
modules-names += tst-gnu2-tls1mod
@ -1264,6 +1267,14 @@ tst-audit12-ENV = LD_AUDIT=$(objpfx)tst-auditmod12.so
$(objpfx)tst-audit12mod1.so: $(objpfx)tst-audit12mod2.so
LDFLAGS-tst-audit12mod2.so = -Wl,--version-script=tst-audit12mod2.map
# Override -z defs, so that we can reference an undefined symbol.
# Force lazy binding for the same reason.
LDFLAGS-tst-latepthreadmod.so = \
-Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
$(objpfx)tst-latepthreadmod.so: $(shared-thread-library)
$(objpfx)tst-latepthread: $(libdl)
$(objpfx)tst-latepthread.out: $(objpfx)tst-latepthreadmod.so
tst-prelink-ENV = LD_TRACE_PRELINKING=1
$(objpfx)tst-prelink-conflict.out: $(objpfx)tst-prelink.out

View File

@ -26,6 +26,9 @@ libc {
_dl_open_hook;
_dl_sym; _dl_vsym;
__libc_dlclose; __libc_dlopen_mode; __libc_dlsym;
# Internal error handling support. Interposes the functions in ld.so.
_dl_signal_error; _dl_catch_error;
}
}
@ -64,5 +67,8 @@ ld {
# Pointer protection.
__pointer_chk_guard;
# Internal error handling support. Interposed by libc.so.
_dl_signal_error; _dl_catch_error;
}
}

View File

@ -814,7 +814,7 @@ _dl_close (void *_map)
}
if (__builtin_expect (map->l_direct_opencount, 1) == 0)
GLRO(dl_signal_error) (0, map->l_name, NULL, N_("shared object not open"));
_dl_signal_error (0, map->l_name, NULL, N_("shared object not open"));
/* Acquire the lock. */
__rtld_lock_lock_recursive (GL(dl_load_lock));

23
elf/dl-error-minimal.c Normal file
View File

@ -0,0 +1,23 @@
/* Error handling for runtime dynamic linker, minimal version.
Copyright (C) 1995-2016 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/>. */
/* This version does lives in ld.so, does not use thread-local data
and supports _dl_signal_cerror and _dl_receive_error. */
#define DL_ERROR_BOOTSTRAP 1
#include "dl-error-skeleton.c"

230
elf/dl-error-skeleton.c Normal file
View File

@ -0,0 +1,230 @@
/* Template for error handling for runtime dynamic linker.
Copyright (C) 1995-2016 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/>. */
/* The following macro needs to be defined before including this
skeleton file:
DL_ERROR_BOOTSTRAP
If 1, do not use TLS and implement _dl_signal_cerror and
_dl_receive_error. If 0, TLS is used, and the variants with
error callbacks are not provided. */
#include <libintl.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ldsodefs.h>
#include <stdio.h>
/* This structure communicates state between _dl_catch_error and
_dl_signal_error. */
struct catch
{
const char **objname; /* Object/File name. */
const char **errstring; /* Error detail filled in here. */
bool *malloced; /* Nonzero if the string is malloced
by the libc malloc. */
volatile int *errcode; /* Return value of _dl_signal_error. */
jmp_buf env; /* longjmp here on error. */
};
/* Multiple threads at once can use the `_dl_catch_error' function. The
calls can come from `_dl_map_object_deps', `_dlerror_run', or from
any of the libc functionality which loads dynamic objects (NSS, iconv).
Therefore we have to be prepared to save the state in thread-local
memory. */
#if !DL_ERROR_BOOTSTRAP
static __thread struct catch *catch_hook attribute_tls_model_ie;
#else
/* The version of this code in ld.so cannot use thread-local variables
and is used during bootstrap only. */
static struct catch *catch_hook;
#endif
/* This message we return as a last resort. We define the string in a
variable since we have to avoid freeing it and so have to enable
a pointer comparison. See below and in dlfcn/dlerror.c. */
static const char _dl_out_of_memory[] = "out of memory";
#if DL_ERROR_BOOTSTRAP
/* This points to a function which is called when an continuable error is
received. Unlike the handling of `catch' this function may return.
The arguments will be the `errstring' and `objname'.
Since this functionality is not used in normal programs (only in ld.so)
we do not care about multi-threaded programs here. We keep this as a
global variable. */
static receiver_fct receiver;
#endif /* DL_ERROR_BOOTSTRAP */
void
internal_function
_dl_signal_error (int errcode, const char *objname, const char *occation,
const char *errstring)
{
struct catch *lcatch = catch_hook;
if (! errstring)
errstring = N_("DYNAMIC LINKER BUG!!!");
if (objname == NULL)
objname = "";
if (lcatch != NULL)
{
/* We are inside _dl_catch_error. Return to it. We have to
duplicate the error string since it might be allocated on the
stack. The object name is always a string constant. */
size_t len_objname = strlen (objname) + 1;
size_t len_errstring = strlen (errstring) + 1;
char *errstring_copy = malloc (len_objname + len_errstring);
if (errstring_copy != NULL)
{
/* Make a copy of the object file name and the error string. */
*lcatch->objname = memcpy (__mempcpy (errstring_copy,
errstring, len_errstring),
objname, len_objname);
*lcatch->errstring = errstring_copy;
/* If the main executable is relocated it means the libc's malloc
is used. */
bool malloced = true;
#ifdef SHARED
malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL
&& (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated != 0));
#endif
*lcatch->malloced = malloced;
}
else
{
/* This is better than nothing. */
*lcatch->objname = "";
*lcatch->errstring = _dl_out_of_memory;
*lcatch->malloced = false;
}
*lcatch->errcode = errcode;
/* We do not restore the signal mask because none was saved. */
__longjmp (lcatch->env[0].__jmpbuf, 1);
}
else
{
/* Lossage while resolving the program's own symbols is always fatal. */
char buffer[1024];
_dl_fatal_printf ("%s: %s: %s%s%s%s%s\n",
RTLD_PROGNAME,
occation ?: N_("error while loading shared libraries"),
objname, *objname ? ": " : "",
errstring, errcode ? ": " : "",
(errcode
? __strerror_r (errcode, buffer, sizeof buffer)
: ""));
}
}
libc_hidden_def (_dl_signal_error)
#if DL_ERROR_BOOTSTRAP
void
internal_function
_dl_signal_cerror (int errcode, const char *objname, const char *occation,
const char *errstring)
{
if (__builtin_expect (GLRO(dl_debug_mask)
& ~(DL_DEBUG_STATISTICS|DL_DEBUG_PRELINK), 0))
_dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occation,
errstring, receiver ? "continued" : "fatal");
if (receiver)
{
/* We are inside _dl_receive_error. Call the user supplied
handler and resume the work. The receiver will still be
installed. */
(*receiver) (errcode, objname, errstring);
}
else
_dl_signal_error (errcode, objname, occation, errstring);
}
#endif /* DL_ERROR_BOOTSTRAP */
int
internal_function
_dl_catch_error (const char **objname, const char **errstring,
bool *mallocedp, void (*operate) (void *), void *args)
{
/* We need not handle `receiver' since setting a `catch' is handled
before it. */
/* Only this needs to be marked volatile, because it is the only local
variable that gets changed between the setjmp invocation and the
longjmp call. All others are just set here (before setjmp) and read
in _dl_signal_error (before longjmp). */
volatile int errcode;
struct catch c;
/* Don't use an initializer since we don't need to clear C.env. */
c.objname = objname;
c.errstring = errstring;
c.malloced = mallocedp;
c.errcode = &errcode;
struct catch *const old = catch_hook;
catch_hook = &c;
/* Do not save the signal mask. */
if (__builtin_expect (__sigsetjmp (c.env, 0), 0) == 0)
{
(*operate) (args);
catch_hook = old;
*objname = NULL;
*errstring = NULL;
*mallocedp = false;
return 0;
}
/* We get here only if we longjmp'd out of OPERATE. _dl_signal_error has
already stored values into *OBJNAME, *ERRSTRING, and *MALLOCEDP. */
catch_hook = old;
return errcode;
}
libc_hidden_def (_dl_catch_error)
#if DL_ERROR_BOOTSTRAP
void
internal_function
_dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
{
struct catch *old_catch = catch_hook;
receiver_fct old_receiver = receiver;
/* Set the new values. */
catch_hook = NULL;
receiver = fct;
(*operate) (args);
catch_hook = old_catch;
receiver = old_receiver;
}
#endif /* DL_ERROR_BOOTSTRAP */

View File

@ -1,4 +1,4 @@
/* Error handling for runtime dynamic linker.
/* Error handling for runtime dynamic linker, full version.
Copyright (C) 1995-2016 Free Software Foundation, Inc.
This file is part of the GNU C Library.
@ -16,206 +16,12 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <libintl.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ldsodefs.h>
/* This implementation lives in libc.so because it uses thread-local
data, which is not available in ld.so. It interposes the version
in dl-error-minimal.c after ld.so bootstrap.
/* This structure communicates state between _dl_catch_error and
_dl_signal_error. */
struct catch
{
const char **objname; /* Object/File name. */
const char **errstring; /* Error detail filled in here. */
bool *malloced; /* Nonzero if the string is malloced
by the libc malloc. */
volatile int *errcode; /* Return value of _dl_signal_error. */
jmp_buf env; /* longjmp here on error. */
};
The signal/catch mechanism is used by the audit framework, which
means that even in ld.so, not all errors are fatal. */
/* Multiple threads at once can use the `_dl_catch_error' function. The
calls can come from `_dl_map_object_deps', `_dlerror_run', or from
any of the libc functionality which loads dynamic objects (NSS, iconv).
Therefore we have to be prepared to save the state in thread-local
memory. The _dl_error_catch_tsd function pointer is reset by the thread
library so that it returns the address of a thread-local variable. */
/* This message we return as a last resort. We define the string in a
variable since we have to avoid freeing it and so have to enable
a pointer comparison. See below and in dlfcn/dlerror.c. */
static const char _dl_out_of_memory[] = "out of memory";
/* This points to a function which is called when an continuable error is
received. Unlike the handling of `catch' this function may return.
The arguments will be the `errstring' and `objname'.
Since this functionality is not used in normal programs (only in ld.so)
we do not care about multi-threaded programs here. We keep this as a
global variable. */
static receiver_fct receiver;
#ifdef _LIBC_REENTRANT
# define CATCH_HOOK (*(struct catch **) (*GL(dl_error_catch_tsd)) ())
#else
static struct catch *catch_hook;
# define CATCH_HOOK catch_hook
#endif
void
internal_function
_dl_signal_error (int errcode, const char *objname, const char *occation,
const char *errstring)
{
struct catch *lcatch;
if (! errstring)
errstring = N_("DYNAMIC LINKER BUG!!!");
lcatch = CATCH_HOOK;
if (objname == NULL)
objname = "";
if (lcatch != NULL)
{
/* We are inside _dl_catch_error. Return to it. We have to
duplicate the error string since it might be allocated on the
stack. The object name is always a string constant. */
size_t len_objname = strlen (objname) + 1;
size_t len_errstring = strlen (errstring) + 1;
char *errstring_copy = malloc (len_objname + len_errstring);
if (errstring_copy != NULL)
{
/* Make a copy of the object file name and the error string. */
*lcatch->objname = memcpy (__mempcpy (errstring_copy,
errstring, len_errstring),
objname, len_objname);
*lcatch->errstring = errstring_copy;
/* If the main executable is relocated it means the libc's malloc
is used. */
bool malloced = true;
#ifdef SHARED
malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL
&& (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated != 0));
#endif
*lcatch->malloced = malloced;
}
else
{
/* This is better than nothing. */
*lcatch->objname = "";
*lcatch->errstring = _dl_out_of_memory;
*lcatch->malloced = false;
}
*lcatch->errcode = errcode;
/* We do not restore the signal mask because none was saved. */
__longjmp (lcatch->env[0].__jmpbuf, 1);
}
else
{
/* Lossage while resolving the program's own symbols is always fatal. */
char buffer[1024];
_dl_fatal_printf ("%s: %s: %s%s%s%s%s\n",
RTLD_PROGNAME,
occation ?: N_("error while loading shared libraries"),
objname, *objname ? ": " : "",
errstring, errcode ? ": " : "",
(errcode
? __strerror_r (errcode, buffer, sizeof buffer)
: ""));
}
}
void
internal_function
_dl_signal_cerror (int errcode, const char *objname, const char *occation,
const char *errstring)
{
if (__builtin_expect (GLRO(dl_debug_mask)
& ~(DL_DEBUG_STATISTICS|DL_DEBUG_PRELINK), 0))
_dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occation,
errstring, receiver ? "continued" : "fatal");
if (receiver)
{
/* We are inside _dl_receive_error. Call the user supplied
handler and resume the work. The receiver will still be
installed. */
(*receiver) (errcode, objname, errstring);
}
else
_dl_signal_error (errcode, objname, occation, errstring);
}
int
internal_function
_dl_catch_error (const char **objname, const char **errstring,
bool *mallocedp, void (*operate) (void *), void *args)
{
/* We need not handle `receiver' since setting a `catch' is handled
before it. */
/* Only this needs to be marked volatile, because it is the only local
variable that gets changed between the setjmp invocation and the
longjmp call. All others are just set here (before setjmp) and read
in _dl_signal_error (before longjmp). */
volatile int errcode;
struct catch c;
/* Don't use an initializer since we don't need to clear C.env. */
c.objname = objname;
c.errstring = errstring;
c.malloced = mallocedp;
c.errcode = &errcode;
struct catch **const catchp = &CATCH_HOOK;
struct catch *const old = *catchp;
*catchp = &c;
/* Do not save the signal mask. */
if (__builtin_expect (__sigsetjmp (c.env, 0), 0) == 0)
{
(*operate) (args);
*catchp = old;
*objname = NULL;
*errstring = NULL;
*mallocedp = false;
return 0;
}
/* We get here only if we longjmp'd out of OPERATE. _dl_signal_error has
already stored values into *OBJNAME, *ERRSTRING, and *MALLOCEDP. */
*catchp = old;
return errcode;
}
void
internal_function
_dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
{
struct catch **const catchp = &CATCH_HOOK;
struct catch *old_catch;
receiver_fct old_receiver;
old_catch = *catchp;
old_receiver = receiver;
/* Set the new values. */
*catchp = NULL;
receiver = fct;
(*operate) (args);
*catchp = old_catch;
receiver = old_receiver;
}
#define DL_ERROR_BOOTSTRAP 0
#include "dl-error-skeleton.c"

View File

@ -43,8 +43,8 @@ dlerror_run (void (*operate) (void *), void *args)
const char *last_errstring = NULL;
bool malloced;
int result = (GLRO(dl_catch_error) (&objname, &last_errstring, &malloced,
operate, args)
int result = (_dl_catch_error (&objname, &last_errstring, &malloced,
operate, args)
?: last_errstring != NULL);
if (result && malloced)

View File

@ -123,8 +123,8 @@ do_sym (void *handle, const char *name, void *who,
const char *objname;
const char *errstring = NULL;
bool malloced;
int err = GLRO(dl_catch_error) (&objname, &errstring, &malloced,
call_dl_lookup, &args);
int err = _dl_catch_error (&objname, &errstring, &malloced,
call_dl_lookup, &args);
THREAD_GSCOPE_RESET_FLAG ();
@ -136,7 +136,7 @@ do_sym (void *handle, const char *name, void *who,
if (malloced)
free ((char *) errstring);
GLRO(dl_signal_error) (err, objname_dup, NULL, errstring_dup);
_dl_signal_error (err, objname_dup, NULL, errstring_dup);
/* NOTREACHED */
}
@ -150,7 +150,7 @@ do_sym (void *handle, const char *name, void *who,
if (match == NULL
|| caller < match->l_map_start
|| caller >= match->l_map_end)
GLRO(dl_signal_error) (0, NULL, NULL, N_("\
_dl_signal_error (0, NULL, NULL, N_("\
RTLD_NEXT used in code not dynamically loaded"));
}

View File

@ -1,53 +0,0 @@
/* Thread-local data used by error handling for runtime dynamic linker.
Copyright (C) 2002-2016 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/>. */
#ifdef _LIBC_REENTRANT
# include <ldsodefs.h>
# include <tls.h>
# ifndef SHARED
/* _dl_error_catch_tsd points to this for the single-threaded case.
It's reset by the thread library for multithreaded programs
if we're not using __thread. */
void ** __attribute__ ((const))
_dl_initial_error_catch_tsd (void)
{
static __thread void *data;
return &data;
}
void **(*_dl_error_catch_tsd) (void) __attribute__ ((const))
= &_dl_initial_error_catch_tsd;
# else
/* libpthread sets _dl_error_catch_tsd to point to this function.
We define it here instead of in libpthread so that it doesn't
need to have a TLS segment of its own just for this one pointer. */
void ** __attribute__ ((const))
__libc_dl_error_tsd (void)
{
static __thread void *data attribute_tls_model_ie;
return &data;
}
# endif /* SHARED */
#endif /* _LIBC_REENTRANT */

View File

@ -167,8 +167,6 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
/* Function pointers. */
._dl_debug_printf = _dl_debug_printf,
._dl_catch_error = _dl_catch_error,
._dl_signal_error = _dl_signal_error,
._dl_mcount = _dl_mcount,
._dl_lookup_symbol_x = _dl_lookup_symbol_x,
._dl_check_caller = _dl_check_caller,
@ -637,18 +635,6 @@ cannot allocate TLS data structures for initial thread");
return tcbp;
}
#ifdef _LIBC_REENTRANT
/* _dl_error_catch_tsd points to this for the single-threaded case.
It's reset by the thread library for multithreaded programs. */
void ** __attribute__ ((const))
_dl_initial_error_catch_tsd (void)
{
static void *data;
return &data;
}
#endif
static unsigned int
do_preload (const char *fname, struct link_map *main_map, const char *where)
{
@ -752,11 +738,6 @@ dl_main (const ElfW(Phdr) *phdr,
#endif
void *tcbp = NULL;
#ifdef _LIBC_REENTRANT
/* Explicit initialization since the reloc would just be more work. */
GL(dl_error_catch_tsd) = &_dl_initial_error_catch_tsd;
#endif
GL(dl_init_static_tls) = &_dl_nothread_init_static_tls;
#if defined SHARED && defined _LIBC_REENTRANT \

105
elf/tst-latepthread.c Normal file
View File

@ -0,0 +1,105 @@
/* Test that loading libpthread does not break ld.so exceptions (bug 16628).
Copyright (C) 2016 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 <dlfcn.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static int
do_test (void)
{
void *handle = dlopen ("tst-latepthreadmod.so", RTLD_LOCAL | RTLD_LAZY);
if (handle == NULL)
{
printf ("error: dlopen failed: %s\n", dlerror ());
return 1;
}
void *ptr = dlsym (handle, "trigger_dynlink_failure");
if (ptr == NULL)
{
printf ("error: dlsym failed: %s\n", dlerror ());
return 1;
}
int (*func) (void) = ptr;
/* Run the actual test in a subprocess, to capture the error. */
int fds[2];
if (pipe (fds) < 0)
{
printf ("error: pipe: %m\n");
return 1;
}
pid_t pid = fork ();
if (pid < 0)
{
printf ("error: fork: %m\n");
return 1;
}
else if (pid == 0)
{
if (dup2 (fds[1], STDERR_FILENO) < 0)
_exit (2);
/* Trigger an abort. */
func ();
_exit (3);
}
/* NB: This assumes that the abort message is so short that the pipe
does not block. */
int status;
if (waitpid (pid, &status, 0) < 0)
{
printf ("error: waitpid: %m\n");
return 1;
}
/* Check the printed error message. */
if (close (fds[1]) < 0)
{
printf ("error: close: %m\n");
return 1;
}
char buf[512];
/* Leave room for the NUL terminator. */
ssize_t ret = read (fds[0], buf, sizeof (buf) - 1);
if (ret < 0)
{
printf ("error: read: %m\n");
return 1;
}
if (ret > 0 && buf[ret - 1] == '\n')
--ret;
buf[ret] = '\0';
printf ("info: exit status: %d, message: %s\n", status, buf);
if (strstr (buf, "undefined symbol: this_function_is_not_defined") == NULL)
{
printf ("error: message does not contain expected string\n");
return 1;
}
if (!WIFEXITED (status) || WEXITSTATUS (status) != 127)
{
printf ("error: unexpected process exit status\n");
return 1;
}
return 0;
}
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"

33
elf/tst-latepthreadmod.c Normal file
View File

@ -0,0 +1,33 @@
/* DSO which links against libpthread and triggers a lazy binding.
Copyright (C) 2016 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/>. */
/* This file is compiled into a DSO which loads libpthread, but fails
the dynamic linker afterwards. */
#include <pthread.h>
/* Link in libpthread. */
void *pthread_create_ptr = &pthread_create;
int this_function_is_not_defined (void);
int
trigger_dynlink_failure (void)
{
return this_function_is_not_defined ();
}

View File

@ -456,10 +456,6 @@ __pthread_initialize_minimal_internal (void)
lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE);
#ifdef SHARED
/* Transfer the old value from the dynamic linker's internal location. */
*__libc_dl_error_tsd () = *(*GL(dl_error_catch_tsd)) ();
GL(dl_error_catch_tsd) = &__libc_dl_error_tsd;
/* Make __rtld_lock_{,un}lock_recursive use pthread_mutex_{,un}lock,
keep the lock count from the ld.so implementation. */
GL(dl_rtld_lock_recursive) = (void *) __pthread_mutex_lock;

View File

@ -357,10 +357,6 @@ struct rtld_global
/* List of search directories. */
EXTERN struct r_search_path_elem *_dl_all_dirs;
#ifdef _LIBC_REENTRANT
EXTERN void **(*_dl_error_catch_tsd) (void) __attribute__ ((const));
#endif
/* Structure describing the dynamic linker itself. We need to
reserve memory for the data the audit libraries need. */
EXTERN struct link_map _dl_rtld_map;
@ -583,10 +579,6 @@ struct rtld_global_ro
PLT relocations in libc.so. */
void (*_dl_debug_printf) (const char *, ...)
__attribute__ ((__format__ (__printf__, 1, 2)));
int (internal_function *_dl_catch_error) (const char **, const char **,
bool *, void (*) (void *), void *);
void (internal_function *_dl_signal_error) (int, const char *, const char *,
const char *);
void (*_dl_mcount) (ElfW(Addr) frompc, ElfW(Addr) selfpc);
lookup_t (internal_function *_dl_lookup_symbol_x) (const char *,
struct link_map *,
@ -632,13 +624,6 @@ extern const ElfW(Phdr) *_dl_phdr;
extern size_t _dl_phnum;
#endif
#if IS_IN (rtld)
/* This is the initial value of GL(dl_error_catch_tsd).
A non-TLS libpthread will change it. */
extern void **_dl_initial_error_catch_tsd (void) __attribute__ ((const))
attribute_hidden;
#endif
/* This is the initial value of GL(dl_make_stack_executable_hook).
A threads library can change it. */
extern int _dl_make_stack_executable (void **stack_endp) internal_function;
@ -705,9 +690,20 @@ extern void _dl_debug_printf_c (const char *fmt, ...)
/* Write a message on the specified descriptor FD. The parameters are
interpreted as for a `printf' call. */
#if IS_IN (rtld) || !defined (SHARED)
extern void _dl_dprintf (int fd, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 2, 3)))
attribute_hidden;
#else
__attribute__ ((always_inline, __format__ (__printf__, 2, 3)))
static inline void
_dl_dprintf (int fd, const char *fmt, ...)
{
/* Use local declaration to avoid includign <stdio.h>. */
extern int __dprintf(int fd, const char *format, ...) attribute_hidden;
__dprintf (fd, fmt, __builtin_va_arg_pack ());
}
#endif
/* Write a message on the specified descriptor standard output. The
parameters are interpreted as for a `printf' call. */
@ -737,13 +733,26 @@ extern void _dl_dprintf (int fd, const char *fmt, ...)
problem. */
extern void _dl_signal_error (int errcode, const char *object,
const char *occurred, const char *errstring)
internal_function __attribute__ ((__noreturn__)) attribute_hidden;
internal_function __attribute__ ((__noreturn__));
libc_hidden_proto (_dl_signal_error)
/* Like _dl_signal_error, but may return when called in the context of
_dl_receive_error. */
_dl_receive_error. This is only used during ld.so bootstrap. In
static and profiled builds, this is equivalent to
_dl_signal_error. */
#if IS_IN (rtld)
extern void _dl_signal_cerror (int errcode, const char *object,
const char *occation, const char *errstring)
internal_function attribute_hidden;
#else
__attribute__ ((always_inline))
static inline void
_dl_signal_cerror (int errcode, const char *object,
const char *occation, const char *errstring)
{
_dl_signal_error (errcode, object, occation, errstring);
}
#endif
/* Call OPERATE, receiving errors from `dl_signal_cerror'. Unlike
`_dl_catch_error' the operation is resumed after the OPERATE
@ -764,7 +773,8 @@ extern void _dl_receive_error (receiver_fct fct, void (*operate) (void *),
extern int _dl_catch_error (const char **objname, const char **errstring,
bool *mallocedp, void (*operate) (void *),
void *args)
internal_function attribute_hidden;
internal_function;
libc_hidden_proto (_dl_catch_error)
/* Open the shared object NAME and map in its segments.
LOADER's DT_RPATH is used in searching for NAME.

View File

@ -15,3 +15,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -16,3 +16,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -33,3 +33,6 @@ ld.so: malloc + RELA R_ALPHA_GLOB_DAT
ld.so: calloc + RELA R_ALPHA_GLOB_DAT
ld.so: realloc + RELA R_ALPHA_GLOB_DAT
ld.so: free + RELA R_ALPHA_GLOB_DAT
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error + RELA R_ALPHA_GLOB_DAT
ld.so: _dl_catch_error + RELA R_ALPHA_GLOB_DAT

View File

@ -16,3 +16,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -21,3 +21,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -15,3 +15,6 @@ ld.so: malloc + REL R_386_GLOB_DAT
ld.so: calloc + REL R_386_GLOB_DAT
ld.so: realloc + REL R_386_GLOB_DAT
ld.so: free + REL R_386_GLOB_DAT
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error + REL R_386_GLOB_DAT
ld.so: _dl_catch_error + REL R_386_GLOB_DAT

View File

@ -14,3 +14,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -14,3 +14,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -15,3 +15,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -35,3 +35,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -13,3 +13,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -43,3 +43,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -12,3 +12,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -20,3 +20,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -18,3 +18,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -24,3 +24,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -26,3 +26,6 @@ ld.so: malloc
ld.so: calloc
ld.so: realloc
ld.so: free
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error
ld.so: _dl_catch_error

View File

@ -17,3 +17,6 @@ ld.so: malloc + RELA R_X86_64_GLOB_DAT
ld.so: calloc + RELA R_X86_64_GLOB_DAT
ld.so: realloc + RELA R_X86_64_GLOB_DAT
ld.so: free + RELA R_X86_64_GLOB_DAT
# The TLS-enabled version of these functions is interposed from libc.so.
ld.so: _dl_signal_error + RELA R_X86_64_GLOB_DAT
ld.so: _dl_catch_error + RELA R_X86_64_GLOB_DAT