mirror of git://sourceware.org/git/glibc.git
				
				
				
			
		
			
				
	
	
		
			252 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Return error detail for failing <dlfcn.h> functions.
 | |
|    Copyright (C) 1995-2000,2002,2003,2004,2005 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 <bits/libc-lock.h>
 | |
| #include <ldsodefs.h>
 | |
| 
 | |
| #if !defined SHARED && defined 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 (__builtin_expect (_dlfcn_hook != NULL, 0))
 | |
|     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 namespave.  */
 | |
|       struct link_map *map = NULL;
 | |
|       Dl_info info;
 | |
|       if (_dl_addr (check_free, &info, &map, NULL) != 0
 | |
| 	  && map != NULL && map->l_ns == 0)
 | |
| 	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
 |