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-2024 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;
}
}
|