about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2016-11-30 15:59:57 +0100
committerFlorian Weimer <fweimer@redhat.com>2016-11-30 15:59:57 +0100
commit9e78f6f6e7134a5f299cc8de77370218f8019237 (patch)
tree04aa339daf9901b8fc1851353c697528659e7afe
parent705a79f82560ff6472cebed86aa5db04cdea3bce (diff)
downloadglibc-9e78f6f6e7134a5f299cc8de77370218f8019237.tar.gz
glibc-9e78f6f6e7134a5f299cc8de77370218f8019237.tar.xz
glibc-9e78f6f6e7134a5f299cc8de77370218f8019237.zip
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.
-rw-r--r--ChangeLog78
-rw-r--r--dlfcn/dlerror.c4
-rw-r--r--dlfcn/dlinfo.c4
-rw-r--r--dlfcn/dlmopen.c4
-rw-r--r--dlfcn/dlopen.c2
-rw-r--r--elf/Makefile21
-rw-r--r--elf/Versions6
-rw-r--r--elf/dl-close.c2
-rw-r--r--elf/dl-error-minimal.c23
-rw-r--r--elf/dl-error-skeleton.c230
-rw-r--r--elf/dl-error.c210
-rw-r--r--elf/dl-libc.c4
-rw-r--r--elf/dl-sym.c8
-rw-r--r--elf/dl-tsd.c53
-rw-r--r--elf/rtld.c19
-rw-r--r--elf/tst-latepthread.c105
-rw-r--r--elf/tst-latepthreadmod.c33
-rw-r--r--nptl/nptl-init.c4
-rw-r--r--sysdeps/generic/ldsodefs.h46
-rw-r--r--sysdeps/generic/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/aarch64/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/alpha/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/arm/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/hppa/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/i386/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/ia64/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/m68k/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/microblaze/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/nios2/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/s390/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/sh/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data3
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data3
-rw-r--r--sysdeps/x86_64/localplt.data3
37 files changed, 595 insertions, 315 deletions
diff --git a/ChangeLog b/ChangeLog
index c2cb608065..d01747866e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,83 @@
 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]
 	* libio/filedoalloc.c (_IO_file_doallocate): Limit buffer size to
 	_IO_BUFSIZ (8192).
diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c
index 41b2bd6bf2..e0e5648696 100644
--- a/dlfcn/dlerror.c
+++ b/dlfcn/dlerror.c
@@ -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;
diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c
index eeba8da408..794e354c23 100644
--- a/dlfcn/dlinfo.c
+++ b/dlfcn/dlinfo.c
@@ -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:
diff --git a/dlfcn/dlmopen.c b/dlfcn/dlmopen.c
index 6da0d0d691..4eed5136f5 100644
--- a/dlfcn/dlmopen.c
+++ b/dlfcn/dlmopen.c
@@ -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,
diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c
index 453efbd1c0..5263fed5f3 100644
--- a/dlfcn/dlopen.c
+++ b/dlfcn/dlopen.c
@@ -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,
diff --git a/elf/Makefile b/elf/Makefile
index 82c7e0559d..33b003b170 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -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
diff --git a/elf/Versions b/elf/Versions
index 23deda984f..05e5449f4d 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -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;
   }
 }
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 687d7de874..648970332e 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -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));
diff --git a/elf/dl-error-minimal.c b/elf/dl-error-minimal.c
new file mode 100644
index 0000000000..d535d65877
--- /dev/null
+++ b/elf/dl-error-minimal.c
@@ -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"
diff --git a/elf/dl-error-skeleton.c b/elf/dl-error-skeleton.c
new file mode 100644
index 0000000000..beb31ae393
--- /dev/null
+++ b/elf/dl-error-skeleton.c
@@ -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 */
diff --git a/elf/dl-error.c b/elf/dl-error.c
index bd22ec6cf0..7fe36b4631 100644
--- a/elf/dl-error.c
+++ b/elf/dl-error.c
@@ -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"
diff --git a/elf/dl-libc.c b/elf/dl-libc.c
index d56de1a57a..dde44c8799 100644
--- a/elf/dl-libc.c
+++ b/elf/dl-libc.c
@@ -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)
diff --git a/elf/dl-sym.c b/elf/dl-sym.c
index 6431c22614..e00b286991 100644
--- a/elf/dl-sym.c
+++ b/elf/dl-sym.c
@@ -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"));
 	}
 
diff --git a/elf/dl-tsd.c b/elf/dl-tsd.c
deleted file mode 100644
index 7181e1c9e0..0000000000
--- a/elf/dl-tsd.c
+++ /dev/null
@@ -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 */
diff --git a/elf/rtld.c b/elf/rtld.c
index 647661ca45..4ec25d7c30 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -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 \
diff --git a/elf/tst-latepthread.c b/elf/tst-latepthread.c
new file mode 100644
index 0000000000..9449ef6c2d
--- /dev/null
+++ b/elf/tst-latepthread.c
@@ -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"
diff --git a/elf/tst-latepthreadmod.c b/elf/tst-latepthreadmod.c
new file mode 100644
index 0000000000..ce81206f2a
--- /dev/null
+++ b/elf/tst-latepthreadmod.c
@@ -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 ();
+}
diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
index 48fab50c4e..0fd54a0a93 100644
--- a/nptl/nptl-init.c
+++ b/nptl/nptl-init.c
@@ -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;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index f68fdf4501..288f5fe32e 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -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.
diff --git a/sysdeps/generic/localplt.data b/sysdeps/generic/localplt.data
index 1a40cf9841..5cf53a4c21 100644
--- a/sysdeps/generic/localplt.data
+++ b/sysdeps/generic/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/aarch64/localplt.data b/sysdeps/unix/sysv/linux/aarch64/localplt.data
index d7d673454f..e431f368e3 100644
--- a/sysdeps/unix/sysv/linux/aarch64/localplt.data
+++ b/sysdeps/unix/sysv/linux/aarch64/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/alpha/localplt.data b/sysdeps/unix/sysv/linux/alpha/localplt.data
index 351596cf57..298439365f 100644
--- a/sysdeps/unix/sysv/linux/alpha/localplt.data
+++ b/sysdeps/unix/sysv/linux/alpha/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/arm/localplt.data b/sysdeps/unix/sysv/linux/arm/localplt.data
index 4301703553..a5ccd7fa58 100644
--- a/sysdeps/unix/sysv/linux/arm/localplt.data
+++ b/sysdeps/unix/sysv/linux/arm/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/hppa/localplt.data b/sysdeps/unix/sysv/linux/hppa/localplt.data
index 2a25ebce46..fea8c9cbf6 100644
--- a/sysdeps/unix/sysv/linux/hppa/localplt.data
+++ b/sysdeps/unix/sysv/linux/hppa/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/i386/localplt.data b/sysdeps/unix/sysv/linux/i386/localplt.data
index 4ce8447673..48bcc42a67 100644
--- a/sysdeps/unix/sysv/linux/i386/localplt.data
+++ b/sysdeps/unix/sysv/linux/i386/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/ia64/localplt.data b/sysdeps/unix/sysv/linux/ia64/localplt.data
index b628ca4dfc..df63530b6c 100644
--- a/sysdeps/unix/sysv/linux/ia64/localplt.data
+++ b/sysdeps/unix/sysv/linux/ia64/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/m68k/localplt.data b/sysdeps/unix/sysv/linux/m68k/localplt.data
index 88124c4460..abfbd34f41 100644
--- a/sysdeps/unix/sysv/linux/m68k/localplt.data
+++ b/sysdeps/unix/sysv/linux/m68k/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/microblaze/localplt.data b/sysdeps/unix/sysv/linux/microblaze/localplt.data
index 176d61818e..697fdd0186 100644
--- a/sysdeps/unix/sysv/linux/microblaze/localplt.data
+++ b/sysdeps/unix/sysv/linux/microblaze/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/nios2/localplt.data b/sysdeps/unix/sysv/linux/nios2/localplt.data
index 6cf93cc2af..a7d774dcf8 100644
--- a/sysdeps/unix/sysv/linux/nios2/localplt.data
+++ b/sysdeps/unix/sysv/linux/nios2/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
index b25abf8006..4ef5bf4a7f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
index 8c4e65d123..c9194264dd 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
index 49d5de6019..2d434726cc 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/s390/localplt.data b/sysdeps/unix/sysv/linux/s390/localplt.data
index 122641312c..bd1adddb1f 100644
--- a/sysdeps/unix/sysv/linux/s390/localplt.data
+++ b/sysdeps/unix/sysv/linux/s390/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/sh/localplt.data b/sysdeps/unix/sysv/linux/sh/localplt.data
index 57f31c579a..e6fb930cf7 100644
--- a/sysdeps/unix/sysv/linux/sh/localplt.data
+++ b/sysdeps/unix/sysv/linux/sh/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data b/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
index a208adff31..d5b5895a5c 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
@@ -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
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data b/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
index 2323551f63..edceab57c3 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
@@ -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
diff --git a/sysdeps/x86_64/localplt.data b/sysdeps/x86_64/localplt.data
index f168b143ff..28096f893e 100644
--- a/sysdeps/x86_64/localplt.data
+++ b/sysdeps/x86_64/localplt.data
@@ -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