diff options
Diffstat (limited to 'elf/dl-exception.c')
-rw-r--r-- | elf/dl-exception.c | 202 |
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) |