]> git.ipfire.org Git - thirdparty/glibc.git/blame - elf/dl-catch.c
INSTALL, install.texi: minor updates, regenerate
[thirdparty/glibc.git] / elf / dl-catch.c
CommitLineData
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 31struct 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)
51static struct rtld_catch *rtld_catch_notls;
9e78f6f6
FW
52#endif
53
ee1ada1b
FW
54static struct rtld_catch *
55get_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
65static void
66set_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. */
77static void
78__attribute__ ((noreturn))
79fatal_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
93void
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 109rtld_hidden_def (_dl_signal_exception)
2449ae7b 110
9e78f6f6 111void
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
131rtld_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. */
141static receiver_fct receiver;
9e78f6f6 142
2449ae7b
FW
143void
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 164void
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
184void
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 201int
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 249rtld_hidden_def (_dl_catch_exception)
2449ae7b
FW
250
251int
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}