| /* Return error detail for failing <dlfcn.h> functions. | 
 |    Copyright (C) 1995-2016 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 = GLRO(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 |