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