elf: Use relaxed atomics for racy accesses [BZ #19329]
[glibc.git] / dlfcn / dlerror.c
blob7db70a26d034658ae02f15aa1d5c008107843945
1 /* Return error detail for failing <dlfcn.h> functions.
2 Copyright (C) 1995-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
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>
31 #if !defined SHARED && IS_IN (libdl)
33 char *
34 dlerror (void)
36 return __dlerror ();
39 #else
41 char *
42 __dlerror (void)
44 # ifdef SHARED
45 if (!rtld_active ())
46 return _dlfcn_hook->dlerror ();
47 # endif
49 struct dl_action_result *result = __libc_dlerror_result;
51 /* No libdl function has been called. No error is possible. */
52 if (result == NULL)
53 return NULL;
55 /* For an early malloc failure, clear the error flag and return the
56 error message. This marks the error as delivered. */
57 if (result == dl_action_result_malloc_failed)
59 __libc_dlerror_result = NULL;
60 return (char *) "out of memory";
63 /* Placeholder object. This can be observed in a recursive call,
64 e.g. from an ELF constructor. */
65 if (result->errstring == NULL)
66 return NULL;
68 /* If we have already reported the error, we can free the result and
69 return NULL. See __libc_dlerror_result_free. */
70 if (result->returned)
72 __libc_dlerror_result = NULL;
73 dl_action_result_errstring_free (result);
74 free (result);
75 return NULL;
78 assert (result->errstring != NULL);
80 /* Create the combined error message. */
81 char *buf;
82 int n;
83 if (result->errcode == 0)
84 n = __asprintf (&buf, "%s%s%s",
85 result->objname,
86 result->objname[0] == '\0' ? "" : ": ",
87 _(result->errstring));
88 else
89 n = __asprintf (&buf, "%s%s%s: %s",
90 result->objname,
91 result->objname[0] == '\0' ? "" : ": ",
92 _(result->errstring),
93 strerror (result->errcode));
95 /* Mark the error as delivered. */
96 result->returned = true;
98 if (n >= 0)
100 /* Replace the error string with the newly allocated one. */
101 dl_action_result_errstring_free (result);
102 result->errstring = buf;
103 result->errstring_source = dl_action_result_errstring_local;
104 return buf;
106 else
107 /* We could not create the combined error message, so use the
108 existing string as a fallback. */
109 return result->errstring;
111 # ifdef SHARED
112 strong_alias (__dlerror, dlerror)
113 # endif
116 _dlerror_run (void (*operate) (void *), void *args)
118 struct dl_action_result *result = __libc_dlerror_result;
119 if (result != NULL)
121 if (result == dl_action_result_malloc_failed)
123 /* Clear the previous error. */
124 __libc_dlerror_result = NULL;
125 result = NULL;
127 else
129 /* There is an existing object. Free its error string, but
130 keep the object. */
131 dl_action_result_errstring_free (result);
132 /* Mark the object as not containing an error. This ensures
133 that call to dlerror from, for example, an ELF
134 constructor will not notice this result object. */
135 result->errstring = NULL;
139 const char *objname;
140 const char *errstring;
141 bool malloced;
142 int errcode = GLRO (dl_catch_error) (&objname, &errstring, &malloced,
143 operate, args);
145 /* ELF constructors or destructors may have indirectly altered the
146 value of __libc_dlerror_result, therefore reload it. */
147 result = __libc_dlerror_result;
149 if (errstring == NULL)
151 /* There is no error. We no longer need the result object if it
152 does not contain an error. However, a recursive call may
153 have added an error even if this call did not cause it. Keep
154 the other error. */
155 if (result != NULL && result->errstring == NULL)
157 __libc_dlerror_result = NULL;
158 free (result);
160 return 0;
162 else
164 /* A new error occurred. Check if a result object has to be
165 allocated. */
166 if (result == NULL || result == dl_action_result_malloc_failed)
168 /* Allocating storage for the error message after the fact
169 is not ideal. But this avoids an infinite recursion in
170 case malloc itself calls libdl functions (without
171 triggering errors). */
172 result = malloc (sizeof (*result));
173 if (result == NULL)
175 /* Assume that the dlfcn failure was due to a malloc
176 failure, too. */
177 if (malloced)
178 dl_error_free ((char *) errstring);
179 __libc_dlerror_result = dl_action_result_malloc_failed;
180 return 1;
182 __libc_dlerror_result = result;
184 else
185 /* Deallocate the existing error message from a recursive
186 call, but reuse the result object. */
187 dl_action_result_errstring_free (result);
189 result->errcode = errcode;
190 result->objname = objname;
191 result->errstring = (char *) errstring;
192 result->returned = false;
193 /* In case of an error, the malloced flag indicates whether the
194 error string is constant or not. */
195 if (malloced)
196 result->errstring_source = dl_action_result_errstring_rtld;
197 else
198 result->errstring_source = dl_action_result_errstring_constant;
200 return 1;
204 # ifdef SHARED
206 struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
207 libdl_hidden_data_def (_dlfcn_hook)
209 # else
211 static struct dlfcn_hook _dlfcn_hooks =
213 .dlopen = __dlopen,
214 .dlclose = __dlclose,
215 .dlsym = __dlsym,
216 .dlvsym = __dlvsym,
217 .dlerror = __dlerror,
218 .dladdr = __dladdr,
219 .dladdr1 = __dladdr1,
220 .dlinfo = __dlinfo,
221 .dlmopen = __dlmopen
224 void
225 __libc_register_dlfcn_hook (struct link_map *map)
227 struct dlfcn_hook **hook;
229 hook = (struct dlfcn_hook **) __libc_dlsym_private (map, "_dlfcn_hook");
230 if (hook != NULL)
231 *hook = &_dlfcn_hooks;
233 # endif
234 #endif