]>
Commit | Line | Data |
---|---|---|
c84142e8 | 1 | /* Return error detail for failing <dlfcn.h> functions. |
2b778ceb | 2 | Copyright (C) 1995-2021 Free Software Foundation, Inc. |
afd4eb37 | 3 | This file is part of the GNU C Library. |
d66e34cd | 4 | |
afd4eb37 | 5 | The GNU C Library is free software; you can redistribute it and/or |
41bdb6e2 AJ |
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. | |
d66e34cd | 9 | |
afd4eb37 UD |
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 | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
d66e34cd | 14 | |
41bdb6e2 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 | 16 | License along with the GNU C Library; if not, see |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
d66e34cd | 18 | |
d66e34cd | 19 | #include <dlfcn.h> |
04470dc0 | 20 | #include <libintl.h> |
74780cf6 | 21 | #include <stdbool.h> |
d66e34cd | 22 | #include <stdio.h> |
d66e34cd | 23 | #include <stdlib.h> |
a853022c | 24 | #include <string.h> |
ec999b8e | 25 | #include <libc-lock.h> |
154d10bd | 26 | #include <ldsodefs.h> |
2827ab99 | 27 | #include <libc-symbols.h> |
8d9618b7 | 28 | |
9b42a0b3 | 29 | #if !defined SHARED && IS_IN (libdl) |
5f21997b UD |
30 | |
31 | char * | |
32 | dlerror (void) | |
33 | { | |
34 | return __dlerror (); | |
35 | } | |
36 | ||
37 | #else | |
38 | ||
8d9618b7 UD |
39 | /* Type for storing results of dynamic loading actions. */ |
40 | struct dl_action_result | |
41 | { | |
42 | int errcode; | |
4f2793d4 | 43 | int returned; |
74780cf6 | 44 | bool malloced; |
04470dc0 UD |
45 | const char *objname; |
46 | const char *errstring; | |
8d9618b7 UD |
47 | }; |
48 | static struct dl_action_result last_result; | |
49 | static struct dl_action_result *static_buf; | |
50 | ||
8d9618b7 UD |
51 | /* This is the key for the thread specific memory. */ |
52 | static __libc_key_t key; | |
d347a4ab | 53 | __libc_once_define (static, once); |
8d9618b7 UD |
54 | |
55 | /* Destructor for the thread-specific data. */ | |
56 | static void init (void); | |
57 | static void free_key_mem (void *mem); | |
d66e34cd | 58 | |
d66e34cd RM |
59 | |
60 | char * | |
5f21997b | 61 | __dlerror (void) |
d66e34cd | 62 | { |
fb0dd050 | 63 | char *buf = NULL; |
8d9618b7 | 64 | struct dl_action_result *result; |
d66e34cd | 65 | |
5f21997b | 66 | # ifdef SHARED |
8e1472d2 | 67 | if (!rtld_active ()) |
5f21997b UD |
68 | return _dlfcn_hook->dlerror (); |
69 | # endif | |
70 | ||
d347a4ab UD |
71 | /* If we have not yet initialized the buffer do it now. */ |
72 | __libc_once (once, init); | |
73 | ||
8d9618b7 | 74 | /* Get error string. */ |
11b451c8 MW |
75 | if (static_buf != NULL) |
76 | result = static_buf; | |
77 | else | |
78 | { | |
79 | /* init () has been run and we don't use the static buffer. | |
80 | So we have a valid key. */ | |
81 | result = (struct dl_action_result *) __libc_getspecific (key); | |
82 | if (result == NULL) | |
83 | result = &last_result; | |
84 | } | |
8d9618b7 | 85 | |
4f2793d4 UD |
86 | /* Test whether we already returned the string. */ |
87 | if (result->returned != 0) | |
88 | { | |
89 | /* We can now free the string. */ | |
90 | if (result->errstring != NULL) | |
91 | { | |
ca3c0135 UD |
92 | if (strcmp (result->errstring, "out of memory") != 0) |
93 | free ((char *) result->errstring); | |
4f2793d4 UD |
94 | result->errstring = NULL; |
95 | } | |
4f2793d4 | 96 | } |
fb0dd050 | 97 | else if (result->errstring != NULL) |
8d9618b7 | 98 | { |
04470dc0 | 99 | buf = (char *) result->errstring; |
4a8ff87c RM |
100 | int n; |
101 | if (result->errcode == 0) | |
102 | n = __asprintf (&buf, "%s%s%s", | |
103 | result->objname, | |
104 | result->objname[0] == '\0' ? "" : ": ", | |
105 | _(result->errstring)); | |
106 | else | |
107 | n = __asprintf (&buf, "%s%s%s: %s", | |
108 | result->objname, | |
109 | result->objname[0] == '\0' ? "" : ": ", | |
110 | _(result->errstring), | |
111 | strerror (result->errcode)); | |
112 | if (n != -1) | |
4f2793d4 UD |
113 | { |
114 | /* We don't need the error string anymore. */ | |
ca3c0135 UD |
115 | if (strcmp (result->errstring, "out of memory") != 0) |
116 | free ((char *) result->errstring); | |
4f2793d4 UD |
117 | result->errstring = buf; |
118 | } | |
8d9618b7 | 119 | |
4f2793d4 UD |
120 | /* Mark the error as returned. */ |
121 | result->returned = 1; | |
8d9618b7 | 122 | } |
421f82e5 | 123 | |
8d9618b7 | 124 | return buf; |
d66e34cd | 125 | } |
5f21997b UD |
126 | # ifdef SHARED |
127 | strong_alias (__dlerror, dlerror) | |
128 | # endif | |
d66e34cd RM |
129 | |
130 | int | |
993b3242 | 131 | _dlerror_run (void (*operate) (void *), void *args) |
d66e34cd | 132 | { |
8d9618b7 UD |
133 | struct dl_action_result *result; |
134 | ||
135 | /* If we have not yet initialized the buffer do it now. */ | |
136 | __libc_once (once, init); | |
137 | ||
138 | /* Get error string and number. */ | |
139 | if (static_buf != NULL) | |
140 | result = static_buf; | |
141 | else | |
142 | { | |
143 | /* We don't use the static buffer and so we have a key. Use it | |
144 | to get the thread-specific buffer. */ | |
145 | result = __libc_getspecific (key); | |
146 | if (result == NULL) | |
147 | { | |
148 | result = (struct dl_action_result *) calloc (1, sizeof (*result)); | |
149 | if (result == NULL) | |
150 | /* We are out of memory. Since this is no really critical | |
151 | situation we carry on by using the global variable. | |
152 | This might lead to conflicts between the threads but | |
153 | they soon all will have memory problems. */ | |
154 | result = &last_result; | |
155 | else | |
156 | /* Set the tsd. */ | |
157 | __libc_setspecific (key, result); | |
158 | } | |
159 | } | |
160 | ||
161 | if (result->errstring != NULL) | |
d743ba1e UD |
162 | { |
163 | /* Free the error string from the last failed command. This can | |
164 | happen if `dlerror' was not run after an error was found. */ | |
74780cf6 | 165 | if (result->malloced) |
ca3c0135 | 166 | free ((char *) result->errstring); |
d743ba1e UD |
167 | result->errstring = NULL; |
168 | } | |
8d9618b7 | 169 | |
9e78f6f6 FW |
170 | result->errcode = _dl_catch_error (&result->objname, &result->errstring, |
171 | &result->malloced, operate, args); | |
8d9618b7 | 172 | |
4f2793d4 UD |
173 | /* If no error we mark that no error string is available. */ |
174 | result->returned = result->errstring == NULL; | |
175 | ||
8d9618b7 UD |
176 | return result->errstring != NULL; |
177 | } | |
178 | ||
dcf0671d | 179 | |
8d9618b7 UD |
180 | /* Initialize buffers for results. */ |
181 | static void | |
182 | init (void) | |
183 | { | |
184 | if (__libc_key_create (&key, free_key_mem)) | |
185 | /* Creating the key failed. This means something really went | |
186 | wrong. In any case use a static buffer which is better than | |
187 | nothing. */ | |
188 | static_buf = &last_result; | |
189 | } | |
190 | ||
bfeabc79 UD |
191 | |
192 | static void | |
193 | check_free (struct dl_action_result *rec) | |
194 | { | |
195 | if (rec->errstring != NULL | |
196 | && strcmp (rec->errstring, "out of memory") != 0) | |
197 | { | |
198 | /* We can free the string only if the allocation happened in the | |
199 | C library used by the dynamic linker. This means, it is | |
273edc5e RM |
200 | always the C library in the base namespace. When we're statically |
201 | linked, the dynamic linker is part of the program and so always | |
202 | uses the same C library we use here. */ | |
203 | #ifdef SHARED | |
bfeabc79 UD |
204 | struct link_map *map = NULL; |
205 | Dl_info info; | |
273edc5e RM |
206 | if (_dl_addr (check_free, &info, &map, NULL) != 0 && map->l_ns == 0) |
207 | #endif | |
bcdaad21 DD |
208 | { |
209 | free ((char *) rec->errstring); | |
210 | rec->errstring = NULL; | |
211 | } | |
bfeabc79 UD |
212 | } |
213 | } | |
214 | ||
215 | ||
0bf5c050 RM |
216 | static void |
217 | __attribute__ ((destructor)) | |
218 | fini (void) | |
219 | { | |
bfeabc79 | 220 | check_free (&last_result); |
0bf5c050 RM |
221 | } |
222 | ||
8d9618b7 UD |
223 | |
224 | /* Free the thread specific data, this is done if a thread terminates. */ | |
225 | static void | |
226 | free_key_mem (void *mem) | |
227 | { | |
bfeabc79 | 228 | check_free ((struct dl_action_result *) mem); |
ca3c0135 | 229 | |
8d9618b7 UD |
230 | free (mem); |
231 | __libc_setspecific (key, NULL); | |
d66e34cd | 232 | } |
5f21997b UD |
233 | |
234 | # ifdef SHARED | |
235 | ||
2827ab99 CD |
236 | /* Free the dlerror-related resources. */ |
237 | void | |
238 | __dlerror_main_freeres (void) | |
239 | { | |
2827ab99 CD |
240 | /* Free the global memory if used. */ |
241 | check_free (&last_result); | |
11b451c8 MW |
242 | |
243 | if (__libc_once_get (once) && static_buf == NULL) | |
244 | { | |
245 | /* init () has been run and we don't use the static buffer. | |
246 | So we have a valid key. */ | |
247 | void *mem; | |
248 | /* Free the TSD memory if used. */ | |
249 | mem = __libc_getspecific (key); | |
250 | if (mem != NULL) | |
251 | free_key_mem (mem); | |
252 | } | |
2827ab99 CD |
253 | } |
254 | ||
5f21997b UD |
255 | struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon)); |
256 | libdl_hidden_data_def (_dlfcn_hook) | |
257 | ||
258 | # else | |
259 | ||
260 | static struct dlfcn_hook _dlfcn_hooks = | |
261 | { | |
262 | .dlopen = __dlopen, | |
263 | .dlclose = __dlclose, | |
264 | .dlsym = __dlsym, | |
265 | .dlvsym = __dlvsym, | |
266 | .dlerror = __dlerror, | |
267 | .dladdr = __dladdr, | |
268 | .dladdr1 = __dladdr1, | |
269 | .dlinfo = __dlinfo, | |
270 | .dlmopen = __dlmopen | |
271 | }; | |
272 | ||
273 | void | |
274 | __libc_register_dlfcn_hook (struct link_map *map) | |
275 | { | |
276 | struct dlfcn_hook **hook; | |
277 | ||
278 | hook = (struct dlfcn_hook **) __libc_dlsym_private (map, "_dlfcn_hook"); | |
279 | if (hook != NULL) | |
280 | *hook = &_dlfcn_hooks; | |
281 | } | |
282 | # endif | |
283 | #endif |