Blame dlfcn/dlerror.c

Packit 6c4009
/* Return error detail for failing <dlfcn.h> functions.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include <libc-symbols.h>
Packit 6c4009
Packit 6c4009
#if !defined SHARED && IS_IN (libdl)
Packit 6c4009
Packit 6c4009
char *
Packit 6c4009
dlerror (void)
Packit 6c4009
{
Packit 6c4009
  return __dlerror ();
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#else
Packit 6c4009
Packit 6c4009
/* Type for storing results of dynamic loading actions.  */
Packit 6c4009
struct dl_action_result
Packit 6c4009
  {
Packit 6c4009
    int errcode;
Packit 6c4009
    int returned;
Packit 6c4009
    bool malloced;
Packit 6c4009
    const char *objname;
Packit 6c4009
    const char *errstring;
Packit 6c4009
  };
Packit 6c4009
static struct dl_action_result last_result;
Packit 6c4009
static struct dl_action_result *static_buf;
Packit 6c4009
Packit 6c4009
/* This is the key for the thread specific memory.  */
Packit 6c4009
static __libc_key_t key;
Packit 6c4009
__libc_once_define (static, once);
Packit 6c4009
Packit 6c4009
/* Destructor for the thread-specific data.  */
Packit 6c4009
static void init (void);
Packit 6c4009
static void free_key_mem (void *mem);
Packit 6c4009
Packit 6c4009
Packit 6c4009
char *
Packit 6c4009
__dlerror (void)
Packit 6c4009
{
Packit 6c4009
  char *buf = NULL;
Packit 6c4009
  struct dl_action_result *result;
Packit 6c4009
Packit 6c4009
# ifdef SHARED
Packit 6c4009
  if (!rtld_active ())
Packit 6c4009
    return _dlfcn_hook->dlerror ();
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
  /* If we have not yet initialized the buffer do it now.  */
Packit 6c4009
  __libc_once (once, init);
Packit 6c4009
Packit 6c4009
  /* Get error string.  */
Packit Service 8bbac7
  result = (struct dl_action_result *) __libc_getspecific (key);
Packit Service 8bbac7
  if (result == NULL)
Packit Service 8bbac7
    result = &last_result;
Packit 6c4009
Packit 6c4009
  /* Test whether we already returned the string.  */
Packit 6c4009
  if (result->returned != 0)
Packit 6c4009
    {
Packit 6c4009
      /* We can now free the string.  */
Packit 6c4009
      if (result->errstring != NULL)
Packit 6c4009
	{
Packit 6c4009
	  if (strcmp (result->errstring, "out of memory") != 0)
Packit 6c4009
	    free ((char *) result->errstring);
Packit 6c4009
	  result->errstring = NULL;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else if (result->errstring != NULL)
Packit 6c4009
    {
Packit 6c4009
      buf = (char *) result->errstring;
Packit 6c4009
      int n;
Packit 6c4009
      if (result->errcode == 0)
Packit 6c4009
	n = __asprintf (&buf, "%s%s%s",
Packit 6c4009
			result->objname,
Packit 6c4009
			result->objname[0] == '\0' ? "" : ": ",
Packit 6c4009
			_(result->errstring));
Packit 6c4009
      else
Packit 6c4009
	n = __asprintf (&buf, "%s%s%s: %s",
Packit 6c4009
			result->objname,
Packit 6c4009
			result->objname[0] == '\0' ? "" : ": ",
Packit 6c4009
			_(result->errstring),
Packit 6c4009
			strerror (result->errcode));
Packit 6c4009
      if (n != -1)
Packit 6c4009
	{
Packit 6c4009
	  /* We don't need the error string anymore.  */
Packit 6c4009
	  if (strcmp (result->errstring, "out of memory") != 0)
Packit 6c4009
	    free ((char *) result->errstring);
Packit 6c4009
	  result->errstring = buf;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Mark the error as returned.  */
Packit 6c4009
      result->returned = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return buf;
Packit 6c4009
}
Packit 6c4009
# ifdef SHARED
Packit 6c4009
strong_alias (__dlerror, dlerror)
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
_dlerror_run (void (*operate) (void *), void *args)
Packit 6c4009
{
Packit 6c4009
  struct dl_action_result *result;
Packit 6c4009
Packit 6c4009
  /* If we have not yet initialized the buffer do it now.  */
Packit 6c4009
  __libc_once (once, init);
Packit 6c4009
Packit 6c4009
  /* Get error string and number.  */
Packit 6c4009
  if (static_buf != NULL)
Packit 6c4009
    result = static_buf;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We don't use the static buffer and so we have a key.  Use it
Packit 6c4009
	 to get the thread-specific buffer.  */
Packit 6c4009
      result = __libc_getspecific (key);
Packit 6c4009
      if (result == NULL)
Packit 6c4009
	{
Packit 6c4009
	  result = (struct dl_action_result *) calloc (1, sizeof (*result));
Packit 6c4009
	  if (result == NULL)
Packit 6c4009
	    /* We are out of memory.  Since this is no really critical
Packit 6c4009
	       situation we carry on by using the global variable.
Packit 6c4009
	       This might lead to conflicts between the threads but
Packit 6c4009
	       they soon all will have memory problems.  */
Packit 6c4009
	    result = &last_result;
Packit 6c4009
	  else
Packit 6c4009
	    /* Set the tsd.  */
Packit 6c4009
	    __libc_setspecific (key, result);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (result->errstring != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Free the error string from the last failed command.  This can
Packit 6c4009
	 happen if `dlerror' was not run after an error was found.  */
Packit 6c4009
      if (result->malloced)
Packit 6c4009
	free ((char *) result->errstring);
Packit 6c4009
      result->errstring = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  result->errcode = _dl_catch_error (&result->objname, &result->errstring,
Packit 6c4009
				     &result->malloced, operate, args);
Packit 6c4009
Packit 6c4009
  /* If no error we mark that no error string is available.  */
Packit 6c4009
  result->returned = result->errstring == NULL;
Packit 6c4009
Packit 6c4009
  return result->errstring != NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Initialize buffers for results.  */
Packit 6c4009
static void
Packit 6c4009
init (void)
Packit 6c4009
{
Packit 6c4009
  if (__libc_key_create (&key, free_key_mem))
Packit 6c4009
    /* Creating the key failed.  This means something really went
Packit 6c4009
       wrong.  In any case use a static buffer which is better than
Packit 6c4009
       nothing.  */
Packit 6c4009
    static_buf = &last_result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
check_free (struct dl_action_result *rec)
Packit 6c4009
{
Packit 6c4009
  if (rec->errstring != NULL
Packit 6c4009
      && strcmp (rec->errstring, "out of memory") != 0)
Packit 6c4009
    {
Packit 6c4009
      /* We can free the string only if the allocation happened in the
Packit 6c4009
	 C library used by the dynamic linker.  This means, it is
Packit 6c4009
	 always the C library in the base namespace.  When we're statically
Packit 6c4009
         linked, the dynamic linker is part of the program and so always
Packit 6c4009
	 uses the same C library we use here.  */
Packit 6c4009
#ifdef SHARED
Packit 6c4009
      struct link_map *map = NULL;
Packit 6c4009
      Dl_info info;
Packit 6c4009
      if (_dl_addr (check_free, &info, &map, NULL) != 0 && map->l_ns == 0)
Packit 6c4009
#endif
Packit Service d14d05
	{
Packit Service d14d05
	  free ((char *) rec->errstring);
Packit Service d14d05
	  rec->errstring = NULL;
Packit Service d14d05
	}
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
__attribute__ ((destructor))
Packit 6c4009
fini (void)
Packit 6c4009
{
Packit 6c4009
  check_free (&last_result);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Free the thread specific data, this is done if a thread terminates.  */
Packit 6c4009
static void
Packit 6c4009
free_key_mem (void *mem)
Packit 6c4009
{
Packit 6c4009
  check_free ((struct dl_action_result *) mem);
Packit 6c4009
Packit 6c4009
  free (mem);
Packit 6c4009
  __libc_setspecific (key, NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
# ifdef SHARED
Packit 6c4009
Packit 6c4009
/* Free the dlerror-related resources.  */
Packit 6c4009
void
Packit 6c4009
__dlerror_main_freeres (void)
Packit 6c4009
{
Packit Service 8bbac7
  void *mem;
Packit 6c4009
  /* Free the global memory if used.  */
Packit 6c4009
  check_free (&last_result);
Packit Service 8bbac7
  /* Free the TSD memory if used.  */
Packit Service 8bbac7
  mem = __libc_getspecific (key);
Packit Service 8bbac7
  if (mem != NULL)
Packit Service 8bbac7
    free_key_mem (mem);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
Packit 6c4009
libdl_hidden_data_def (_dlfcn_hook)
Packit 6c4009
Packit 6c4009
# else
Packit 6c4009
Packit 6c4009
static struct dlfcn_hook _dlfcn_hooks =
Packit 6c4009
  {
Packit 6c4009
    .dlopen = __dlopen,
Packit 6c4009
    .dlclose = __dlclose,
Packit 6c4009
    .dlsym = __dlsym,
Packit 6c4009
    .dlvsym = __dlvsym,
Packit 6c4009
    .dlerror = __dlerror,
Packit 6c4009
    .dladdr = __dladdr,
Packit 6c4009
    .dladdr1 = __dladdr1,
Packit 6c4009
    .dlinfo = __dlinfo,
Packit 6c4009
    .dlmopen = __dlmopen
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
__libc_register_dlfcn_hook (struct link_map *map)
Packit 6c4009
{
Packit 6c4009
  struct dlfcn_hook **hook;
Packit 6c4009
Packit 6c4009
  hook = (struct dlfcn_hook **) __libc_dlsym_private (map, "_dlfcn_hook");
Packit 6c4009
  if (hook != NULL)
Packit 6c4009
    *hook = &_dlfcn_hooks;
Packit 6c4009
}
Packit 6c4009
# endif
Packit 6c4009
#endif