mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-28 05:21:13 +00:00
199 lines
5.8 KiB
C
199 lines
5.8 KiB
C
/* Return error detail for failing <dlfcn.h> functions.
|
|
Copyright (C) 1995-2023 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;
|
|
}
|
|
}
|