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