]>
Commit | Line | Data |
---|---|---|
c84142e8 | 1 | /* Return error detail for failing <dlfcn.h> functions. |
bfeabc79 | 2 | Copyright (C) 1995-2000,2002,2003,2004,2005 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 AJ |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, write to the Free | |
17 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
18 | 02111-1307 USA. */ | |
d66e34cd | 19 | |
d66e34cd | 20 | #include <dlfcn.h> |
04470dc0 | 21 | #include <libintl.h> |
74780cf6 | 22 | #include <stdbool.h> |
d66e34cd | 23 | #include <stdio.h> |
d66e34cd | 24 | #include <stdlib.h> |
a853022c | 25 | #include <string.h> |
8d9618b7 | 26 | #include <bits/libc-lock.h> |
154d10bd | 27 | #include <ldsodefs.h> |
8d9618b7 | 28 | |
5f21997b UD |
29 | #if !defined SHARED && defined IS_IN_libdl |
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 UD |
66 | # ifdef SHARED |
67 | if (__builtin_expect (_dlfcn_hook != NULL, 0)) | |
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. */ |
af3878df UD |
75 | result = (struct dl_action_result *) __libc_getspecific (key); |
76 | if (result == NULL) | |
8d9618b7 UD |
77 | result = &last_result; |
78 | ||
4f2793d4 UD |
79 | /* Test whether we already returned the string. */ |
80 | if (result->returned != 0) | |
81 | { | |
82 | /* We can now free the string. */ | |
83 | if (result->errstring != NULL) | |
84 | { | |
ca3c0135 UD |
85 | if (strcmp (result->errstring, "out of memory") != 0) |
86 | free ((char *) result->errstring); | |
4f2793d4 UD |
87 | result->errstring = NULL; |
88 | } | |
4f2793d4 | 89 | } |
fb0dd050 | 90 | else if (result->errstring != NULL) |
8d9618b7 | 91 | { |
04470dc0 | 92 | buf = (char *) result->errstring; |
4a8ff87c RM |
93 | int n; |
94 | if (result->errcode == 0) | |
95 | n = __asprintf (&buf, "%s%s%s", | |
96 | result->objname, | |
97 | result->objname[0] == '\0' ? "" : ": ", | |
98 | _(result->errstring)); | |
99 | else | |
100 | n = __asprintf (&buf, "%s%s%s: %s", | |
101 | result->objname, | |
102 | result->objname[0] == '\0' ? "" : ": ", | |
103 | _(result->errstring), | |
104 | strerror (result->errcode)); | |
105 | if (n != -1) | |
4f2793d4 UD |
106 | { |
107 | /* We don't need the error string anymore. */ | |
ca3c0135 UD |
108 | if (strcmp (result->errstring, "out of memory") != 0) |
109 | free ((char *) result->errstring); | |
4f2793d4 UD |
110 | result->errstring = buf; |
111 | } | |
8d9618b7 | 112 | |
4f2793d4 UD |
113 | /* Mark the error as returned. */ |
114 | result->returned = 1; | |
8d9618b7 | 115 | } |
421f82e5 | 116 | |
8d9618b7 | 117 | return buf; |
d66e34cd | 118 | } |
5f21997b UD |
119 | # ifdef SHARED |
120 | strong_alias (__dlerror, dlerror) | |
121 | # endif | |
d66e34cd RM |
122 | |
123 | int | |
d0fc4041 | 124 | internal_function |
993b3242 | 125 | _dlerror_run (void (*operate) (void *), void *args) |
d66e34cd | 126 | { |
8d9618b7 UD |
127 | struct dl_action_result *result; |
128 | ||
129 | /* If we have not yet initialized the buffer do it now. */ | |
130 | __libc_once (once, init); | |
131 | ||
132 | /* Get error string and number. */ | |
133 | if (static_buf != NULL) | |
134 | result = static_buf; | |
135 | else | |
136 | { | |
137 | /* We don't use the static buffer and so we have a key. Use it | |
138 | to get the thread-specific buffer. */ | |
139 | result = __libc_getspecific (key); | |
140 | if (result == NULL) | |
141 | { | |
142 | result = (struct dl_action_result *) calloc (1, sizeof (*result)); | |
143 | if (result == NULL) | |
144 | /* We are out of memory. Since this is no really critical | |
145 | situation we carry on by using the global variable. | |
146 | This might lead to conflicts between the threads but | |
147 | they soon all will have memory problems. */ | |
148 | result = &last_result; | |
149 | else | |
150 | /* Set the tsd. */ | |
151 | __libc_setspecific (key, result); | |
152 | } | |
153 | } | |
154 | ||
155 | if (result->errstring != NULL) | |
d743ba1e UD |
156 | { |
157 | /* Free the error string from the last failed command. This can | |
158 | happen if `dlerror' was not run after an error was found. */ | |
74780cf6 | 159 | if (result->malloced) |
ca3c0135 | 160 | free ((char *) result->errstring); |
d743ba1e UD |
161 | result->errstring = NULL; |
162 | } | |
8d9618b7 | 163 | |
154d10bd | 164 | result->errcode = GLRO(dl_catch_error) (&result->objname, &result->errstring, |
74780cf6 | 165 | &result->malloced, operate, args); |
8d9618b7 | 166 | |
4f2793d4 UD |
167 | /* If no error we mark that no error string is available. */ |
168 | result->returned = result->errstring == NULL; | |
169 | ||
8d9618b7 UD |
170 | return result->errstring != NULL; |
171 | } | |
172 | ||
dcf0671d | 173 | |
8d9618b7 UD |
174 | /* Initialize buffers for results. */ |
175 | static void | |
176 | init (void) | |
177 | { | |
178 | if (__libc_key_create (&key, free_key_mem)) | |
179 | /* Creating the key failed. This means something really went | |
180 | wrong. In any case use a static buffer which is better than | |
181 | nothing. */ | |
182 | static_buf = &last_result; | |
183 | } | |
184 | ||
bfeabc79 UD |
185 | |
186 | static void | |
187 | check_free (struct dl_action_result *rec) | |
188 | { | |
189 | if (rec->errstring != NULL | |
190 | && strcmp (rec->errstring, "out of memory") != 0) | |
191 | { | |
192 | /* We can free the string only if the allocation happened in the | |
193 | C library used by the dynamic linker. This means, it is | |
194 | always the C library in the base namespave. */ | |
195 | struct link_map *map = NULL; | |
196 | Dl_info info; | |
197 | if (_dl_addr (check_free, &info, &map, NULL) != 0 | |
198 | && map != NULL && map->l_ns == 0) | |
199 | free ((char *) rec->errstring); | |
200 | } | |
201 | } | |
202 | ||
203 | ||
0bf5c050 RM |
204 | static void |
205 | __attribute__ ((destructor)) | |
206 | fini (void) | |
207 | { | |
bfeabc79 | 208 | check_free (&last_result); |
0bf5c050 RM |
209 | } |
210 | ||
8d9618b7 UD |
211 | |
212 | /* Free the thread specific data, this is done if a thread terminates. */ | |
213 | static void | |
214 | free_key_mem (void *mem) | |
215 | { | |
bfeabc79 | 216 | check_free ((struct dl_action_result *) mem); |
ca3c0135 | 217 | |
8d9618b7 UD |
218 | free (mem); |
219 | __libc_setspecific (key, NULL); | |
d66e34cd | 220 | } |
5f21997b UD |
221 | |
222 | # ifdef SHARED | |
223 | ||
224 | struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon)); | |
225 | libdl_hidden_data_def (_dlfcn_hook) | |
226 | ||
227 | # else | |
228 | ||
229 | static struct dlfcn_hook _dlfcn_hooks = | |
230 | { | |
231 | .dlopen = __dlopen, | |
232 | .dlclose = __dlclose, | |
233 | .dlsym = __dlsym, | |
234 | .dlvsym = __dlvsym, | |
235 | .dlerror = __dlerror, | |
236 | .dladdr = __dladdr, | |
237 | .dladdr1 = __dladdr1, | |
238 | .dlinfo = __dlinfo, | |
239 | .dlmopen = __dlmopen | |
240 | }; | |
241 | ||
242 | void | |
243 | __libc_register_dlfcn_hook (struct link_map *map) | |
244 | { | |
245 | struct dlfcn_hook **hook; | |
246 | ||
247 | hook = (struct dlfcn_hook **) __libc_dlsym_private (map, "_dlfcn_hook"); | |
248 | if (hook != NULL) | |
249 | *hook = &_dlfcn_hooks; | |
250 | } | |
251 | # endif | |
252 | #endif |