libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
except.c
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license in the file COPYING
10 * or http://www.opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file COPYING.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2023 Saso Kiselkov. All rights reserved.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28
29#if APL || LIN
30#include <signal.h>
31#include <err.h>
32#else /* !APL && !LIN */
33#include <windows.h>
34#endif /* !APL && !LIN */
35
36#include "acfutils/assert.h"
37#include "acfutils/except.h"
38#include "acfutils/helpers.h"
39#include "acfutils/log.h"
40
41static bool_t inited = B_FALSE;
42
43#if APL || LIN
44
45static struct sigaction old_sigsegv = {};
46static struct sigaction old_sigabrt = {};
47static struct sigaction old_sigfpe = {};
48static struct sigaction old_sigint = {};
49static struct sigaction old_sigill = {};
50static struct sigaction old_sigterm = {};
51
52static const char *
53sigfpe2str(int si_code)
54{
55 switch (si_code) {
56 case FPE_INTDIV:
57 return ("integer divide by zero");
58 case FPE_INTOVF:
59 return ("integer overflow");
60 case FPE_FLTDIV:
61 return ("floating-point divide by zero");
62 case FPE_FLTOVF:
63 return ("floating-point overflow");
64 case FPE_FLTUND:
65 return ("floating-point underflow");
66 case FPE_FLTRES:
67 return ("floating-point inexact result");
68 case FPE_FLTINV:
69 return ("floating-point invalid operation");
70 case FPE_FLTSUB:
71 return ("subscript out of range");
72 default:
73 return ("general arithmetic exception");
74 }
75}
76
77static const char *
78sigill2str(int si_code)
79{
80 switch(si_code) {
81 case ILL_ILLOPC:
82 return ("illegal opcode");
83 case ILL_ILLOPN:
84 return ("illegal operand");
85 case ILL_ILLADR:
86 return ("illegal addressing mode");
87 case ILL_ILLTRP:
88 return ("illegal trap");
89 case ILL_PRVOPC:
90 return ("privileged opcode");
91 case ILL_PRVREG:
92 return ("privileged register");
93 case ILL_COPROC:
94 return ("coprocessor error");
95 case ILL_BADSTK:
96 return ("internal stack error");
97 default:
98 return ("unknown error");
99 }
100}
101
102static void
103handle_posix_sig(int sig, siginfo_t *siginfo, void *context)
104{
105#define SIGNAL_FORWARD(sigact) \
106 do { \
107 if ((sigact)->sa_sigaction != NULL && \
108 ((sigact)->sa_flags & SA_SIGINFO)) { \
109 (sigact)->sa_sigaction(sig, siginfo, context); \
110 } else if ((sigact)->sa_handler != NULL) { \
111 (sigact)->sa_handler(sig); \
112 } \
113 } while (0)
114 switch (sig) {
115 case SIGSEGV:
116 logMsg("Caught SIGSEGV: segmentation fault (%p)",
117 siginfo->si_addr);
118 break;
119 case SIGABRT:
120 logMsg("Caught SIGABORT: abort (%p)", siginfo->si_addr);
121 break;
122 case SIGFPE:
123 logMsg("Caught SIGFPE: floating point exception (%s)",
124 sigfpe2str(siginfo->si_code));
125 break;
126 case SIGILL:
127 logMsg("Caught SIGILL: illegal instruction (%s)",
128 sigill2str(siginfo->si_code));
129 break;
130 case SIGTERM:
131 logMsg("Caught SIGTERM: terminated");
132 break;
133 default:
134 logMsg("Caught signal %d", sig);
135 break;
136 }
137
138 log_backtrace(1);
139
140 switch (sig) {
141 case SIGSEGV:
142 SIGNAL_FORWARD(&old_sigsegv);
143 break;
144 case SIGABRT:
145 SIGNAL_FORWARD(&old_sigabrt);
146 break;
147 case SIGFPE:
148 SIGNAL_FORWARD(&old_sigfpe);
149 break;
150 case SIGILL:
151 SIGNAL_FORWARD(&old_sigill);
152 break;
153 case SIGTERM:
154 SIGNAL_FORWARD(&old_sigterm);
155 break;
156 }
157
158 exit(EXIT_FAILURE);
159}
160
161#if LIN
162static uint8_t *alternate_stack = NULL;
163#endif
164
165static void
166signal_handler_init(void)
167{
168 struct sigaction sig_action = { .sa_sigaction = handle_posix_sig };
169
170 sigemptyset(&sig_action.sa_mask);
171
172#if LIN
173 /*
174 * Since glibc 2.34, SIGSTKSZ is no longer constant, so we need to
175 * heap storage for the stack snapshot.
176 */
177 alternate_stack = malloc(SIGSTKSZ);
178 stack_t ss = {
179 .ss_sp = (void*)alternate_stack,
180 .ss_size = SIGSTKSZ,
181 .ss_flags = 0
182 };
183
184 VERIFY0(sigaltstack(&ss, NULL));
185 sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
186#else /* !LIN */
187 sig_action.sa_flags = SA_SIGINFO;
188#endif /* !LIN */
189
190 VERIFY0(sigaction(SIGSEGV, &sig_action, &old_sigsegv));
191 VERIFY0(sigaction(SIGABRT, &sig_action, &old_sigabrt));
192 VERIFY0(sigaction(SIGFPE, &sig_action, &old_sigfpe));
193 VERIFY0(sigaction(SIGINT, &sig_action, &old_sigint));
194 VERIFY0(sigaction(SIGILL, &sig_action, &old_sigill));
195 VERIFY0(sigaction(SIGTERM, &sig_action, &old_sigterm));
196}
197
198static void
199signal_handler_fini(void)
200{
201 VERIFY0(sigaction(SIGSEGV, &old_sigsegv, NULL));
202 VERIFY0(sigaction(SIGABRT, &old_sigabrt, NULL));
203 VERIFY0(sigaction(SIGFPE, &old_sigfpe, NULL));
204 VERIFY0(sigaction(SIGINT, &old_sigint, NULL));
205 VERIFY0(sigaction(SIGILL, &old_sigill, NULL));
206 VERIFY0(sigaction(SIGTERM, &old_sigterm, NULL));
207#if LIN
208 free(alternate_stack);
209 alternate_stack = NULL;
210#endif /* LIN */
211}
212
213#else /* APL || LIN */
214
215static LPTOP_LEVEL_EXCEPTION_FILTER prev_windows_except_handler = NULL;
216
217LONG WINAPI
218handle_windows_exception(EXCEPTION_POINTERS *ei)
219{
220 switch(ei->ExceptionRecord->ExceptionCode) {
221 case EXCEPTION_ASSERTION_FAILED:
222 /* No need to print anything, there's already a log message */
223 break;
224 case EXCEPTION_ACCESS_VIOLATION:
225 logMsg("Caught EXCEPTION_ACCESS_VIOLATION");
226 break;
227 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
228 logMsg("Caught EXCEPTION_ARRAY_BOUNDS_EXCEEDED");
229 break;
230 case EXCEPTION_BREAKPOINT:
231 logMsg("Caught EXCEPTION_BREAKPOINT");
232 break;
233 case EXCEPTION_DATATYPE_MISALIGNMENT:
234 logMsg("Caught EXCEPTION_DATATYPE_MISALIGNMENT");
235 break;
236 case EXCEPTION_FLT_DENORMAL_OPERAND:
237 logMsg("Caught EXCEPTION_FLT_DENORMAL_OPERAND");
238 break;
239 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
240 logMsg("Caught EXCEPTION_FLT_DIVIDE_BY_ZERO");
241 break;
242 case EXCEPTION_FLT_INEXACT_RESULT:
243 logMsg("Caught EXCEPTION_FLT_INEXACT_RESULT");
244 break;
245 case EXCEPTION_FLT_INVALID_OPERATION:
246 logMsg("Caught EXCEPTION_FLT_INVALID_OPERATION");
247 break;
248 case EXCEPTION_FLT_OVERFLOW:
249 logMsg("Caught EXCEPTION_FLT_OVERFLOW");
250 break;
251 case EXCEPTION_FLT_STACK_CHECK:
252 logMsg("Caught EXCEPTION_FLT_STACK_CHECK");
253 break;
254 case EXCEPTION_FLT_UNDERFLOW:
255 logMsg("Caught EXCEPTION_FLT_UNDERFLOW");
256 break;
257 case EXCEPTION_ILLEGAL_INSTRUCTION:
258 logMsg("Caught EXCEPTION_ILLEGAL_INSTRUCTION");
259 break;
260 case EXCEPTION_IN_PAGE_ERROR:
261 logMsg("Caught EXCEPTION_IN_PAGE_ERROR");
262 break;
263 case EXCEPTION_INT_DIVIDE_BY_ZERO:
264 logMsg("Caught EXCEPTION_INT_DIVIDE_BY_ZERO");
265 break;
266 case EXCEPTION_INT_OVERFLOW:
267 logMsg("Caught EXCEPTION_INT_OVERFLOW");
268 break;
269 case EXCEPTION_INVALID_DISPOSITION:
270 logMsg("Caught EXCEPTION_INVALID_DISPOSITION");
271 break;
272 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
273 logMsg("Caught EXCEPTION_NONCONTINUABLE_EXCEPTION");
274 break;
275 case EXCEPTION_PRIV_INSTRUCTION:
276 logMsg("Caught EXCEPTION_PRIV_INSTRUCTION");
277 break;
278 case EXCEPTION_SINGLE_STEP:
279 logMsg("Caught EXCEPTION_SINGLE_STEP");
280 break;
281 case EXCEPTION_STACK_OVERFLOW:
282 logMsg("Caught EXCEPTION_STACK_OVERFLOW");
283 break;
284 default:
285 logMsg("Caught unknown exception %lx",
286 ei->ExceptionRecord->ExceptionCode);
287 break;
288 }
289 log_backtrace_sw64(ei->ContextRecord);
290
291 if (prev_windows_except_handler != NULL)
292 return (prev_windows_except_handler(ei));
293
294 return (EXCEPTION_CONTINUE_SEARCH);
295}
296
297#endif /* APL || LIN */
298
303void
305{
306 ASSERT(!inited);
307 inited = B_TRUE;
308
309#if LIN || APL
310 signal_handler_init();
311#else /* !LIN && !APL */
312 prev_windows_except_handler =
313 SetUnhandledExceptionFilter(handle_windows_exception);
314#endif /* !LIN && !APL */
315}
316
321void
323{
324 if (!inited)
325 return;
326 inited = B_FALSE;
327
328#if LIN || APL
329 signal_handler_fini();
330#else /* !LIN && !APL */
331 SetUnhandledExceptionFilter(prev_windows_except_handler);
332#endif /* !LIN && !APL */
333}
#define VERIFY0(x)
Definition assert.h:154
#define ASSERT(x)
Definition assert.h:208
void except_fini(void)
Definition except.c:322
void except_init(void)
Definition except.c:304
#define logMsg(...)
Definition log.h:112
void log_backtrace(int skip_frames)
Definition log.c:516