about summary refs log tree commit diff
path: root/elf/dl-exception.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-08-10 13:40:22 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-08-10 16:54:57 +0200
commit2449ae7b2da24c9940962304a3e44bc80e389265 (patch)
treec2cfdcfc3a90731d2da26dda79984eda95e9079e /elf/dl-exception.c
parentf87cc2bfba9b844da48a63441c6099342b1551c7 (diff)
downloadglibc-2449ae7b2da24c9940962304a3e44bc80e389265.tar.gz
glibc-2449ae7b2da24c9940962304a3e44bc80e389265.tar.xz
glibc-2449ae7b2da24c9940962304a3e44bc80e389265.zip
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.
Diffstat (limited to 'elf/dl-exception.c')
-rw-r--r--elf/dl-exception.c202
1 files changed, 202 insertions, 0 deletions
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)