(root)/
glibc-2.38/
dlfcn/
dlerror.c
       1  /* Return error detail for failing <dlfcn.h> functions.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <dlfcn.h>
      20  #include <libintl.h>
      21  #include <stdbool.h>
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <libc-lock.h>
      26  #include <ldsodefs.h>
      27  #include <libc-symbols.h>
      28  #include <assert.h>
      29  #include <dlerror.h>
      30  
      31  char *
      32  __dlerror (void)
      33  {
      34  # ifdef SHARED
      35    if (GLRO (dl_dlfcn_hook) != NULL)
      36      return GLRO (dl_dlfcn_hook)->dlerror ();
      37  # endif
      38  
      39    struct dl_action_result *result = __libc_dlerror_result;
      40  
      41    /* No libdl function has been called.  No error is possible.  */
      42    if (result == NULL)
      43      return NULL;
      44  
      45    /* For an early malloc failure, clear the error flag and return the
      46       error message.  This marks the error as delivered.  */
      47    if (result == dl_action_result_malloc_failed)
      48      {
      49        __libc_dlerror_result = NULL;
      50        return (char *) "out of memory";
      51      }
      52  
      53    /* Placeholder object.  This can be observed in a recursive call,
      54       e.g. from an ELF constructor.  */
      55    if (result->errstring == NULL)
      56      return NULL;
      57  
      58    /* If we have already reported the error, we can free the result and
      59       return NULL.  See __libc_dlerror_result_free.  */
      60    if (result->returned)
      61      {
      62        __libc_dlerror_result = NULL;
      63        dl_action_result_errstring_free (result);
      64        free (result);
      65        return NULL;
      66      }
      67  
      68    assert (result->errstring != NULL);
      69  
      70    /* Create the combined error message.  */
      71    char *buf;
      72    int n;
      73    if (result->errcode == 0)
      74      n = __asprintf (&buf, "%s%s%s",
      75  		    result->objname,
      76  		    result->objname[0] == '\0' ? "" : ": ",
      77  		    _(result->errstring));
      78    else
      79      {
      80        __set_errno (result->errcode);
      81        n = __asprintf (&buf, "%s%s%s: %m",
      82  		      result->objname,
      83  		      result->objname[0] == '\0' ? "" : ": ",
      84  		      _(result->errstring));
      85        /* Set errno again in case asprintf clobbered it.  */
      86        __set_errno (result->errcode);
      87      }
      88  
      89    /* Mark the error as delivered.  */
      90    result->returned = true;
      91  
      92    if (n >= 0)
      93      {
      94        /* Replace the error string with the newly allocated one.  */
      95        dl_action_result_errstring_free (result);
      96        result->errstring = buf;
      97        result->errstring_source = dl_action_result_errstring_local;
      98        return buf;
      99      }
     100    else
     101      /* We could not create the combined error message, so use the
     102         existing string as a fallback.  */
     103      return result->errstring;
     104  }
     105  versioned_symbol (libc, __dlerror, dlerror, GLIBC_2_34);
     106  
     107  #if OTHER_SHLIB_COMPAT (libdl, GLIBC_2_0, GLIBC_2_34)
     108  compat_symbol (libdl, __dlerror, dlerror, GLIBC_2_0);
     109  #endif
     110  
     111  int
     112  _dlerror_run (void (*operate) (void *), void *args)
     113  {
     114    struct dl_action_result *result = __libc_dlerror_result;
     115    if (result != NULL)
     116      {
     117        if (result == dl_action_result_malloc_failed)
     118  	{
     119  	  /* Clear the previous error.  */
     120  	  __libc_dlerror_result = NULL;
     121  	  result = NULL;
     122  	}
     123        else
     124  	{
     125  	  /* There is an existing object.  Free its error string, but
     126  	     keep the object.  */
     127  	  dl_action_result_errstring_free (result);
     128  	  /* Mark the object as not containing an error.  This ensures
     129  	     that call to dlerror from, for example, an ELF
     130  	     constructor will not notice this result object.  */
     131  	  result->errstring = NULL;
     132  	}
     133      }
     134  
     135    const char *objname;
     136    const char *errstring;
     137    bool malloced;
     138    int errcode = GLRO (dl_catch_error) (&objname, &errstring, &malloced,
     139  				       operate, args);
     140  
     141    /* ELF constructors or destructors may have indirectly altered the
     142       value of __libc_dlerror_result, therefore reload it.  */
     143    result = __libc_dlerror_result;
     144  
     145    if (errstring == NULL)
     146      {
     147        /* There is no error.  We no longer need the result object if it
     148  	 does not contain an error.  However, a recursive call may
     149  	 have added an error even if this call did not cause it.  Keep
     150  	 the other error.  */
     151        if (result != NULL && result->errstring == NULL)
     152  	{
     153  	  __libc_dlerror_result = NULL;
     154  	  free (result);
     155  	}
     156        return 0;
     157      }
     158    else
     159      {
     160        /* A new error occurred.  Check if a result object has to be
     161  	 allocated.  */
     162        if (result == NULL || result == dl_action_result_malloc_failed)
     163  	{
     164  	  /* Allocating storage for the error message after the fact
     165  	     is not ideal.  But this avoids an infinite recursion in
     166  	     case malloc itself calls libdl functions (without
     167  	     triggering errors).  */
     168  	  result = malloc (sizeof (*result));
     169  	  if (result == NULL)
     170  	    {
     171  	      /* Assume that the dlfcn failure was due to a malloc
     172  		 failure, too.  */
     173  	      if (malloced)
     174  		dl_error_free ((char *) errstring);
     175  	      __libc_dlerror_result = dl_action_result_malloc_failed;
     176  	      return 1;
     177  	    }
     178  	  __libc_dlerror_result = result;
     179  	}
     180        else
     181  	/* Deallocate the existing error message from a recursive
     182  	   call, but reuse the result object.  */
     183  	dl_action_result_errstring_free (result);
     184  
     185        result->errcode = errcode;
     186        result->objname = objname;
     187        result->errstring = (char *) errstring;
     188        result->returned = false;
     189        /* In case of an error, the malloced flag indicates whether the
     190  	 error string is constant or not.  */
     191        if (malloced)
     192  	result->errstring_source = dl_action_result_errstring_rtld;
     193        else
     194  	result->errstring_source = dl_action_result_errstring_constant;
     195  
     196        return 1;
     197      }
     198  }