1/* Return error detail for failing <dlfcn.h> functions.
2 Copyright (C) 1995-2022 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
31char *
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 (ptr: 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}
105versioned_symbol (libc, __dlerror, dlerror, GLIBC_2_34);
106
107#if OTHER_SHLIB_COMPAT (libdl, GLIBC_2_0, GLIBC_2_34)
108compat_symbol (libdl, __dlerror, dlerror, GLIBC_2_0);
109#endif
110
111int
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 (ptr: 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 (size: 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 (ptr: (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}
199

source code of glibc/dlfcn/dlerror.c