diff options
Diffstat (limited to 'REORG.TODO/dlfcn/dlerror.c')
-rw-r--r-- | REORG.TODO/dlfcn/dlerror.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/REORG.TODO/dlfcn/dlerror.c b/REORG.TODO/dlfcn/dlerror.c new file mode 100644 index 0000000000..c0ead7dcb6 --- /dev/null +++ b/REORG.TODO/dlfcn/dlerror.c @@ -0,0 +1,254 @@ +/* Return error detail for failing <dlfcn.h> functions. + 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 <dlfcn.h> +#include <libintl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libc-lock.h> +#include <ldsodefs.h> + +#if !defined SHARED && IS_IN (libdl) + +char * +dlerror (void) +{ + return __dlerror (); +} + +#else + +/* Type for storing results of dynamic loading actions. */ +struct dl_action_result + { + int errcode; + int returned; + bool malloced; + const char *objname; + const char *errstring; + }; +static struct dl_action_result last_result; +static struct dl_action_result *static_buf; + +/* This is the key for the thread specific memory. */ +static __libc_key_t key; +__libc_once_define (static, once); + +/* Destructor for the thread-specific data. */ +static void init (void); +static void free_key_mem (void *mem); + + +char * +__dlerror (void) +{ + char *buf = NULL; + struct dl_action_result *result; + +# ifdef SHARED + if (__glibc_unlikely (_dlfcn_hook != NULL)) + return _dlfcn_hook->dlerror (); +# endif + + /* If we have not yet initialized the buffer do it now. */ + __libc_once (once, init); + + /* Get error string. */ + result = (struct dl_action_result *) __libc_getspecific (key); + if (result == NULL) + result = &last_result; + + /* Test whether we already returned the string. */ + if (result->returned != 0) + { + /* We can now free the string. */ + if (result->errstring != NULL) + { + if (strcmp (result->errstring, "out of memory") != 0) + free ((char *) result->errstring); + result->errstring = NULL; + } + } + else if (result->errstring != NULL) + { + buf = (char *) result->errstring; + int n; + if (result->errcode == 0) + n = __asprintf (&buf, "%s%s%s", + result->objname, + result->objname[0] == '\0' ? "" : ": ", + _(result->errstring)); + else + n = __asprintf (&buf, "%s%s%s: %s", + result->objname, + result->objname[0] == '\0' ? "" : ": ", + _(result->errstring), + strerror (result->errcode)); + if (n != -1) + { + /* We don't need the error string anymore. */ + if (strcmp (result->errstring, "out of memory") != 0) + free ((char *) result->errstring); + result->errstring = buf; + } + + /* Mark the error as returned. */ + result->returned = 1; + } + + return buf; +} +# ifdef SHARED +strong_alias (__dlerror, dlerror) +# endif + +int +internal_function +_dlerror_run (void (*operate) (void *), void *args) +{ + struct dl_action_result *result; + + /* If we have not yet initialized the buffer do it now. */ + __libc_once (once, init); + + /* Get error string and number. */ + if (static_buf != NULL) + result = static_buf; + else + { + /* We don't use the static buffer and so we have a key. Use it + to get the thread-specific buffer. */ + result = __libc_getspecific (key); + if (result == NULL) + { + result = (struct dl_action_result *) calloc (1, sizeof (*result)); + if (result == NULL) + /* We are out of memory. Since this is no really critical + situation we carry on by using the global variable. + This might lead to conflicts between the threads but + they soon all will have memory problems. */ + result = &last_result; + else + /* Set the tsd. */ + __libc_setspecific (key, result); + } + } + + if (result->errstring != NULL) + { + /* Free the error string from the last failed command. This can + happen if `dlerror' was not run after an error was found. */ + if (result->malloced) + free ((char *) result->errstring); + result->errstring = NULL; + } + + result->errcode = _dl_catch_error (&result->objname, &result->errstring, + &result->malloced, operate, args); + + /* If no error we mark that no error string is available. */ + result->returned = result->errstring == NULL; + + return result->errstring != NULL; +} + + +/* Initialize buffers for results. */ +static void +init (void) +{ + if (__libc_key_create (&key, free_key_mem)) + /* Creating the key failed. This means something really went + wrong. In any case use a static buffer which is better than + nothing. */ + static_buf = &last_result; +} + + +static void +check_free (struct dl_action_result *rec) +{ + if (rec->errstring != NULL + && strcmp (rec->errstring, "out of memory") != 0) + { + /* We can free the string only if the allocation happened in the + C library used by the dynamic linker. This means, it is + always the C library in the base namespace. When we're statically + linked, the dynamic linker is part of the program and so always + uses the same C library we use here. */ +#ifdef SHARED + struct link_map *map = NULL; + Dl_info info; + if (_dl_addr (check_free, &info, &map, NULL) != 0 && map->l_ns == 0) +#endif + free ((char *) rec->errstring); + } +} + + +static void +__attribute__ ((destructor)) +fini (void) +{ + check_free (&last_result); +} + + +/* Free the thread specific data, this is done if a thread terminates. */ +static void +free_key_mem (void *mem) +{ + check_free ((struct dl_action_result *) mem); + + free (mem); + __libc_setspecific (key, NULL); +} + +# ifdef SHARED + +struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon)); +libdl_hidden_data_def (_dlfcn_hook) + +# else + +static struct dlfcn_hook _dlfcn_hooks = + { + .dlopen = __dlopen, + .dlclose = __dlclose, + .dlsym = __dlsym, + .dlvsym = __dlvsym, + .dlerror = __dlerror, + .dladdr = __dladdr, + .dladdr1 = __dladdr1, + .dlinfo = __dlinfo, + .dlmopen = __dlmopen + }; + +void +__libc_register_dlfcn_hook (struct link_map *map) +{ + struct dlfcn_hook **hook; + + hook = (struct dlfcn_hook **) __libc_dlsym_private (map, "_dlfcn_hook"); + if (hook != NULL) + *hook = &_dlfcn_hooks; +} +# endif +#endif |