about summary refs log tree commit diff
path: root/elf
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 /elf
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.
Diffstat (limited to 'elf')
-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
12 files changed, 428 insertions, 286 deletions
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 ();
+}