about summary refs log tree commit diff
path: root/elf/dl-catch.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dl-catch.c')
-rw-r--r--elf/dl-catch.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/elf/dl-catch.c b/elf/dl-catch.c
new file mode 100644
index 0000000000..54763dd34e
--- /dev/null
+++ b/elf/dl-catch.c
@@ -0,0 +1,261 @@
+/* Exception handling in the dynamic linker.
+   Copyright (C) 1995-2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <libintl.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <stdio.h>
+#include <tls.h>
+
+/* This structure communicates state between _dl_catch_error and
+   _dl_signal_error.  */
+struct rtld_catch
+  {
+    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.  */
+  };
+
+/* Multiple threads at once can use the `_dl_catch_error' function.
+   The calls can come from `_dl_map_object_deps', `_dlerror_run', or
+   from any of the libc functionality which loads dynamic objects
+   (NSS, iconv).  Therefore we have to be prepared to save the state
+   in thread-local memory.  We use THREAD_GETMEM and THREAD_SETMEM
+   instead of ELF TLS because ELF TLS is not available in the dynamic
+   loader.  Additionally, the exception handling mechanism must be
+   usable before the TCB has been set up, which is why
+   rtld_catch_notls is used if !__rtld_tls_init_tp_called.  This is
+   not needed for static builds, where initialization completes before
+   static dlopen etc. can be called.  */
+
+#if IS_IN (rtld)
+static struct rtld_catch *rtld_catch_notls;
+#endif
+
+static struct rtld_catch *
+get_catch (void)
+{
+#if IS_IN (rtld)
+  if (!__rtld_tls_init_tp_called)
+    return rtld_catch_notls;
+  else
+#endif
+    return THREAD_GETMEM (THREAD_SELF, rtld_catch);
+}
+
+static void
+set_catch (struct rtld_catch *catch)
+{
+#if IS_IN (rtld)
+  if (!__rtld_tls_init_tp_called)
+    rtld_catch_notls = catch;
+  else
+#endif
+    THREAD_SETMEM (THREAD_SELF, rtld_catch, catch);
+}
+
+/* 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 rtld_catch *lcatch = get_catch ();
+  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);
+}
+rtld_hidden_def (_dl_signal_exception)
+
+void
+_dl_signal_error (int errcode, const char *objname, const char *occation,
+		  const char *errstring)
+{
+  struct rtld_catch *lcatch = get_catch ();
+
+  if (! errstring)
+    errstring = N_("DYNAMIC LINKER BUG!!!");
+
+  if (lcatch != NULL)
+    {
+      _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
+    fatal_error (errcode, objname, occation, errstring);
+}
+rtld_hidden_def (_dl_signal_error)
+
+#if IS_IN (rtld)
+/* This points to a function which is called when a continuable error is
+   received.  Unlike the handling of `catch' this function may return.
+   The arguments will be the `errstring' and `objname'.
+
+   Since this functionality is not used in normal programs (only in ld.so)
+   we do not care about multi-threaded programs here.  We keep this as a
+   global variable.  */
+static receiver_fct receiver;
+
+void
+_dl_signal_cexception (int errcode, struct dl_exception *exception,
+		       const char *occasion)
+{
+  if (__builtin_expect (GLRO(dl_debug_mask)
+			& ~(DL_DEBUG_STATISTICS), 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
+_dl_signal_cerror (int errcode, const char *objname, const char *occation,
+		   const char *errstring)
+{
+  if (__builtin_expect (GLRO(dl_debug_mask)
+			& ~(DL_DEBUG_STATISTICS), 0))
+    _dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occation,
+		      errstring, receiver ? "continued" : "fatal");
+
+  if (receiver)
+    {
+      /* We are inside _dl_receive_error.  Call the user supplied
+	 handler and resume the work.  The receiver will still be
+	 installed.  */
+      (*receiver) (errcode, objname, errstring);
+    }
+  else
+    _dl_signal_error (errcode, objname, occation, errstring);
+}
+
+void
+_dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
+{
+  struct rtld_catch *old_catch = get_catch ();
+  receiver_fct old_receiver = receiver;
+
+  /* Set the new values.  */
+  set_catch (NULL);
+  receiver = fct;
+
+  (*operate) (args);
+
+  set_catch (old_catch);
+  receiver = old_receiver;
+}
+#endif
+
+int
+_dl_catch_exception (struct dl_exception *exception,
+		     void (*operate) (void *), void *args)
+{
+  /* If exception is NULL, temporarily disable exception handling.
+     Exceptions during operate (args) are fatal.  */
+  if (exception == NULL)
+    {
+      struct rtld_catch *old_catch = get_catch ();
+      set_catch (NULL);
+      operate (args);
+      /* If we get here, the operation was successful.  */
+      set_catch (old_catch);
+      return 0;
+    }
+
+  /* We need not handle `receiver' since setting a `catch' is handled
+     before it.  */
+
+  /* Only this needs to be marked volatile, because it is the only local
+     variable that gets changed between the setjmp invocation and the
+     longjmp call.  All others are just set here (before setjmp) and read
+     in _dl_signal_error (before longjmp).  */
+  volatile int errcode;
+
+  struct rtld_catch c;
+  /* Don't use an initializer since we don't need to clear C.env.  */
+  c.exception = exception;
+  c.errcode = &errcode;
+
+  struct rtld_catch *old = get_catch ();
+  set_catch (&c);
+
+  /* Do not save the signal mask.  */
+  if (__builtin_expect (__sigsetjmp (c.env, 0), 0) == 0)
+    {
+      (*operate) (args);
+      set_catch (old);
+      *exception = (struct dl_exception) { NULL };
+      return 0;
+    }
+
+  /* We get here only if we longjmp'd out of OPERATE.
+     _dl_signal_exception has already stored values into
+     *EXCEPTION.  */
+  set_catch (old);
+  return errcode;
+}
+rtld_hidden_def (_dl_catch_exception)
+
+int
+_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;
+}