diff mbox

ld.so: Introduce struct dl_exception

Message ID 14388f99-d227-a01a-45f6-df66a40574b9@redhat.com
State New
Headers show

Commit Message

Florian Weimer Aug. 10, 2017, 11:56 a.m. UTC
On 06/19/2017 05:29 PM, Florian Weimer wrote:
> This commit separates allocating and raising exceptions.  This
> simplifies catching and re-raising them because it is no longer
> necessary to make a temporary, on-stack copy of the exception message.

Ping?

I rebased the attached patch to current master.

I plan to remove the old handler functions in a subsequent patch.  This
patch only removes the alloca-based string manipulation, but leaves some
uses of the legacy interfaces in place.

Thanks,
Florian

Comments

Carlos O'Donell Aug. 10, 2017, 2:23 p.m. UTC | #1
On 08/10/2017 07:56 AM, Florian Weimer wrote:
> On 06/19/2017 05:29 PM, Florian Weimer wrote:
>> This commit separates allocating and raising exceptions.  This
>> simplifies catching and re-raising them because it is no longer
>> necessary to make a temporary, on-stack copy of the exception message.
> Ping?
> 
> I rebased the attached patch to current master.
> 
> I plan to remove the old handler functions in a subsequent patch.  This
> patch only removes the alloca-based string manipulation, but leaves some
> uses of the legacy interfaces in place.

Yeah, I noticed while reviewing that the old handler was almost unused after
you split up the sequence into create/throw/free.

Thanks for doing this kind of structured cleanup and for introducing more
structure to the internals of ld.so.

(1) High level.

The idea of grouping the exception data into a single structure is both
logical and modular and provides for a cleaner interface.

(2) Design.

The removal of the intermediate alloca's from strdupa'ing the strings is
big win. It clarifies the semantics of ownership of the string storage
and is a good step forward in improving the internal structure of the
loader.

(3) Implementation.

You added a minimal number of key functions to create, free, and use the
new dl_exception structure. You made good use of the API internally to
delete a lot of boilerplate code. The existing tests should cover the
exception code well so no new tests need to be added.

Overall it looks good to me.

> ld.so: Introduce struct dl_exception
> 
> This commit separates allocating and raising exceptions.  This
> simplifies catching and re-raising them because it is no longer
> necessary to make a temporary, on-stack copy of the exception message.
> 
> 2017-08-10  Florian Weimer  <fweimer@redhat.com>
> 
> 	Introduce ld.so exceptions.
> 	* sysdeps/generic/ldsodefs.h (struct dl_exception): Define.
> 	(_dl_exception_create, _dl_exception_create_format)
> 	(_dl_exception_free, _dl_signal_exception, _dl_signal_cexception)
> 	(_dl_catch_exception): Declare.
> 	(_dl_catch_error): Update comment.
> 	* elf/dl-error-skeleton.c (struct catch): Replace objname,
> 	errstring, malloced members with exception member.
> 	(_dl_out_of_memory): Remove.
> 	(fatal_error): New function, extracted from _dl_signal_error.
> 	(_dl_signal_exception, _dl_signal_cexception): New functions.
> 	(_dl_signal_error): Call _dl_exception_create to allocate an
> 	exception object.
> 	(_dl_catch_exception): New function, based on _dl_catch_error.
> 	(_dl_catch_error): Implement using _dl_catch_exception.
> 	* elf/dl-exception.c: New file.
> 	* elf/Makefile (dl-routines): Add dl-exception.
> 	(elide-routines.os): Likewise.
> 	* elf/Version (ld/GLIBC_PRIVATE): Add _dl_exception_create,
> 	_dl_exception_create_format, _dl_exception_free.
> 	* elf/dl-deps.c (_dl_map_object_deps): Use _dl_catch_exception and
> 	_dl_signal_exception.
> 	* elf/dl-lookup.c (make_string): Remove.
> 	(_dl_lookup_symbol_x): Use _dl_exception_create_format,
> 	_dl_signal_cexception, _dl_exception_free.
> 	* elf/dl-open.c (_dl_open): Use _dl_catch_exception and
> 	_dl_signal_exception.
> 	* elf/dl-sym.c (do_sym): Likewise.
> 	* elf/dl-version.c (make_string): Remove.
> 	(match_symbol): Use _dl_exception_create_format,
> 	_dl_signal_cexception, _dl_exception_free.
> 	(_dl_check_map_versions): Likewise.
> 	* sysdeps/generic/localplt.data (ld.so): Add _dl_signal_exception,
> 	_dl_catch_exception.
> 	* 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.
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index b54ebf8a98..d314a5fa7e 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -31,7 +31,8 @@ routines	= $(all-dl-routines) dl-support dl-iteratephdr \
>  dl-routines	= $(addprefix dl-,load lookup object reloc deps hwcaps \
>  				  runtime init fini debug misc \
>  				  version profile tls origin scope \
> -				  execstack caller open close trampoline)
> +				  execstack caller open close trampoline \
> +				  exception)

OK.

>  ifeq (yes,$(use-ldconfig))
>  dl-routines += dl-cache
>  endif
> @@ -51,7 +52,7 @@ endif
>  all-dl-routines = $(dl-routines) $(sysdep-dl-routines)
>  # But they are absent from the shared libc, because that code is in ld.so.
>  elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
> -		    dl-sysdep
> +		    dl-sysdep dl-exception

OK.

>  shared-only-routines += dl-caller
>  
>  # ld.so uses those routines, plus some special stuff for being the program
> diff --git a/elf/Versions b/elf/Versions
> index e65f2fac20..79ffaf73d2 100644
> --- a/elf/Versions
> +++ b/elf/Versions
> @@ -28,6 +28,7 @@ libc {
>      __libc_dlclose; __libc_dlopen_mode; __libc_dlsym;
>  
>      # Internal error handling support.  Interposes the functions in ld.so.
> +    _dl_signal_exception; _dl_catch_exception;

OK.

>      _dl_signal_error; _dl_catch_error;
>    }
>  }
> @@ -68,7 +69,11 @@ ld {
>      # Pointer protection.
>      __pointer_chk_guard;
>  
> +    # Internal error handling support.
> +    _dl_exception_create; _dl_exception_create_format; _dl_exception_free;

OK.

> +
>      # Internal error handling support.  Interposed by libc.so.
> +    _dl_signal_exception; _dl_catch_exception;

OK.

>      _dl_signal_error; _dl_catch_error;
>  
>      # Set value of a tunable.
> diff --git a/elf/dl-deps.c b/elf/dl-deps.c
> index 1b8bac6593..7c82d42be9 100644
> --- a/elf/dl-deps.c
> +++ b/elf/dl-deps.c
> @@ -165,8 +165,7 @@ _dl_map_object_deps (struct link_map *map,
>    const char *name;
>    int errno_saved;
>    int errno_reason;
> -  const char *errstring;
> -  const char *objname;
> +  struct dl_exception exception;
>  
>    /* No loaded object so far.  */
>    nlist = 0;
> @@ -200,7 +199,6 @@ _dl_map_object_deps (struct link_map *map,
>       alloca means we cannot use recursive function calls.  */
>    errno_saved = errno;
>    errno_reason = 0;
> -  errstring = NULL;
>    errno = 0;
>    name = NULL;
>    for (runp = known; runp; )
> @@ -250,17 +248,9 @@ _dl_map_object_deps (struct link_map *map,
>  		/* Store the tag in the argument structure.  */
>  		args.name = name;
>  
> -		bool malloced;
> -		int err = _dl_catch_error (&objname, &errstring, &malloced,
> -					   openaux, &args);
> -		if (__glibc_unlikely (errstring != NULL))
> +		int err = _dl_catch_exception (&exception, openaux, &args);
> +		if (__glibc_unlikely (exception.errstring != NULL))
>  		  {
> -		    char *new_errstring = strdupa (errstring);
> -		    objname = strdupa (objname);
> -		    if (malloced)
> -		      free ((char *) errstring);
> -		    errstring = new_errstring;
> -
>  		    if (err)
>  		      errno_reason = err;
>  		    else
> @@ -313,31 +303,18 @@ _dl_map_object_deps (struct link_map *map,
>  		/* We must be prepared that the addressed shared
>  		   object is not available.  For filter objects the dependency
>  		   must be available.  */
> -		bool malloced;
> -		int err = _dl_catch_error (&objname, &errstring, &malloced,
> -					openaux, &args);
> -
> -		if (__glibc_unlikely (errstring != NULL))
> +		int err = _dl_catch_exception (&exception, openaux, &args);
> +		if (__glibc_unlikely (exception.errstring != NULL))
>  		  {
>  		    if (d->d_tag == DT_AUXILIARY)
>  		      {
>  			/* We are not interested in the error message.  */
> -			assert (errstring != NULL);
> -			if (malloced)
> -			  free ((char *) errstring);
> -
> +			_dl_exception_free (&exception);
>  			/* Simply ignore this error and continue the work.  */
>  			continue;
>  		      }
>  		    else
>  		      {
> -
> -			char *new_errstring = strdupa (errstring);
> -			objname = strdupa (objname);
> -			if (malloced)
> -			  free ((char *) errstring);
> -			errstring = new_errstring;
> -
>  			if (err)
>  			  errno_reason = err;
>  			else
> @@ -683,6 +660,6 @@ Filters not supported with LD_TRACE_PRELINKING"));
>      _dl_scope_free (old_l_initfini);
>  
>    if (errno_reason)
> -    _dl_signal_error (errno_reason == -1 ? 0 : errno_reason, objname,
> -		      NULL, errstring);
> +    _dl_signal_exception (errno_reason == -1 ? 0 : errno_reason,
> +			  &exception, NULL);

OK.

>  }
> diff --git a/elf/dl-error-skeleton.c b/elf/dl-error-skeleton.c
> index 8e5888d4bd..8de6c87abf 100644
> --- a/elf/dl-error-skeleton.c
> +++ b/elf/dl-error-skeleton.c
> @@ -39,10 +39,7 @@
>     _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.  */
> +    struct dl_exception *exception; /* The exception data is stored there.  */

OK.

>      volatile int *errcode;	/* Return value of _dl_signal_error.  */
>      jmp_buf env;		/* longjmp here on error.  */
>    };
> @@ -60,11 +57,6 @@ static __thread struct catch *catch_hook attribute_tls_model_ie;
>  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";
> -

OK.

>  #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.
> @@ -76,6 +68,41 @@ static const char _dl_out_of_memory[] = "out of memory";
>  static receiver_fct receiver;
>  #endif /* DL_ERROR_BOOTSTRAP */
>  
> +/* Lossage while resolving the program's own symbols is always fatal.  */
> +static void
> +__attribute__ ((noreturn))
> +fatal_error (int errcode, const char *objname, const char *occasion,
> +	     const char *errstring)
> +{
> +  char buffer[1024];
> +  _dl_fatal_printf ("%s: %s: %s%s%s%s%s\n",
> +		    RTLD_PROGNAME,
> +		    occasion ?: N_("error while loading shared libraries"),
> +		    objname, *objname ? ": " : "",
> +		    errstring, errcode ? ": " : "",
> +		    (errcode
> +		     ? __strerror_r (errcode, buffer, sizeof buffer)
> +		     : ""));
> +}

OK.

> +
> +void
> +_dl_signal_exception (int errcode, struct dl_exception *exception,
> +		      const char *occasion)
> +{
> +  struct catch *lcatch = catch_hook;
> +  if (lcatch != NULL)
> +    {
> +      *lcatch->exception = *exception;
> +      *lcatch->errcode = errcode;
> +
> +      /* We do not restore the signal mask because none was saved.  */
> +      __longjmp (lcatch->env[0].__jmpbuf, 1);
> +    }
> +  else
> +    fatal_error (errcode, exception->objname, occasion, exception->errstring);
> +}
> +libc_hidden_def (_dl_signal_exception)

OK.

> +
>  void
>  internal_function
>  _dl_signal_error (int errcode, const char *objname, const char *occation,
> @@ -86,66 +113,43 @@ _dl_signal_error (int errcode, const char *objname, const char *occation,
>    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;
> -	}
> -
> +      _dl_exception_create (lcatch->exception, objname, errstring);

OK.

>        *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)
> -			 : ""));
> -    }
> +    fatal_error (errcode, objname, occation, errstring);

OK.

>  }
>  libc_hidden_def (_dl_signal_error)
>  
>  
>  #if DL_ERROR_BOOTSTRAP
>  void
> +_dl_signal_cexception (int errcode, struct dl_exception *exception,
> +		       const char *occasion)
> +{
> +  if (__builtin_expect (GLRO(dl_debug_mask)
> +			& ~(DL_DEBUG_STATISTICS|DL_DEBUG_PRELINK), 0))
> +    _dl_debug_printf ("%s: error: %s: %s (%s)\n",
> +		      exception->objname, occasion,
> +		      exception->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, exception->objname, exception->errstring);
> +    }
> +  else
> +    _dl_signal_exception (errcode, exception, occasion);
> +}

OK.

> +
> +void
>  internal_function
>  _dl_signal_cerror (int errcode, const char *objname, const char *occation,
>  		   const char *errstring)
> @@ -167,11 +171,9 @@ _dl_signal_cerror (int errcode, const char *objname, const char *occation,
>  }
>  #endif /* DL_ERROR_BOOTSTRAP */
>  
> -
>  int
> -internal_function
> -_dl_catch_error (const char **objname, const char **errstring,
> -		 bool *mallocedp, void (*operate) (void *), void *args)
> +_dl_catch_exception (struct dl_exception *exception,
> +		     void (*operate) (void *), void *args)

OK.

>  {
>    /* We need not handle `receiver' since setting a `catch' is handled
>       before it.  */
> @@ -184,9 +186,7 @@ _dl_catch_error (const char **objname, const char **errstring,
>  
>    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.exception = exception;

OK.

>    c.errcode = &errcode;
>  
>    struct catch *const old = catch_hook;
> @@ -197,17 +197,30 @@ _dl_catch_error (const char **objname, const char **errstring,
>      {
>        (*operate) (args);
>        catch_hook = old;
> -      *objname = NULL;
> -      *errstring = NULL;
> -      *mallocedp = false;
> +      *exception = (struct dl_exception) { NULL };
>        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.  */
> +  /* We get here only if we longjmp'd out of OPERATE.
> +     _dl_signal_exception has already stored values into
> +     *EXCEPTION.  */
>    catch_hook = old;
>    return errcode;
>  }
> +libc_hidden_def (_dl_catch_exception)
> +
> +int
> +internal_function
> +_dl_catch_error (const char **objname, const char **errstring,
> +		 bool *mallocedp, void (*operate) (void *), void *args)
> +{
> +  struct dl_exception exception;
> +  int errorcode = _dl_catch_exception (&exception, operate, args);
> +  *objname = exception.objname;
> +  *errstring = exception.errstring;
> +  *mallocedp = exception.message_buffer == exception.errstring;
> +  return errorcode;
> +}

OK.

>  libc_hidden_def (_dl_catch_error)
>  
>  #if DL_ERROR_BOOTSTRAP
> diff --git a/elf/dl-exception.c b/elf/dl-exception.c
> new file mode 100644
> index 0000000000..b4d0ca7578
> --- /dev/null
> +++ b/elf/dl-exception.c
> @@ -0,0 +1,202 @@
> +/* ld.so error exception allocation and deallocation.
> +   Copyright (C) 1995-2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <ldsodefs.h>
> +#include <limits.h>
> +#include <stdarg.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +/* 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";
> +
> +/* Dummy allocation object used if allocating the message buffer
> +   fails.  */
> +static void
> +oom_exception (struct dl_exception *exception)
> +{
> +  exception->objname = "";
> +  exception->errstring = _dl_out_of_memory;
> +  exception->message_buffer = NULL;
> +}
> +
> +static void
> +__attribute__ ((noreturn))
> +length_mismatch (void)
> +{
> +  _dl_fatal_printf ("Fatal error: "
> +                    "length accounting in _dl_exception_create_format\n");
> +}
> +
> +/* Adjust the message buffer to indicate whether it is possible to
> +   free it.  EXCEPTION->errstring must be a potentially deallocatable
> +   pointer.  */
> +static void
> +adjust_message_buffer (struct dl_exception *exception)
> +{
> +  /* 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
> +  if (malloced)
> +    exception->message_buffer = (char *) exception->errstring;
> +  else
> +    exception->message_buffer = NULL;
> +}
> +
> +void
> +_dl_exception_create (struct dl_exception *exception, const char *objname,
> +                      const char *errstring)
> +{
> +  if (objname == NULL)
> +    objname = "";
> +  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.  */
> +      exception->objname = memcpy (__mempcpy (errstring_copy,
> +                                              errstring, len_errstring),
> +                                   objname, len_objname);
> +      exception->errstring = errstring_copy;
> +      adjust_message_buffer (exception);
> +    }
> +  else
> +    oom_exception (exception);
> +}
> +rtld_hidden_def (_dl_exception_create)
> +
> +void
> +_dl_exception_create_format (struct dl_exception *exception, const char *objname,
> +                             const char *fmt, ...)
> +{
> +  if (objname == NULL)
> +    objname = "";
> +  size_t len_objname = strlen (objname) + 1;
> +  /* Compute the length of the result.  Include room for two NUL
> +     bytes.  */
> +  size_t length = len_objname + 1;
> +  {
> +    va_list ap;
> +    va_start (ap, fmt);
> +    for (const char *p = fmt; *p != '\0'; ++p)
> +      if (*p == '%')
> +        {
> +          ++p;
> +          switch (*p)
> +            {
> +            case 's':
> +              length += strlen (va_arg (ap, const char *));
> +              break;
> +            default:
> +              /* Assumed to be '%'.  */
> +              ++length;
> +              break;
> +            }
> +        }
> +      else
> +        ++length;
> +    va_end (ap);
> +  }

OK.

> +
> +  if (length > PTRDIFF_MAX)
> +    {
> +      oom_exception (exception);
> +      return;
> +    }
> +  char *errstring = malloc (length);
> +  if (errstring == NULL)
> +    {
> +      oom_exception (exception);
> +      return;
> +    }
> +  exception->errstring = errstring;
> +  adjust_message_buffer (exception);
> +
> +  /* Copy the error message to errstring.  */
> +  {
> +    /* Next byte to be written in errstring.  */
> +    char *wptr = errstring;
> +    /* End of the allocated string.  */
> +    char *const end = errstring + length;
> +
> +    va_list ap;
> +    va_start (ap, fmt);
> +
> +    for (const char *p = fmt; *p != '\0'; ++p)
> +      if (*p == '%')
> +        {
> +          ++p;
> +          switch (*p)
> +            {
> +            case 's':
> +              {
> +                const char *ptr = va_arg (ap, const char *);
> +                size_t len_ptr = strlen (ptr);
> +                if (len_ptr > end - wptr)
> +                  length_mismatch ();
> +                wptr = __mempcpy (wptr, ptr, len_ptr);
> +              }
> +              break;
> +            case '%':
> +              if (wptr == end)
> +                length_mismatch ();
> +              *wptr = '%';
> +              ++wptr;
> +              break;
> +            default:
> +              _dl_fatal_printf ("Fatal error:"
> +                                " invalid format in exception string\n");
> +            }
> +        }
> +      else
> +        {
> +          if (wptr == end)
> +            length_mismatch ();
> +          *wptr = *p;
> +          ++wptr;
> +        }
> +
> +    if (wptr == end)
> +      length_mismatch ();
> +    *wptr = '\0';
> +    ++wptr;
> +    if (len_objname != end - wptr)
> +      length_mismatch ();
> +    exception->objname = memcpy (wptr, objname, len_objname);
> +  }
> +}

OK.

> +rtld_hidden_def (_dl_exception_create_format)
> +
> +void
> +_dl_exception_free (struct dl_exception *exception)
> +{
> +  free (exception->message_buffer);
> +  exception->objname = NULL;
> +  exception->errstring = NULL;
> +  exception->message_buffer = NULL;
> +}
> +rtld_hidden_def (_dl_exception_free)
> diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
> index 3d2369dbf2..645dc3ebb4 100644
> --- a/elf/dl-lookup.c
> +++ b/elf/dl-lookup.c
> @@ -47,23 +47,6 @@ struct sym_val
>    };
>  
>  
> -#define make_string(string, rest...) \
> -  ({									      \
> -    const char *all[] = { string, ## rest };				      \
> -    size_t len, cnt;							      \
> -    char *result, *cp;							      \
> -									      \
> -    len = 1;								      \
> -    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
> -      len += strlen (all[cnt]);						      \
> -									      \
> -    cp = result = alloca (len);						      \
> -    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
> -      cp = __stpcpy (cp, all[cnt]);					      \
> -									      \
> -    result;								      \
> -  })
> -

OK.

>  /* Statistics function.  */
>  #ifdef SHARED
>  # define bump_num_relocations() ++GL(dl_num_relocations)
> @@ -843,17 +826,16 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
>  	     for unversioned lookups.  */
>  	  assert (version != NULL);
>  	  const char *reference_name = undef_map ? undef_map->l_name : "";
> -
> +	  struct dl_exception exception;
>  	  /* XXX We cannot translate the message.  */
> -	  _dl_signal_cerror (0, DSO_FILENAME (reference_name),
> -			     N_("relocation error"),
> -			     make_string ("symbol ", undef_name, ", version ",
> -					  version->name,
> -					  " not defined in file ",
> -					  version->filename,
> -					  " with link time reference",
> -					  res == -2
> -					  ? " (no version symbols)" : ""));
> +	  _dl_exception_create_format
> +	    (&exception, DSO_FILENAME (reference_name),
> +	     "symbol %s version %s not defined in file %s"
> +	     " with link time reference%s",
> +	     undef_name, version->name, version->filename,
> +	     res == -2 ? " (no version symbols)" : "");
> +	  _dl_signal_cexception (0, &exception, N_("relocation error"));
> +	  _dl_exception_free (&exception);
>  	  *ref = NULL;
>  	  return 0;
>  	}
> @@ -869,12 +851,14 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
>  	  const char *versionstr = version ? ", version " : "";
>  	  const char *versionname = (version && version->name
>  				     ? version->name : "");
> -
> +	  struct dl_exception exception;
>  	  /* XXX We cannot translate the message.  */
> -	  _dl_signal_cerror (0, DSO_FILENAME (reference_name),
> -			     N_("symbol lookup error"),
> -			     make_string ("undefined symbol: ", undef_name,
> -					  versionstr, versionname));
> +	  _dl_exception_create_format
> +	    (&exception, DSO_FILENAME (reference_name),
> +	     "undefined symbol: %s%s%s",
> +	     undef_name, versionstr, versionname);
> +	  _dl_signal_cexception (0, &exception, N_("symbol lookup error"));
> +	  _dl_exception_free (&exception);

OK.

>  	}
>        *ref = NULL;
>        return 0;
> diff --git a/elf/dl-open.c b/elf/dl-open.c
> index cec54db413..91a1d1a4f8 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -643,11 +643,8 @@ no more namespaces available for dlmopen()"));
>    args.argv = argv;
>    args.env = env;
>  
> -  const char *objname;
> -  const char *errstring;
> -  bool malloced;
> -  int errcode = _dl_catch_error (&objname, &errstring, &malloced,
> -				 dl_open_worker, &args);
> +  struct dl_exception exception;
> +  int errcode = _dl_catch_exception (&exception, dl_open_worker, &args);
>  
>  #if defined USE_LDCONFIG && !defined MAP_COPY
>    /* We must unmap the cache file.  */
> @@ -655,7 +652,7 @@ no more namespaces available for dlmopen()"));
>  #endif
>  
>    /* See if an error occurred during loading.  */
> -  if (__glibc_unlikely (errstring != NULL))
> +  if (__glibc_unlikely (exception.errstring != NULL))
>      {
>        /* Remove the object from memory.  It may be in an inconsistent
>  	 state if relocation failed, for example.  */
> @@ -679,28 +676,8 @@ no more namespaces available for dlmopen()"));
>        /* Release the lock.  */
>        __rtld_lock_unlock_recursive (GL(dl_load_lock));
>  
> -      /* Make a local copy of the error string so that we can release the
> -	 memory allocated for it.  */
> -      size_t len_errstring = strlen (errstring) + 1;
> -      char *local_errstring;
> -      if (objname == errstring + len_errstring)
> -	{
> -	  size_t total_len = len_errstring + strlen (objname) + 1;
> -	  local_errstring = alloca (total_len);
> -	  memcpy (local_errstring, errstring, total_len);
> -	  objname = local_errstring + len_errstring;
> -	}
> -      else
> -	{
> -	  local_errstring = alloca (len_errstring);
> -	  memcpy (local_errstring, errstring, len_errstring);
> -	}
> -
> -      if (malloced)
> -	free ((char *) errstring);
> -
>        /* Reraise the error.  */
> -      _dl_signal_error (errcode, objname, NULL, local_errstring);
> +      _dl_signal_exception (errcode, &exception, NULL);

OK.

>      }
>  
>    assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
> diff --git a/elf/dl-sym.c b/elf/dl-sym.c
> index 7cd6e97643..fb54a91858 100644
> --- a/elf/dl-sym.c
> +++ b/elf/dl-sym.c
> @@ -119,26 +119,11 @@ do_sym (void *handle, const char *name, void *who,
>  	  args.refp = &ref;
>  
>  	  THREAD_GSCOPE_SET_FLAG ();
> -
> -	  const char *objname;
> -	  const char *errstring = NULL;
> -	  bool malloced;
> -	  int err = _dl_catch_error (&objname, &errstring, &malloced,
> -				     call_dl_lookup, &args);
> -
> +	  struct dl_exception exception;
> +	  int err = _dl_catch_exception (&exception, call_dl_lookup, &args);
>  	  THREAD_GSCOPE_RESET_FLAG ();
> -
> -	  if (__glibc_unlikely (errstring != NULL))
> -	    {
> -	      /* The lookup was unsuccessful.  Rethrow the error.  */
> -	      char *errstring_dup = strdupa (errstring);
> -	      char *objname_dup = strdupa (objname);
> -	      if (malloced)
> -		free ((char *) errstring);
> -
> -	      _dl_signal_error (err, objname_dup, NULL, errstring_dup);
> -	      /* NOTREACHED */
> -	    }
> +	  if (__glibc_unlikely (exception.errstring != NULL))
> +	    _dl_signal_exception (err, &exception, NULL);
>  

OK.

>  	  result = args.map;
>  	}
> diff --git a/elf/dl-version.c b/elf/dl-version.c
> index c00078e5e4..c0d76ad42a 100644
> --- a/elf/dl-version.c
> +++ b/elf/dl-version.c
> @@ -27,25 +27,6 @@
>  
>  #include <assert.h>
>  
> -
> -#define make_string(string, rest...) \
> -  ({									      \
> -    const char *all[] = { string, ## rest };				      \
> -    size_t len, cnt;							      \
> -    char *result, *cp;							      \
> -									      \
> -    len = 1;								      \
> -    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
> -      len += strlen (all[cnt]);						      \
> -									      \
> -    cp = result = alloca (len);						      \
> -    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
> -      cp = __stpcpy (cp, all[cnt]);					      \
> -									      \
> -    result;								      \
> -  })
> -
> -

OK.

>  static inline struct link_map *
>  __attribute ((always_inline))
>  find_needed (const char *name, struct link_map *map)
> @@ -78,8 +59,8 @@ match_symbol (const char *name, Lmid_t ns, ElfW(Word) hash, const char *string,
>    ElfW(Addr) def_offset;
>    ElfW(Verdef) *def;
>    /* Initialize to make the compiler happy.  */
> -  const char *errstring = NULL;
>    int result = 0;
> +  struct dl_exception exception;
>  
>    /* Display information about what we are doing while debugging.  */
>    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS))
> @@ -96,8 +77,9 @@ checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
>        if (verbose)
>  	{
>  	  /* XXX We cannot translate the messages.  */
> -	  errstring = make_string ("\
> -no version information available (required by ", name, ")");
> +	  _dl_exception_create_format
> +	    (&exception, DSO_FILENAME (map->l_name),
> +	     "no version information available (required by %s)", name);
>  	  goto call_cerror;
>  	}
>        return 0;
> @@ -116,10 +98,10 @@ no version information available (required by ", name, ")");
>  	  char buf[20];
>  	  buf[sizeof (buf) - 1] = '\0';
>  	  /* XXX We cannot translate the message.  */
> -	  errstring = make_string ("unsupported version ",
> -				   _itoa (def->vd_version,
> -					  &buf[sizeof (buf) - 1], 10, 0),
> -				   " of Verdef record");
> +	  _dl_exception_create_format
> +	    (&exception, DSO_FILENAME (map->l_name),
> +	     "unsupported version %s of Verdef record",
> +	     _itoa (def->vd_version, &buf[sizeof (buf) - 1], 10, 0));
>  	  result = 1;
>  	  goto call_cerror;
>  	}
> @@ -150,20 +132,22 @@ no version information available (required by ", name, ")");
>        if (verbose)
>  	{
>  	  /* XXX We cannot translate the message.  */
> -	  errstring = make_string ("weak version `", string,
> -				   "' not found (required by ", name, ")");
> +	  _dl_exception_create_format
> +	    (&exception, DSO_FILENAME (map->l_name),
> +	     "weak version `%s' not found (required by %s)", string, name);
>  	  goto call_cerror;
>  	}
>        return 0;
>      }
>  
>    /* XXX We cannot translate the message.  */
> -  errstring = make_string ("version `", string, "' not found (required by ",
> -			   name, ")");
> +  _dl_exception_create_format
> +    (&exception, DSO_FILENAME (map->l_name),
> +     "version `%s' not found (required by %s)", string, name);
>    result = 1;
>   call_cerror:
> -  _dl_signal_cerror (0, DSO_FILENAME (map->l_name),
> -		     N_("version lookup error"), errstring);
> +  _dl_signal_cexception (0, &exception, N_("version lookup error"));
> +  _dl_exception_free (&exception);
>    return result;
>  }
>  
> @@ -181,8 +165,8 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
>    /* We need to find out which is the highest version index used
>      in a dependecy.  */
>    unsigned int ndx_high = 0;
> +  struct dl_exception exception;
>    /* Initialize to make the compiler happy.  */
> -  const char *errstring = NULL;
>    int errval = 0;
>  
>    /* If we don't have a string table, we must be ok.  */
> @@ -205,13 +189,12 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
>  	  char buf[20];
>  	  buf[sizeof (buf) - 1] = '\0';
>  	  /* XXX We cannot translate the message.  */
> -	  errstring = make_string ("unsupported version ",
> -				   _itoa (ent->vn_version,
> -					  &buf[sizeof (buf) - 1], 10, 0),
> -				   " of Verneed record\n");
> +	  _dl_exception_create_format
> +	    (&exception, DSO_FILENAME (map->l_name),
> +	     "unsupported version %s of Verneed record",
> +	     _itoa (ent->vn_version, &buf[sizeof (buf) - 1], 10, 0));
>  	call_error:
> -	  _dl_signal_error (errval, DSO_FILENAME (map->l_name),
> -			    NULL, errstring);
> +	  _dl_signal_exception (errval, &exception, NULL);
>  	}
>  
>        while (1)
> @@ -293,7 +276,9 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
>  	calloc (ndx_high + 1, sizeof (*map->l_versions));
>        if (__glibc_unlikely (map->l_versions == NULL))
>  	{
> -	  errstring = N_("cannot allocate version reference table");
> +	  _dl_exception_create
> +	    (&exception, DSO_FILENAME (map->l_name),
> +	     N_("cannot allocate version reference table"));
>  	  errval = ENOMEM;
>  	  goto call_error;
>  	}
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 4508365871..1c0b9cb32e 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -732,31 +732,88 @@ _dl_dprintf (int fd, const char *fmt, ...)
>    while (1)
>  
>  
> -/* This function is called by all the internal dynamic linker functions
> -   when they encounter an error.  ERRCODE is either an `errno' code or
> -   zero; OBJECT is the name of the problematical shared object, or null if
> -   it is a general problem; ERRSTRING is a string describing the specific
> -   problem.  */
> +/* An exception raised by the _dl_signal_error function family and
> +   caught by _dl_catch_error function family.  Exceptions themselves
> +   are copied as part of the raise operation, but the strings are
> +   not.  */
> +struct dl_exception
> +{
> +  const char *objname;
> +  const char *errstring;
> +
> +  /* This buffer typically stores both objname and errstring
> +     above.  */
> +  char *message_buffer;
> +};
> +
> +/* Creates a new exception.  This calls malloc; if allocation fails,
> +   dummy values are inserted.  OBJECT is the name of the problematical
> +   shared object, or null if its a general problem.  ERRSTRING is a
> +   string describing the specific problem.  */
> +void _dl_exception_create (struct dl_exception *, const char *object,
> +			   const char *errstring)
> +  __attribute__ ((nonnull (1, 3)));
> +rtld_hidden_proto (_dl_exception_create)
> +
> +/* Like _dl_exception_create, but create errstring from a format
> +   string FMT.  Currently, only "%s" and "%%" are supported as format
> +   directives.  */
> +void _dl_exception_create_format (struct dl_exception *, const char *objname,
> +				  const char *fmt, ...)
> +  __attribute__ ((nonnull (1, 3), format (printf, 3, 4)));
> +rtld_hidden_proto (_dl_exception_create_format)
> +
> +/* Deallocate the exception, freeing allocated buffers (if
> +   possible).  */
> +void _dl_exception_free (struct dl_exception *)
> +  __attribute__ ((nonnull (1)));
> +rtld_hidden_proto (_dl_exception_free)
> +
> +/* This function is called by all the internal dynamic linker
> +   functions when they encounter an error.  ERRCODE is either an
> +   `errno' code or zero; it specifies the return value of
> +   _dl_catch_error.  OCCASION is included in the error message if the
> +   process is terminated immediately.  */
> +void _dl_signal_exception (int errcode, struct dl_exception *,
> +			   const char *occasion)
> +  __attribute__ ((__noreturn__));
> +libc_hidden_proto (_dl_signal_exception)
> +
> +/* Like _dl_signal_exception, but creates the exception first.  */
>  extern void _dl_signal_error (int errcode, const char *object,
> -			      const char *occurred, const char *errstring)
> +			      const char *occasion, const char *errstring)
>       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.  This is only used during ld.so bootstrap.  In
> -   static and profiled builds, this is equivalent to
> -   _dl_signal_error.  */
> +/* Like _dl_signal_exception, but may return when called in the
> +   context of _dl_receive_error.  This is only used during ld.so
> +   bootstrap.  In static and profiled builds, this is equivalent to
> +   _dl_signal_exception.  */
> +#if IS_IN (rtld)
> +extern void _dl_signal_cexception (int errcode, struct dl_exception *,
> +				   const char *occasion) attribute_hidden;
> +#else
> +__attribute__ ((always_inline))
> +static inline void
> +_dl_signal_cexception (int errcode, struct dl_exception *exception,
> +		       const char *occasion)
> +{
> +  _dl_signal_exception (errcode, exception, occasion);
> +}
> +#endif
> +
> +/* See _dl_signal_cexception above.  */
>  #if IS_IN (rtld)
>  extern void _dl_signal_cerror (int errcode, const char *object,
> -			       const char *occation, const char *errstring)
> +			       const char *occasion, 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)
> +			       const char *occasion, const char *errstring)
>  {
> -  _dl_signal_error (errcode, object, occation, errstring);
> +  _dl_signal_error (errcode, object, occasion, errstring);
>  }
>  #endif
>  

OK.

> @@ -768,20 +825,28 @@ extern void _dl_receive_error (receiver_fct fct, void (*operate) (void *),
>  			       void *args)
>       internal_function attribute_hidden;
>  
> -/* Call OPERATE, catching errors from `dl_signal_error'.  If there is no
> -   error, *ERRSTRING is set to null.  If there is an error, *ERRSTRING is
> -   set to a string constructed from the strings passed to _dl_signal_error,
> -   and the error code passed is the return value and *OBJNAME is set to
> -   the object name which experienced the problems.  ERRSTRING if nonzero
> -   points to a malloc'ed string which the caller has to free after use.
> -   ARGS is passed as argument to OPERATE.  MALLOCEDP is set to true only
> -   if the returned string is allocated using the libc's malloc.  */
> +/* Call OPERATE, catching errors from `_dl_signal_error' and related
> +   functions.  If there is no error, *ERRSTRING is set to null.  If
> +   there is an error, *ERRSTRING is set to a string constructed from
> +   the strings passed to _dl_signal_error, and the error code passed
> +   is the return value and *OBJNAME is set to the object name which
> +   experienced the problems.  ERRSTRING if nonzero points to a
> +   malloc'ed string which the caller has to free after use.  ARGS is
> +   passed as argument to OPERATE.  MALLOCEDP is set to true only if
> +   the returned string is allocated using the libc's malloc.  */
>  extern int _dl_catch_error (const char **objname, const char **errstring,
>  			    bool *mallocedp, void (*operate) (void *),
>  			    void *args)
>       internal_function;

OK.

>  libc_hidden_proto (_dl_catch_error)
>  
> +/* Call OPERATE (ARGS).  If no error occurs, set *EXCEPTION to zero.
> +   Otherwise, store a copy of the raised exception in *EXCEPTION,
> +   which has to be freed by _dl_exception_free.  */
> +int _dl_catch_exception (struct dl_exception *exception,
> +			 void (*operate) (void *), void *args);
> +libc_hidden_proto (_dl_catch_exception)
> +
>  /* Open the shared object NAME and map in its segments.
>     LOADER's DT_RPATH is used in searching for NAME.
>     If the object is already opened, returns its existing map.  */
> diff --git a/sysdeps/generic/localplt.data b/sysdeps/generic/localplt.data
> index 81c741b038..2d5c66ae28 100644
> --- a/sysdeps/generic/localplt.data
> +++ b/sysdeps/generic/localplt.data
> @@ -16,3 +16,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/aarch64/localplt.data b/sysdeps/unix/sysv/linux/aarch64/localplt.data
> index bb18ff9bb2..a60053b914 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/localplt.data
> +++ b/sysdeps/unix/sysv/linux/aarch64/localplt.data
> @@ -18,3 +18,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/alpha/localplt.data b/sysdeps/unix/sysv/linux/alpha/localplt.data
> index 1f0e3b494e..c69eb04ce5 100644
> --- a/sysdeps/unix/sysv/linux/alpha/localplt.data
> +++ b/sysdeps/unix/sysv/linux/alpha/localplt.data
> @@ -35,3 +35,5 @@ 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
> +ld.so: _dl_signal_exception + RELA R_ALPHA_GLOB_DAT
> +ld.so: _dl_catch_exception + RELA R_ALPHA_GLOB_DAT
> diff --git a/sysdeps/unix/sysv/linux/arm/localplt.data b/sysdeps/unix/sysv/linux/arm/localplt.data
> index 19d3299d98..7bd541c28a 100644
> --- a/sysdeps/unix/sysv/linux/arm/localplt.data
> +++ b/sysdeps/unix/sysv/linux/arm/localplt.data
> @@ -17,3 +17,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/hppa/localplt.data b/sysdeps/unix/sysv/linux/hppa/localplt.data
> index db9e24b090..3279c0af05 100644
> --- a/sysdeps/unix/sysv/linux/hppa/localplt.data
> +++ b/sysdeps/unix/sysv/linux/hppa/localplt.data
> @@ -21,3 +21,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/i386/localplt.data b/sysdeps/unix/sysv/linux/i386/localplt.data
> index 8ea4333846..f6f20a5d15 100644
> --- a/sysdeps/unix/sysv/linux/i386/localplt.data
> +++ b/sysdeps/unix/sysv/linux/i386/localplt.data
> @@ -16,3 +16,5 @@ 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
> +ld.so: _dl_signal_exception + REL R_386_GLOB_DAT
> +ld.so: _dl_catch_exception + REL R_386_GLOB_DAT
> diff --git a/sysdeps/unix/sysv/linux/ia64/localplt.data b/sysdeps/unix/sysv/linux/ia64/localplt.data
> index fd2b98c8b6..3820e2a4e6 100644
> --- a/sysdeps/unix/sysv/linux/ia64/localplt.data
> +++ b/sysdeps/unix/sysv/linux/ia64/localplt.data
> @@ -15,3 +15,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/m68k/localplt.data b/sysdeps/unix/sysv/linux/m68k/localplt.data
> index 1a2acfdb93..c70d6ea301 100644
> --- a/sysdeps/unix/sysv/linux/m68k/localplt.data
> +++ b/sysdeps/unix/sysv/linux/m68k/localplt.data
> @@ -15,3 +15,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/microblaze/localplt.data b/sysdeps/unix/sysv/linux/microblaze/localplt.data
> index ca476bedd8..8ca23897df 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/localplt.data
> +++ b/sysdeps/unix/sysv/linux/microblaze/localplt.data
> @@ -16,3 +16,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/nios2/localplt.data b/sysdeps/unix/sysv/linux/nios2/localplt.data
> index b0d6dcae55..4430a5891e 100644
> --- a/sysdeps/unix/sysv/linux/nios2/localplt.data
> +++ b/sysdeps/unix/sysv/linux/nios2/localplt.data
> @@ -36,3 +36,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
> index 50006317c7..e822e0a480 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
> @@ -14,3 +14,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
> index 1c20d2f2b4..fead931d4e 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
> @@ -44,3 +44,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
> index 6f8ed25922..c1209336d2 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
> @@ -13,3 +13,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/s390/localplt.data b/sysdeps/unix/sysv/linux/s390/localplt.data
> index 50006317c7..e822e0a480 100644
> --- a/sysdeps/unix/sysv/linux/s390/localplt.data
> +++ b/sysdeps/unix/sysv/linux/s390/localplt.data
> @@ -14,3 +14,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/sh/localplt.data b/sysdeps/unix/sysv/linux/sh/localplt.data
> index f1f5effc24..2753547d97 100644
> --- a/sysdeps/unix/sysv/linux/sh/localplt.data
> +++ b/sysdeps/unix/sysv/linux/sh/localplt.data
> @@ -19,3 +19,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data b/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
> index 2f6ff3c3a6..1668f4017e 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
> @@ -26,3 +26,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data b/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
> index 912bd1a16e..b881b9096d 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
> @@ -27,3 +27,5 @@ 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
> +ld.so: _dl_signal_exception
> +ld.so: _dl_catch_exception
> diff --git a/sysdeps/x86_64/localplt.data b/sysdeps/x86_64/localplt.data
> index a1840cff31..c27a02b66a 100644
> --- a/sysdeps/x86_64/localplt.data
> +++ b/sysdeps/x86_64/localplt.data
> @@ -18,3 +18,5 @@ 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
> +ld.so: _dl_signal_exception + RELA R_X86_64_GLOB_DAT
> +ld.so: _dl_catch_exception + RELA R_X86_64_GLOB_DAT

OK.
diff mbox

Patch

ld.so: Introduce struct dl_exception

This commit separates allocating and raising exceptions.  This
simplifies catching and re-raising them because it is no longer
necessary to make a temporary, on-stack copy of the exception message.

2017-08-10  Florian Weimer  <fweimer@redhat.com>

	Introduce ld.so exceptions.
	* sysdeps/generic/ldsodefs.h (struct dl_exception): Define.
	(_dl_exception_create, _dl_exception_create_format)
	(_dl_exception_free, _dl_signal_exception, _dl_signal_cexception)
	(_dl_catch_exception): Declare.
	(_dl_catch_error): Update comment.
	* elf/dl-error-skeleton.c (struct catch): Replace objname,
	errstring, malloced members with exception member.
	(_dl_out_of_memory): Remove.
	(fatal_error): New function, extracted from _dl_signal_error.
	(_dl_signal_exception, _dl_signal_cexception): New functions.
	(_dl_signal_error): Call _dl_exception_create to allocate an
	exception object.
	(_dl_catch_exception): New function, based on _dl_catch_error.
	(_dl_catch_error): Implement using _dl_catch_exception.
	* elf/dl-exception.c: New file.
	* elf/Makefile (dl-routines): Add dl-exception.
	(elide-routines.os): Likewise.
	* elf/Version (ld/GLIBC_PRIVATE): Add _dl_exception_create,
	_dl_exception_create_format, _dl_exception_free.
	* elf/dl-deps.c (_dl_map_object_deps): Use _dl_catch_exception and
	_dl_signal_exception.
	* elf/dl-lookup.c (make_string): Remove.
	(_dl_lookup_symbol_x): Use _dl_exception_create_format,
	_dl_signal_cexception, _dl_exception_free.
	* elf/dl-open.c (_dl_open): Use _dl_catch_exception and
	_dl_signal_exception.
	* elf/dl-sym.c (do_sym): Likewise.
	* elf/dl-version.c (make_string): Remove.
	(match_symbol): Use _dl_exception_create_format,
	_dl_signal_cexception, _dl_exception_free.
	(_dl_check_map_versions): Likewise.
	* sysdeps/generic/localplt.data (ld.so): Add _dl_signal_exception,
	_dl_catch_exception.
	* 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.

diff --git a/elf/Makefile b/elf/Makefile
index b54ebf8a98..d314a5fa7e 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -31,7 +31,8 @@  routines	= $(all-dl-routines) dl-support dl-iteratephdr \
 dl-routines	= $(addprefix dl-,load lookup object reloc deps hwcaps \
 				  runtime init fini debug misc \
 				  version profile tls origin scope \
-				  execstack caller open close trampoline)
+				  execstack caller open close trampoline \
+				  exception)
 ifeq (yes,$(use-ldconfig))
 dl-routines += dl-cache
 endif
@@ -51,7 +52,7 @@  endif
 all-dl-routines = $(dl-routines) $(sysdep-dl-routines)
 # But they are absent from the shared libc, because that code is in ld.so.
 elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
-		    dl-sysdep
+		    dl-sysdep dl-exception
 shared-only-routines += dl-caller
 
 # ld.so uses those routines, plus some special stuff for being the program
diff --git a/elf/Versions b/elf/Versions
index e65f2fac20..79ffaf73d2 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -28,6 +28,7 @@  libc {
     __libc_dlclose; __libc_dlopen_mode; __libc_dlsym;
 
     # Internal error handling support.  Interposes the functions in ld.so.
+    _dl_signal_exception; _dl_catch_exception;
     _dl_signal_error; _dl_catch_error;
   }
 }
@@ -68,7 +69,11 @@  ld {
     # Pointer protection.
     __pointer_chk_guard;
 
+    # Internal error handling support.
+    _dl_exception_create; _dl_exception_create_format; _dl_exception_free;
+
     # Internal error handling support.  Interposed by libc.so.
+    _dl_signal_exception; _dl_catch_exception;
     _dl_signal_error; _dl_catch_error;
 
     # Set value of a tunable.
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 1b8bac6593..7c82d42be9 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -165,8 +165,7 @@  _dl_map_object_deps (struct link_map *map,
   const char *name;
   int errno_saved;
   int errno_reason;
-  const char *errstring;
-  const char *objname;
+  struct dl_exception exception;
 
   /* No loaded object so far.  */
   nlist = 0;
@@ -200,7 +199,6 @@  _dl_map_object_deps (struct link_map *map,
      alloca means we cannot use recursive function calls.  */
   errno_saved = errno;
   errno_reason = 0;
-  errstring = NULL;
   errno = 0;
   name = NULL;
   for (runp = known; runp; )
@@ -250,17 +248,9 @@  _dl_map_object_deps (struct link_map *map,
 		/* Store the tag in the argument structure.  */
 		args.name = name;
 
-		bool malloced;
-		int err = _dl_catch_error (&objname, &errstring, &malloced,
-					   openaux, &args);
-		if (__glibc_unlikely (errstring != NULL))
+		int err = _dl_catch_exception (&exception, openaux, &args);
+		if (__glibc_unlikely (exception.errstring != NULL))
 		  {
-		    char *new_errstring = strdupa (errstring);
-		    objname = strdupa (objname);
-		    if (malloced)
-		      free ((char *) errstring);
-		    errstring = new_errstring;
-
 		    if (err)
 		      errno_reason = err;
 		    else
@@ -313,31 +303,18 @@  _dl_map_object_deps (struct link_map *map,
 		/* We must be prepared that the addressed shared
 		   object is not available.  For filter objects the dependency
 		   must be available.  */
-		bool malloced;
-		int err = _dl_catch_error (&objname, &errstring, &malloced,
-					openaux, &args);
-
-		if (__glibc_unlikely (errstring != NULL))
+		int err = _dl_catch_exception (&exception, openaux, &args);
+		if (__glibc_unlikely (exception.errstring != NULL))
 		  {
 		    if (d->d_tag == DT_AUXILIARY)
 		      {
 			/* We are not interested in the error message.  */
-			assert (errstring != NULL);
-			if (malloced)
-			  free ((char *) errstring);
-
+			_dl_exception_free (&exception);
 			/* Simply ignore this error and continue the work.  */
 			continue;
 		      }
 		    else
 		      {
-
-			char *new_errstring = strdupa (errstring);
-			objname = strdupa (objname);
-			if (malloced)
-			  free ((char *) errstring);
-			errstring = new_errstring;
-
 			if (err)
 			  errno_reason = err;
 			else
@@ -683,6 +660,6 @@  Filters not supported with LD_TRACE_PRELINKING"));
     _dl_scope_free (old_l_initfini);
 
   if (errno_reason)
-    _dl_signal_error (errno_reason == -1 ? 0 : errno_reason, objname,
-		      NULL, errstring);
+    _dl_signal_exception (errno_reason == -1 ? 0 : errno_reason,
+			  &exception, NULL);
 }
diff --git a/elf/dl-error-skeleton.c b/elf/dl-error-skeleton.c
index 8e5888d4bd..8de6c87abf 100644
--- a/elf/dl-error-skeleton.c
+++ b/elf/dl-error-skeleton.c
@@ -39,10 +39,7 @@ 
    _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.  */
+    struct dl_exception *exception; /* The exception data is stored there.  */
     volatile int *errcode;	/* Return value of _dl_signal_error.  */
     jmp_buf env;		/* longjmp here on error.  */
   };
@@ -60,11 +57,6 @@  static __thread struct catch *catch_hook attribute_tls_model_ie;
 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.
@@ -76,6 +68,41 @@  static const char _dl_out_of_memory[] = "out of memory";
 static receiver_fct receiver;
 #endif /* DL_ERROR_BOOTSTRAP */
 
+/* Lossage while resolving the program's own symbols is always fatal.  */
+static void
+__attribute__ ((noreturn))
+fatal_error (int errcode, const char *objname, const char *occasion,
+	     const char *errstring)
+{
+  char buffer[1024];
+  _dl_fatal_printf ("%s: %s: %s%s%s%s%s\n",
+		    RTLD_PROGNAME,
+		    occasion ?: N_("error while loading shared libraries"),
+		    objname, *objname ? ": " : "",
+		    errstring, errcode ? ": " : "",
+		    (errcode
+		     ? __strerror_r (errcode, buffer, sizeof buffer)
+		     : ""));
+}
+
+void
+_dl_signal_exception (int errcode, struct dl_exception *exception,
+		      const char *occasion)
+{
+  struct catch *lcatch = catch_hook;
+  if (lcatch != NULL)
+    {
+      *lcatch->exception = *exception;
+      *lcatch->errcode = errcode;
+
+      /* We do not restore the signal mask because none was saved.  */
+      __longjmp (lcatch->env[0].__jmpbuf, 1);
+    }
+  else
+    fatal_error (errcode, exception->objname, occasion, exception->errstring);
+}
+libc_hidden_def (_dl_signal_exception)
+
 void
 internal_function
 _dl_signal_error (int errcode, const char *objname, const char *occation,
@@ -86,66 +113,43 @@  _dl_signal_error (int errcode, const char *objname, const char *occation,
   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;
-	}
-
+      _dl_exception_create (lcatch->exception, objname, errstring);
       *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)
-			 : ""));
-    }
+    fatal_error (errcode, objname, occation, errstring);
 }
 libc_hidden_def (_dl_signal_error)
 
 
 #if DL_ERROR_BOOTSTRAP
 void
+_dl_signal_cexception (int errcode, struct dl_exception *exception,
+		       const char *occasion)
+{
+  if (__builtin_expect (GLRO(dl_debug_mask)
+			& ~(DL_DEBUG_STATISTICS|DL_DEBUG_PRELINK), 0))
+    _dl_debug_printf ("%s: error: %s: %s (%s)\n",
+		      exception->objname, occasion,
+		      exception->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, exception->objname, exception->errstring);
+    }
+  else
+    _dl_signal_exception (errcode, exception, occasion);
+}
+
+void
 internal_function
 _dl_signal_cerror (int errcode, const char *objname, const char *occation,
 		   const char *errstring)
@@ -167,11 +171,9 @@  _dl_signal_cerror (int errcode, const char *objname, const char *occation,
 }
 #endif /* DL_ERROR_BOOTSTRAP */
 
-
 int
-internal_function
-_dl_catch_error (const char **objname, const char **errstring,
-		 bool *mallocedp, void (*operate) (void *), void *args)
+_dl_catch_exception (struct dl_exception *exception,
+		     void (*operate) (void *), void *args)
 {
   /* We need not handle `receiver' since setting a `catch' is handled
      before it.  */
@@ -184,9 +186,7 @@  _dl_catch_error (const char **objname, const char **errstring,
 
   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.exception = exception;
   c.errcode = &errcode;
 
   struct catch *const old = catch_hook;
@@ -197,17 +197,30 @@  _dl_catch_error (const char **objname, const char **errstring,
     {
       (*operate) (args);
       catch_hook = old;
-      *objname = NULL;
-      *errstring = NULL;
-      *mallocedp = false;
+      *exception = (struct dl_exception) { NULL };
       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.  */
+  /* We get here only if we longjmp'd out of OPERATE.
+     _dl_signal_exception has already stored values into
+     *EXCEPTION.  */
   catch_hook = old;
   return errcode;
 }
+libc_hidden_def (_dl_catch_exception)
+
+int
+internal_function
+_dl_catch_error (const char **objname, const char **errstring,
+		 bool *mallocedp, void (*operate) (void *), void *args)
+{
+  struct dl_exception exception;
+  int errorcode = _dl_catch_exception (&exception, operate, args);
+  *objname = exception.objname;
+  *errstring = exception.errstring;
+  *mallocedp = exception.message_buffer == exception.errstring;
+  return errorcode;
+}
 libc_hidden_def (_dl_catch_error)
 
 #if DL_ERROR_BOOTSTRAP
diff --git a/elf/dl-exception.c b/elf/dl-exception.c
new file mode 100644
index 0000000000..b4d0ca7578
--- /dev/null
+++ b/elf/dl-exception.c
@@ -0,0 +1,202 @@ 
+/* ld.so error exception allocation and deallocation.
+   Copyright (C) 1995-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <ldsodefs.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+/* 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";
+
+/* Dummy allocation object used if allocating the message buffer
+   fails.  */
+static void
+oom_exception (struct dl_exception *exception)
+{
+  exception->objname = "";
+  exception->errstring = _dl_out_of_memory;
+  exception->message_buffer = NULL;
+}
+
+static void
+__attribute__ ((noreturn))
+length_mismatch (void)
+{
+  _dl_fatal_printf ("Fatal error: "
+                    "length accounting in _dl_exception_create_format\n");
+}
+
+/* Adjust the message buffer to indicate whether it is possible to
+   free it.  EXCEPTION->errstring must be a potentially deallocatable
+   pointer.  */
+static void
+adjust_message_buffer (struct dl_exception *exception)
+{
+  /* 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
+  if (malloced)
+    exception->message_buffer = (char *) exception->errstring;
+  else
+    exception->message_buffer = NULL;
+}
+
+void
+_dl_exception_create (struct dl_exception *exception, const char *objname,
+                      const char *errstring)
+{
+  if (objname == NULL)
+    objname = "";
+  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.  */
+      exception->objname = memcpy (__mempcpy (errstring_copy,
+                                              errstring, len_errstring),
+                                   objname, len_objname);
+      exception->errstring = errstring_copy;
+      adjust_message_buffer (exception);
+    }
+  else
+    oom_exception (exception);
+}
+rtld_hidden_def (_dl_exception_create)
+
+void
+_dl_exception_create_format (struct dl_exception *exception, const char *objname,
+                             const char *fmt, ...)
+{
+  if (objname == NULL)
+    objname = "";
+  size_t len_objname = strlen (objname) + 1;
+  /* Compute the length of the result.  Include room for two NUL
+     bytes.  */
+  size_t length = len_objname + 1;
+  {
+    va_list ap;
+    va_start (ap, fmt);
+    for (const char *p = fmt; *p != '\0'; ++p)
+      if (*p == '%')
+        {
+          ++p;
+          switch (*p)
+            {
+            case 's':
+              length += strlen (va_arg (ap, const char *));
+              break;
+            default:
+              /* Assumed to be '%'.  */
+              ++length;
+              break;
+            }
+        }
+      else
+        ++length;
+    va_end (ap);
+  }
+
+  if (length > PTRDIFF_MAX)
+    {
+      oom_exception (exception);
+      return;
+    }
+  char *errstring = malloc (length);
+  if (errstring == NULL)
+    {
+      oom_exception (exception);
+      return;
+    }
+  exception->errstring = errstring;
+  adjust_message_buffer (exception);
+
+  /* Copy the error message to errstring.  */
+  {
+    /* Next byte to be written in errstring.  */
+    char *wptr = errstring;
+    /* End of the allocated string.  */
+    char *const end = errstring + length;
+
+    va_list ap;
+    va_start (ap, fmt);
+
+    for (const char *p = fmt; *p != '\0'; ++p)
+      if (*p == '%')
+        {
+          ++p;
+          switch (*p)
+            {
+            case 's':
+              {
+                const char *ptr = va_arg (ap, const char *);
+                size_t len_ptr = strlen (ptr);
+                if (len_ptr > end - wptr)
+                  length_mismatch ();
+                wptr = __mempcpy (wptr, ptr, len_ptr);
+              }
+              break;
+            case '%':
+              if (wptr == end)
+                length_mismatch ();
+              *wptr = '%';
+              ++wptr;
+              break;
+            default:
+              _dl_fatal_printf ("Fatal error:"
+                                " invalid format in exception string\n");
+            }
+        }
+      else
+        {
+          if (wptr == end)
+            length_mismatch ();
+          *wptr = *p;
+          ++wptr;
+        }
+
+    if (wptr == end)
+      length_mismatch ();
+    *wptr = '\0';
+    ++wptr;
+    if (len_objname != end - wptr)
+      length_mismatch ();
+    exception->objname = memcpy (wptr, objname, len_objname);
+  }
+}
+rtld_hidden_def (_dl_exception_create_format)
+
+void
+_dl_exception_free (struct dl_exception *exception)
+{
+  free (exception->message_buffer);
+  exception->objname = NULL;
+  exception->errstring = NULL;
+  exception->message_buffer = NULL;
+}
+rtld_hidden_def (_dl_exception_free)
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 3d2369dbf2..645dc3ebb4 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -47,23 +47,6 @@  struct sym_val
   };
 
 
-#define make_string(string, rest...) \
-  ({									      \
-    const char *all[] = { string, ## rest };				      \
-    size_t len, cnt;							      \
-    char *result, *cp;							      \
-									      \
-    len = 1;								      \
-    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
-      len += strlen (all[cnt]);						      \
-									      \
-    cp = result = alloca (len);						      \
-    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
-      cp = __stpcpy (cp, all[cnt]);					      \
-									      \
-    result;								      \
-  })
-
 /* Statistics function.  */
 #ifdef SHARED
 # define bump_num_relocations() ++GL(dl_num_relocations)
@@ -843,17 +826,16 @@  _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
 	     for unversioned lookups.  */
 	  assert (version != NULL);
 	  const char *reference_name = undef_map ? undef_map->l_name : "";
-
+	  struct dl_exception exception;
 	  /* XXX We cannot translate the message.  */
-	  _dl_signal_cerror (0, DSO_FILENAME (reference_name),
-			     N_("relocation error"),
-			     make_string ("symbol ", undef_name, ", version ",
-					  version->name,
-					  " not defined in file ",
-					  version->filename,
-					  " with link time reference",
-					  res == -2
-					  ? " (no version symbols)" : ""));
+	  _dl_exception_create_format
+	    (&exception, DSO_FILENAME (reference_name),
+	     "symbol %s version %s not defined in file %s"
+	     " with link time reference%s",
+	     undef_name, version->name, version->filename,
+	     res == -2 ? " (no version symbols)" : "");
+	  _dl_signal_cexception (0, &exception, N_("relocation error"));
+	  _dl_exception_free (&exception);
 	  *ref = NULL;
 	  return 0;
 	}
@@ -869,12 +851,14 @@  _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
 	  const char *versionstr = version ? ", version " : "";
 	  const char *versionname = (version && version->name
 				     ? version->name : "");
-
+	  struct dl_exception exception;
 	  /* XXX We cannot translate the message.  */
-	  _dl_signal_cerror (0, DSO_FILENAME (reference_name),
-			     N_("symbol lookup error"),
-			     make_string ("undefined symbol: ", undef_name,
-					  versionstr, versionname));
+	  _dl_exception_create_format
+	    (&exception, DSO_FILENAME (reference_name),
+	     "undefined symbol: %s%s%s",
+	     undef_name, versionstr, versionname);
+	  _dl_signal_cexception (0, &exception, N_("symbol lookup error"));
+	  _dl_exception_free (&exception);
 	}
       *ref = NULL;
       return 0;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index cec54db413..91a1d1a4f8 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -643,11 +643,8 @@  no more namespaces available for dlmopen()"));
   args.argv = argv;
   args.env = env;
 
-  const char *objname;
-  const char *errstring;
-  bool malloced;
-  int errcode = _dl_catch_error (&objname, &errstring, &malloced,
-				 dl_open_worker, &args);
+  struct dl_exception exception;
+  int errcode = _dl_catch_exception (&exception, dl_open_worker, &args);
 
 #if defined USE_LDCONFIG && !defined MAP_COPY
   /* We must unmap the cache file.  */
@@ -655,7 +652,7 @@  no more namespaces available for dlmopen()"));
 #endif
 
   /* See if an error occurred during loading.  */
-  if (__glibc_unlikely (errstring != NULL))
+  if (__glibc_unlikely (exception.errstring != NULL))
     {
       /* Remove the object from memory.  It may be in an inconsistent
 	 state if relocation failed, for example.  */
@@ -679,28 +676,8 @@  no more namespaces available for dlmopen()"));
       /* Release the lock.  */
       __rtld_lock_unlock_recursive (GL(dl_load_lock));
 
-      /* Make a local copy of the error string so that we can release the
-	 memory allocated for it.  */
-      size_t len_errstring = strlen (errstring) + 1;
-      char *local_errstring;
-      if (objname == errstring + len_errstring)
-	{
-	  size_t total_len = len_errstring + strlen (objname) + 1;
-	  local_errstring = alloca (total_len);
-	  memcpy (local_errstring, errstring, total_len);
-	  objname = local_errstring + len_errstring;
-	}
-      else
-	{
-	  local_errstring = alloca (len_errstring);
-	  memcpy (local_errstring, errstring, len_errstring);
-	}
-
-      if (malloced)
-	free ((char *) errstring);
-
       /* Reraise the error.  */
-      _dl_signal_error (errcode, objname, NULL, local_errstring);
+      _dl_signal_exception (errcode, &exception, NULL);
     }
 
   assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
diff --git a/elf/dl-sym.c b/elf/dl-sym.c
index 7cd6e97643..fb54a91858 100644
--- a/elf/dl-sym.c
+++ b/elf/dl-sym.c
@@ -119,26 +119,11 @@  do_sym (void *handle, const char *name, void *who,
 	  args.refp = &ref;
 
 	  THREAD_GSCOPE_SET_FLAG ();
-
-	  const char *objname;
-	  const char *errstring = NULL;
-	  bool malloced;
-	  int err = _dl_catch_error (&objname, &errstring, &malloced,
-				     call_dl_lookup, &args);
-
+	  struct dl_exception exception;
+	  int err = _dl_catch_exception (&exception, call_dl_lookup, &args);
 	  THREAD_GSCOPE_RESET_FLAG ();
-
-	  if (__glibc_unlikely (errstring != NULL))
-	    {
-	      /* The lookup was unsuccessful.  Rethrow the error.  */
-	      char *errstring_dup = strdupa (errstring);
-	      char *objname_dup = strdupa (objname);
-	      if (malloced)
-		free ((char *) errstring);
-
-	      _dl_signal_error (err, objname_dup, NULL, errstring_dup);
-	      /* NOTREACHED */
-	    }
+	  if (__glibc_unlikely (exception.errstring != NULL))
+	    _dl_signal_exception (err, &exception, NULL);
 
 	  result = args.map;
 	}
diff --git a/elf/dl-version.c b/elf/dl-version.c
index c00078e5e4..c0d76ad42a 100644
--- a/elf/dl-version.c
+++ b/elf/dl-version.c
@@ -27,25 +27,6 @@ 
 
 #include <assert.h>
 
-
-#define make_string(string, rest...) \
-  ({									      \
-    const char *all[] = { string, ## rest };				      \
-    size_t len, cnt;							      \
-    char *result, *cp;							      \
-									      \
-    len = 1;								      \
-    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
-      len += strlen (all[cnt]);						      \
-									      \
-    cp = result = alloca (len);						      \
-    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
-      cp = __stpcpy (cp, all[cnt]);					      \
-									      \
-    result;								      \
-  })
-
-
 static inline struct link_map *
 __attribute ((always_inline))
 find_needed (const char *name, struct link_map *map)
@@ -78,8 +59,8 @@  match_symbol (const char *name, Lmid_t ns, ElfW(Word) hash, const char *string,
   ElfW(Addr) def_offset;
   ElfW(Verdef) *def;
   /* Initialize to make the compiler happy.  */
-  const char *errstring = NULL;
   int result = 0;
+  struct dl_exception exception;
 
   /* Display information about what we are doing while debugging.  */
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS))
@@ -96,8 +77,9 @@  checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
       if (verbose)
 	{
 	  /* XXX We cannot translate the messages.  */
-	  errstring = make_string ("\
-no version information available (required by ", name, ")");
+	  _dl_exception_create_format
+	    (&exception, DSO_FILENAME (map->l_name),
+	     "no version information available (required by %s)", name);
 	  goto call_cerror;
 	}
       return 0;
@@ -116,10 +98,10 @@  no version information available (required by ", name, ")");
 	  char buf[20];
 	  buf[sizeof (buf) - 1] = '\0';
 	  /* XXX We cannot translate the message.  */
-	  errstring = make_string ("unsupported version ",
-				   _itoa (def->vd_version,
-					  &buf[sizeof (buf) - 1], 10, 0),
-				   " of Verdef record");
+	  _dl_exception_create_format
+	    (&exception, DSO_FILENAME (map->l_name),
+	     "unsupported version %s of Verdef record",
+	     _itoa (def->vd_version, &buf[sizeof (buf) - 1], 10, 0));
 	  result = 1;
 	  goto call_cerror;
 	}
@@ -150,20 +132,22 @@  no version information available (required by ", name, ")");
       if (verbose)
 	{
 	  /* XXX We cannot translate the message.  */
-	  errstring = make_string ("weak version `", string,
-				   "' not found (required by ", name, ")");
+	  _dl_exception_create_format
+	    (&exception, DSO_FILENAME (map->l_name),
+	     "weak version `%s' not found (required by %s)", string, name);
 	  goto call_cerror;
 	}
       return 0;
     }
 
   /* XXX We cannot translate the message.  */
-  errstring = make_string ("version `", string, "' not found (required by ",
-			   name, ")");
+  _dl_exception_create_format
+    (&exception, DSO_FILENAME (map->l_name),
+     "version `%s' not found (required by %s)", string, name);
   result = 1;
  call_cerror:
-  _dl_signal_cerror (0, DSO_FILENAME (map->l_name),
-		     N_("version lookup error"), errstring);
+  _dl_signal_cexception (0, &exception, N_("version lookup error"));
+  _dl_exception_free (&exception);
   return result;
 }
 
@@ -181,8 +165,8 @@  _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
   /* We need to find out which is the highest version index used
     in a dependecy.  */
   unsigned int ndx_high = 0;
+  struct dl_exception exception;
   /* Initialize to make the compiler happy.  */
-  const char *errstring = NULL;
   int errval = 0;
 
   /* If we don't have a string table, we must be ok.  */
@@ -205,13 +189,12 @@  _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
 	  char buf[20];
 	  buf[sizeof (buf) - 1] = '\0';
 	  /* XXX We cannot translate the message.  */
-	  errstring = make_string ("unsupported version ",
-				   _itoa (ent->vn_version,
-					  &buf[sizeof (buf) - 1], 10, 0),
-				   " of Verneed record\n");
+	  _dl_exception_create_format
+	    (&exception, DSO_FILENAME (map->l_name),
+	     "unsupported version %s of Verneed record",
+	     _itoa (ent->vn_version, &buf[sizeof (buf) - 1], 10, 0));
 	call_error:
-	  _dl_signal_error (errval, DSO_FILENAME (map->l_name),
-			    NULL, errstring);
+	  _dl_signal_exception (errval, &exception, NULL);
 	}
 
       while (1)
@@ -293,7 +276,9 @@  _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
 	calloc (ndx_high + 1, sizeof (*map->l_versions));
       if (__glibc_unlikely (map->l_versions == NULL))
 	{
-	  errstring = N_("cannot allocate version reference table");
+	  _dl_exception_create
+	    (&exception, DSO_FILENAME (map->l_name),
+	     N_("cannot allocate version reference table"));
 	  errval = ENOMEM;
 	  goto call_error;
 	}
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 4508365871..1c0b9cb32e 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -732,31 +732,88 @@  _dl_dprintf (int fd, const char *fmt, ...)
   while (1)
 
 
-/* This function is called by all the internal dynamic linker functions
-   when they encounter an error.  ERRCODE is either an `errno' code or
-   zero; OBJECT is the name of the problematical shared object, or null if
-   it is a general problem; ERRSTRING is a string describing the specific
-   problem.  */
+/* An exception raised by the _dl_signal_error function family and
+   caught by _dl_catch_error function family.  Exceptions themselves
+   are copied as part of the raise operation, but the strings are
+   not.  */
+struct dl_exception
+{
+  const char *objname;
+  const char *errstring;
+
+  /* This buffer typically stores both objname and errstring
+     above.  */
+  char *message_buffer;
+};
+
+/* Creates a new exception.  This calls malloc; if allocation fails,
+   dummy values are inserted.  OBJECT is the name of the problematical
+   shared object, or null if its a general problem.  ERRSTRING is a
+   string describing the specific problem.  */
+void _dl_exception_create (struct dl_exception *, const char *object,
+			   const char *errstring)
+  __attribute__ ((nonnull (1, 3)));
+rtld_hidden_proto (_dl_exception_create)
+
+/* Like _dl_exception_create, but create errstring from a format
+   string FMT.  Currently, only "%s" and "%%" are supported as format
+   directives.  */
+void _dl_exception_create_format (struct dl_exception *, const char *objname,
+				  const char *fmt, ...)
+  __attribute__ ((nonnull (1, 3), format (printf, 3, 4)));
+rtld_hidden_proto (_dl_exception_create_format)
+
+/* Deallocate the exception, freeing allocated buffers (if
+   possible).  */
+void _dl_exception_free (struct dl_exception *)
+  __attribute__ ((nonnull (1)));
+rtld_hidden_proto (_dl_exception_free)
+
+/* This function is called by all the internal dynamic linker
+   functions when they encounter an error.  ERRCODE is either an
+   `errno' code or zero; it specifies the return value of
+   _dl_catch_error.  OCCASION is included in the error message if the
+   process is terminated immediately.  */
+void _dl_signal_exception (int errcode, struct dl_exception *,
+			   const char *occasion)
+  __attribute__ ((__noreturn__));
+libc_hidden_proto (_dl_signal_exception)
+
+/* Like _dl_signal_exception, but creates the exception first.  */
 extern void _dl_signal_error (int errcode, const char *object,
-			      const char *occurred, const char *errstring)
+			      const char *occasion, const char *errstring)
      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.  This is only used during ld.so bootstrap.  In
-   static and profiled builds, this is equivalent to
-   _dl_signal_error.  */
+/* Like _dl_signal_exception, but may return when called in the
+   context of _dl_receive_error.  This is only used during ld.so
+   bootstrap.  In static and profiled builds, this is equivalent to
+   _dl_signal_exception.  */
+#if IS_IN (rtld)
+extern void _dl_signal_cexception (int errcode, struct dl_exception *,
+				   const char *occasion) attribute_hidden;
+#else
+__attribute__ ((always_inline))
+static inline void
+_dl_signal_cexception (int errcode, struct dl_exception *exception,
+		       const char *occasion)
+{
+  _dl_signal_exception (errcode, exception, occasion);
+}
+#endif
+
+/* See _dl_signal_cexception above.  */
 #if IS_IN (rtld)
 extern void _dl_signal_cerror (int errcode, const char *object,
-			       const char *occation, const char *errstring)
+			       const char *occasion, 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)
+			       const char *occasion, const char *errstring)
 {
-  _dl_signal_error (errcode, object, occation, errstring);
+  _dl_signal_error (errcode, object, occasion, errstring);
 }
 #endif
 
@@ -768,20 +825,28 @@  extern void _dl_receive_error (receiver_fct fct, void (*operate) (void *),
 			       void *args)
      internal_function attribute_hidden;
 
-/* Call OPERATE, catching errors from `dl_signal_error'.  If there is no
-   error, *ERRSTRING is set to null.  If there is an error, *ERRSTRING is
-   set to a string constructed from the strings passed to _dl_signal_error,
-   and the error code passed is the return value and *OBJNAME is set to
-   the object name which experienced the problems.  ERRSTRING if nonzero
-   points to a malloc'ed string which the caller has to free after use.
-   ARGS is passed as argument to OPERATE.  MALLOCEDP is set to true only
-   if the returned string is allocated using the libc's malloc.  */
+/* Call OPERATE, catching errors from `_dl_signal_error' and related
+   functions.  If there is no error, *ERRSTRING is set to null.  If
+   there is an error, *ERRSTRING is set to a string constructed from
+   the strings passed to _dl_signal_error, and the error code passed
+   is the return value and *OBJNAME is set to the object name which
+   experienced the problems.  ERRSTRING if nonzero points to a
+   malloc'ed string which the caller has to free after use.  ARGS is
+   passed as argument to OPERATE.  MALLOCEDP is set to true only if
+   the returned string is allocated using the libc's malloc.  */
 extern int _dl_catch_error (const char **objname, const char **errstring,
 			    bool *mallocedp, void (*operate) (void *),
 			    void *args)
      internal_function;
 libc_hidden_proto (_dl_catch_error)
 
+/* Call OPERATE (ARGS).  If no error occurs, set *EXCEPTION to zero.
+   Otherwise, store a copy of the raised exception in *EXCEPTION,
+   which has to be freed by _dl_exception_free.  */
+int _dl_catch_exception (struct dl_exception *exception,
+			 void (*operate) (void *), void *args);
+libc_hidden_proto (_dl_catch_exception)
+
 /* Open the shared object NAME and map in its segments.
    LOADER's DT_RPATH is used in searching for NAME.
    If the object is already opened, returns its existing map.  */
diff --git a/sysdeps/generic/localplt.data b/sysdeps/generic/localplt.data
index 81c741b038..2d5c66ae28 100644
--- a/sysdeps/generic/localplt.data
+++ b/sysdeps/generic/localplt.data
@@ -16,3 +16,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/aarch64/localplt.data b/sysdeps/unix/sysv/linux/aarch64/localplt.data
index bb18ff9bb2..a60053b914 100644
--- a/sysdeps/unix/sysv/linux/aarch64/localplt.data
+++ b/sysdeps/unix/sysv/linux/aarch64/localplt.data
@@ -18,3 +18,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/alpha/localplt.data b/sysdeps/unix/sysv/linux/alpha/localplt.data
index 1f0e3b494e..c69eb04ce5 100644
--- a/sysdeps/unix/sysv/linux/alpha/localplt.data
+++ b/sysdeps/unix/sysv/linux/alpha/localplt.data
@@ -35,3 +35,5 @@  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
+ld.so: _dl_signal_exception + RELA R_ALPHA_GLOB_DAT
+ld.so: _dl_catch_exception + RELA R_ALPHA_GLOB_DAT
diff --git a/sysdeps/unix/sysv/linux/arm/localplt.data b/sysdeps/unix/sysv/linux/arm/localplt.data
index 19d3299d98..7bd541c28a 100644
--- a/sysdeps/unix/sysv/linux/arm/localplt.data
+++ b/sysdeps/unix/sysv/linux/arm/localplt.data
@@ -17,3 +17,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/hppa/localplt.data b/sysdeps/unix/sysv/linux/hppa/localplt.data
index db9e24b090..3279c0af05 100644
--- a/sysdeps/unix/sysv/linux/hppa/localplt.data
+++ b/sysdeps/unix/sysv/linux/hppa/localplt.data
@@ -21,3 +21,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/i386/localplt.data b/sysdeps/unix/sysv/linux/i386/localplt.data
index 8ea4333846..f6f20a5d15 100644
--- a/sysdeps/unix/sysv/linux/i386/localplt.data
+++ b/sysdeps/unix/sysv/linux/i386/localplt.data
@@ -16,3 +16,5 @@  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
+ld.so: _dl_signal_exception + REL R_386_GLOB_DAT
+ld.so: _dl_catch_exception + REL R_386_GLOB_DAT
diff --git a/sysdeps/unix/sysv/linux/ia64/localplt.data b/sysdeps/unix/sysv/linux/ia64/localplt.data
index fd2b98c8b6..3820e2a4e6 100644
--- a/sysdeps/unix/sysv/linux/ia64/localplt.data
+++ b/sysdeps/unix/sysv/linux/ia64/localplt.data
@@ -15,3 +15,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/m68k/localplt.data b/sysdeps/unix/sysv/linux/m68k/localplt.data
index 1a2acfdb93..c70d6ea301 100644
--- a/sysdeps/unix/sysv/linux/m68k/localplt.data
+++ b/sysdeps/unix/sysv/linux/m68k/localplt.data
@@ -15,3 +15,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/microblaze/localplt.data b/sysdeps/unix/sysv/linux/microblaze/localplt.data
index ca476bedd8..8ca23897df 100644
--- a/sysdeps/unix/sysv/linux/microblaze/localplt.data
+++ b/sysdeps/unix/sysv/linux/microblaze/localplt.data
@@ -16,3 +16,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/nios2/localplt.data b/sysdeps/unix/sysv/linux/nios2/localplt.data
index b0d6dcae55..4430a5891e 100644
--- a/sysdeps/unix/sysv/linux/nios2/localplt.data
+++ b/sysdeps/unix/sysv/linux/nios2/localplt.data
@@ -36,3 +36,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
index 50006317c7..e822e0a480 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/localplt.data
@@ -14,3 +14,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
index 1c20d2f2b4..fead931d4e 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/localplt.data
@@ -44,3 +44,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data b/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
index 6f8ed25922..c1209336d2 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/localplt.data
@@ -13,3 +13,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/s390/localplt.data b/sysdeps/unix/sysv/linux/s390/localplt.data
index 50006317c7..e822e0a480 100644
--- a/sysdeps/unix/sysv/linux/s390/localplt.data
+++ b/sysdeps/unix/sysv/linux/s390/localplt.data
@@ -14,3 +14,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/sh/localplt.data b/sysdeps/unix/sysv/linux/sh/localplt.data
index f1f5effc24..2753547d97 100644
--- a/sysdeps/unix/sysv/linux/sh/localplt.data
+++ b/sysdeps/unix/sysv/linux/sh/localplt.data
@@ -19,3 +19,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data b/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
index 2f6ff3c3a6..1668f4017e 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/localplt.data
@@ -26,3 +26,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data b/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
index 912bd1a16e..b881b9096d 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/localplt.data
@@ -27,3 +27,5 @@  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
+ld.so: _dl_signal_exception
+ld.so: _dl_catch_exception
diff --git a/sysdeps/x86_64/localplt.data b/sysdeps/x86_64/localplt.data
index a1840cff31..c27a02b66a 100644
--- a/sysdeps/x86_64/localplt.data
+++ b/sysdeps/x86_64/localplt.data
@@ -18,3 +18,5 @@  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
+ld.so: _dl_signal_exception + RELA R_X86_64_GLOB_DAT
+ld.so: _dl_catch_exception + RELA R_X86_64_GLOB_DAT