]>
Commit | Line | Data |
---|---|---|
ee1ada1b | 1 | /* Exception handling in the dynamic linker. |
dff8da6b | 2 | Copyright (C) 1995-2024 Free Software Foundation, Inc. |
9e78f6f6 FW |
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 | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
9e78f6f6 | 18 | |
9e78f6f6 FW |
19 | #include <libintl.h> |
20 | #include <setjmp.h> | |
21 | #include <stdbool.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
24 | #include <unistd.h> | |
25 | #include <ldsodefs.h> | |
26 | #include <stdio.h> | |
ee1ada1b | 27 | #include <tls.h> |
9e78f6f6 FW |
28 | |
29 | /* This structure communicates state between _dl_catch_error and | |
30 | _dl_signal_error. */ | |
ee1ada1b | 31 | struct rtld_catch |
9e78f6f6 | 32 | { |
2449ae7b | 33 | struct dl_exception *exception; /* The exception data is stored there. */ |
9e78f6f6 FW |
34 | volatile int *errcode; /* Return value of _dl_signal_error. */ |
35 | jmp_buf env; /* longjmp here on error. */ | |
36 | }; | |
37 | ||
ee1ada1b FW |
38 | /* Multiple threads at once can use the `_dl_catch_error' function. |
39 | The calls can come from `_dl_map_object_deps', `_dlerror_run', or | |
40 | from any of the libc functionality which loads dynamic objects | |
41 | (NSS, iconv). Therefore we have to be prepared to save the state | |
42 | in thread-local memory. We use THREAD_GETMEM and THREAD_SETMEM | |
43 | instead of ELF TLS because ELF TLS is not available in the dynamic | |
44 | loader. Additionally, the exception handling mechanism must be | |
45 | usable before the TCB has been set up, which is why | |
46 | rtld_catch_notls is used if !__rtld_tls_init_tp_called. This is | |
47 | not needed for static builds, where initialization completes before | |
48 | static dlopen etc. can be called. */ | |
49 | ||
50 | #if IS_IN (rtld) | |
51 | static struct rtld_catch *rtld_catch_notls; | |
9e78f6f6 FW |
52 | #endif |
53 | ||
ee1ada1b FW |
54 | static struct rtld_catch * |
55 | get_catch (void) | |
56 | { | |
57 | #if IS_IN (rtld) | |
58 | if (!__rtld_tls_init_tp_called) | |
59 | return rtld_catch_notls; | |
60 | else | |
61 | #endif | |
62 | return THREAD_GETMEM (THREAD_SELF, rtld_catch); | |
63 | } | |
9e78f6f6 | 64 | |
ee1ada1b FW |
65 | static void |
66 | set_catch (struct rtld_catch *catch) | |
67 | { | |
68 | #if IS_IN (rtld) | |
69 | if (!__rtld_tls_init_tp_called) | |
70 | rtld_catch_notls = catch; | |
71 | else | |
72 | #endif | |
73 | THREAD_SETMEM (THREAD_SELF, rtld_catch, catch); | |
74 | } | |
9e78f6f6 | 75 | |
2449ae7b FW |
76 | /* Lossage while resolving the program's own symbols is always fatal. */ |
77 | static void | |
78 | __attribute__ ((noreturn)) | |
79 | fatal_error (int errcode, const char *objname, const char *occasion, | |
80 | const char *errstring) | |
81 | { | |
82 | char buffer[1024]; | |
83 | _dl_fatal_printf ("%s: %s: %s%s%s%s%s\n", | |
84 | RTLD_PROGNAME, | |
85 | occasion ?: N_("error while loading shared libraries"), | |
86 | objname, *objname ? ": " : "", | |
87 | errstring, errcode ? ": " : "", | |
88 | (errcode | |
89 | ? __strerror_r (errcode, buffer, sizeof buffer) | |
90 | : "")); | |
91 | } | |
92 | ||
93 | void | |
94 | _dl_signal_exception (int errcode, struct dl_exception *exception, | |
95 | const char *occasion) | |
96 | { | |
ee1ada1b | 97 | struct rtld_catch *lcatch = get_catch (); |
2449ae7b FW |
98 | if (lcatch != NULL) |
99 | { | |
100 | *lcatch->exception = *exception; | |
101 | *lcatch->errcode = errcode; | |
102 | ||
103 | /* We do not restore the signal mask because none was saved. */ | |
104 | __longjmp (lcatch->env[0].__jmpbuf, 1); | |
105 | } | |
106 | else | |
107 | fatal_error (errcode, exception->objname, occasion, exception->errstring); | |
108 | } | |
ee1ada1b | 109 | rtld_hidden_def (_dl_signal_exception) |
2449ae7b | 110 | |
9e78f6f6 | 111 | void |
630da022 | 112 | _dl_signal_error (int errcode, const char *objname, const char *occasion, |
9e78f6f6 FW |
113 | const char *errstring) |
114 | { | |
ee1ada1b | 115 | struct rtld_catch *lcatch = get_catch (); |
9e78f6f6 FW |
116 | |
117 | if (! errstring) | |
118 | errstring = N_("DYNAMIC LINKER BUG!!!"); | |
119 | ||
9e78f6f6 FW |
120 | if (lcatch != NULL) |
121 | { | |
2449ae7b | 122 | _dl_exception_create (lcatch->exception, objname, errstring); |
9e78f6f6 FW |
123 | *lcatch->errcode = errcode; |
124 | ||
125 | /* We do not restore the signal mask because none was saved. */ | |
126 | __longjmp (lcatch->env[0].__jmpbuf, 1); | |
127 | } | |
128 | else | |
630da022 | 129 | fatal_error (errcode, objname, occasion, errstring); |
9e78f6f6 | 130 | } |
ee1ada1b FW |
131 | rtld_hidden_def (_dl_signal_error) |
132 | ||
133 | #if IS_IN (rtld) | |
134 | /* This points to a function which is called when a continuable error is | |
135 | received. Unlike the handling of `catch' this function may return. | |
136 | The arguments will be the `errstring' and `objname'. | |
9e78f6f6 | 137 | |
ee1ada1b FW |
138 | Since this functionality is not used in normal programs (only in ld.so) |
139 | we do not care about multi-threaded programs here. We keep this as a | |
140 | global variable. */ | |
141 | static receiver_fct receiver; | |
9e78f6f6 | 142 | |
2449ae7b FW |
143 | void |
144 | _dl_signal_cexception (int errcode, struct dl_exception *exception, | |
145 | const char *occasion) | |
146 | { | |
147 | if (__builtin_expect (GLRO(dl_debug_mask) | |
6628c742 | 148 | & ~(DL_DEBUG_STATISTICS), 0)) |
2449ae7b FW |
149 | _dl_debug_printf ("%s: error: %s: %s (%s)\n", |
150 | exception->objname, occasion, | |
151 | exception->errstring, receiver ? "continued" : "fatal"); | |
152 | ||
153 | if (receiver) | |
154 | { | |
155 | /* We are inside _dl_receive_error. Call the user supplied | |
156 | handler and resume the work. The receiver will still be | |
157 | installed. */ | |
158 | (*receiver) (errcode, exception->objname, exception->errstring); | |
159 | } | |
160 | else | |
161 | _dl_signal_exception (errcode, exception, occasion); | |
162 | } | |
163 | ||
9e78f6f6 | 164 | void |
630da022 | 165 | _dl_signal_cerror (int errcode, const char *objname, const char *occasion, |
9e78f6f6 FW |
166 | const char *errstring) |
167 | { | |
168 | if (__builtin_expect (GLRO(dl_debug_mask) | |
6628c742 | 169 | & ~(DL_DEBUG_STATISTICS), 0)) |
630da022 | 170 | _dl_debug_printf ("%s: error: %s: %s (%s)\n", objname, occasion, |
9e78f6f6 FW |
171 | errstring, receiver ? "continued" : "fatal"); |
172 | ||
173 | if (receiver) | |
174 | { | |
175 | /* We are inside _dl_receive_error. Call the user supplied | |
176 | handler and resume the work. The receiver will still be | |
177 | installed. */ | |
178 | (*receiver) (errcode, objname, errstring); | |
179 | } | |
180 | else | |
630da022 | 181 | _dl_signal_error (errcode, objname, occasion, errstring); |
9e78f6f6 | 182 | } |
ee1ada1b FW |
183 | |
184 | void | |
185 | _dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args) | |
186 | { | |
187 | struct rtld_catch *old_catch = get_catch (); | |
188 | receiver_fct old_receiver = receiver; | |
189 | ||
190 | /* Set the new values. */ | |
191 | set_catch (NULL); | |
192 | receiver = fct; | |
193 | ||
194 | (*operate) (args); | |
195 | ||
196 | set_catch (old_catch); | |
197 | receiver = old_receiver; | |
198 | } | |
199 | #endif | |
9e78f6f6 | 200 | |
9e78f6f6 | 201 | int |
2449ae7b FW |
202 | _dl_catch_exception (struct dl_exception *exception, |
203 | void (*operate) (void *), void *args) | |
9e78f6f6 | 204 | { |
2a764c6e FW |
205 | /* If exception is NULL, temporarily disable exception handling. |
206 | Exceptions during operate (args) are fatal. */ | |
207 | if (exception == NULL) | |
208 | { | |
ee1ada1b FW |
209 | struct rtld_catch *old_catch = get_catch (); |
210 | set_catch (NULL); | |
2a764c6e FW |
211 | operate (args); |
212 | /* If we get here, the operation was successful. */ | |
ee1ada1b | 213 | set_catch (old_catch); |
2a764c6e FW |
214 | return 0; |
215 | } | |
216 | ||
9e78f6f6 FW |
217 | /* We need not handle `receiver' since setting a `catch' is handled |
218 | before it. */ | |
219 | ||
220 | /* Only this needs to be marked volatile, because it is the only local | |
221 | variable that gets changed between the setjmp invocation and the | |
222 | longjmp call. All others are just set here (before setjmp) and read | |
223 | in _dl_signal_error (before longjmp). */ | |
224 | volatile int errcode; | |
225 | ||
ee1ada1b | 226 | struct rtld_catch c; |
9e78f6f6 | 227 | /* Don't use an initializer since we don't need to clear C.env. */ |
2449ae7b | 228 | c.exception = exception; |
9e78f6f6 FW |
229 | c.errcode = &errcode; |
230 | ||
ee1ada1b FW |
231 | struct rtld_catch *old = get_catch (); |
232 | set_catch (&c); | |
9e78f6f6 FW |
233 | |
234 | /* Do not save the signal mask. */ | |
235 | if (__builtin_expect (__sigsetjmp (c.env, 0), 0) == 0) | |
236 | { | |
237 | (*operate) (args); | |
ee1ada1b | 238 | set_catch (old); |
2449ae7b | 239 | *exception = (struct dl_exception) { NULL }; |
9e78f6f6 FW |
240 | return 0; |
241 | } | |
242 | ||
2449ae7b FW |
243 | /* We get here only if we longjmp'd out of OPERATE. |
244 | _dl_signal_exception has already stored values into | |
245 | *EXCEPTION. */ | |
ee1ada1b | 246 | set_catch (old); |
9e78f6f6 FW |
247 | return errcode; |
248 | } | |
ee1ada1b | 249 | rtld_hidden_def (_dl_catch_exception) |
2449ae7b FW |
250 | |
251 | int | |
2449ae7b FW |
252 | _dl_catch_error (const char **objname, const char **errstring, |
253 | bool *mallocedp, void (*operate) (void *), void *args) | |
254 | { | |
255 | struct dl_exception exception; | |
256 | int errorcode = _dl_catch_exception (&exception, operate, args); | |
257 | *objname = exception.objname; | |
258 | *errstring = exception.errstring; | |
259 | *mallocedp = exception.message_buffer == exception.errstring; | |
260 | return errorcode; | |
261 | } |