aboutsummaryrefslogtreecommitdiff
path: root/elf/dl-exception.c
diff options
context:
space:
mode:
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)