Loading...
Searching...
No Matches
fpe_handler.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#include "eon/fpe_handler.h"
13
14#include <cfenv>
15#include <csignal>
16#include <cstdint>
17#include <cstdio>
18
19#ifndef _WIN32
20#include <unistd.h>
21#endif
22
23#ifdef _WIN32
24#define WIN32_LEAN_AND_MEAN
25#include <float.h>
26#include <windows.h>
27#endif
28
29#if defined(__linux__)
30#include <ucontext.h>
31#endif
32#if defined(__APPLE__)
33#include <sys/ucontext.h>
34#endif
35
36#if defined(__APPLE__) && defined(__x86_64__)
37#include <xmmintrin.h>
38#endif
39
40namespace eonc {
41
42#ifdef _WIN32
43// Report each exception class once. Clearing the status alone is not enough
44// for a true continue: the faulting op re-executes and re-traps forever.
45// After the first report, unmask-trapping is demoted for that class so the
46// instruction completes with the IEEE default (Inf/NaN) and the process
47// proceeds.
48static LONG WINAPI windowsFPEHandler(EXCEPTION_POINTERS *info) {
49 DWORD code = info->ExceptionRecord->ExceptionCode;
50 static bool reported_div = false;
51 static bool reported_inv = false;
52 static bool reported_ovf = false;
53 static bool reported_other = false;
54 switch (code) {
55 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
56 if (!reported_div) {
57 reported_div = true;
58 fprintf(stderr, "FPE (continuing, masking further): division by zero\n");
59 }
60 break;
61 case EXCEPTION_FLT_INVALID_OPERATION:
62 if (!reported_inv) {
63 reported_inv = true;
64 fprintf(stderr, "FPE (continuing, masking further): invalid operation\n");
65 }
66 break;
67 case EXCEPTION_FLT_OVERFLOW:
68 if (!reported_ovf) {
69 reported_ovf = true;
70 fprintf(stderr, "FPE (continuing, masking further): overflow\n");
71 }
72 break;
73 case EXCEPTION_FLT_UNDERFLOW:
74 case EXCEPTION_FLT_INEXACT_RESULT:
75 case EXCEPTION_FLT_DENORMAL_OPERAND:
76 case EXCEPTION_FLT_STACK_CHECK:
77 if (!reported_other) {
78 reported_other = true;
79 fprintf(stderr, "FPE (continuing, masking further): other float fault\n");
80 }
81 break;
82 default:
83 return EXCEPTION_CONTINUE_SEARCH;
84 }
85 // Re-mask every class we care about so CONTINUE_EXECUTION does not re-trap.
86 // The continue restores MxCsr from ContextRecord. _controlfp_s changes the
87 // live register only, so the unmasked fault word would be reloaded and the
88 // same instruction would trap again.
89 _clearfp();
90 unsigned int control = 0;
91 _controlfp_s(&control, _MCW_EM, _MCW_EM);
92#if defined(_M_X64) || defined(__x86_64__)
93 if (info->ContextRecord != nullptr) {
94 const DWORD mxcsr = maskWindowsMxcsrForContinue(info->ContextRecord->MxCsr);
95 info->ContextRecord->MxCsr = mxcsr;
96 info->ContextRecord->FltSave.MxCsr = mxcsr;
97 info->ContextRecord->FltSave.StatusWord &= static_cast<WORD>(~0x3Fu);
98 info->ContextRecord->FltSave.ControlWord |= static_cast<WORD>(0x3Fu);
99 }
100#endif
101 return EXCEPTION_CONTINUE_EXECUTION;
102}
103#else
104// MXCSR exception-mask bits (Intel SDM): bit7=IM, bit8=DM, bit9=ZM,
105// bit10=OM, bit11=UM, bit12=PM. Sticky status flags are bits 0-5.
106static constexpr unsigned MXCSR_MASK_IM = 1u << 7;
107static constexpr unsigned MXCSR_MASK_ZM = 1u << 9;
108static constexpr unsigned MXCSR_MASK_OM = 1u << 10;
109
110// One DIV/IDIV. length 0 means the bytes at IP are not that instruction.
111// width is the operand size in bytes (1, 2, 4, or 8).
114 int width;
115};
116
117// #DE is a fault: the saved IP points at the divide. Only DIV and IDIV
118// raise it. Prefixes and a ModRM (plus SIB/displacement) are enough to
119// measure that one instruction; anything else is left untouched.
120static DecodedDiv decode_div_insn(const unsigned char *code, bool long_mode) {
121 DecodedDiv out{0, 0};
122 const unsigned char *p = code;
123 const unsigned char *limit = code + 15;
124 bool operand16 = false;
125 bool rex_w = false;
126 bool addr16 = false;
127 while (p < limit) {
128 unsigned char c = *p;
129 if (c == 0x66) {
130 operand16 = true;
131 ++p;
132 continue;
133 }
134 if (c == 0x67) {
135 if (!long_mode) {
136 addr16 = true;
137 }
138 ++p;
139 continue;
140 }
141 if (c == 0xF0 || c == 0xF2 || c == 0xF3 || c == 0x26 || c == 0x2E ||
142 c == 0x36 || c == 0x3E || c == 0x64 || c == 0x65) {
143 ++p;
144 continue;
145 }
146 if (long_mode && c >= 0x40 && c <= 0x4F) {
147 rex_w = (c & 0x08) != 0;
148 ++p;
149 continue;
150 }
151 break;
152 }
153 if (addr16 || p >= limit) {
154 return out;
155 }
156 unsigned char opcode = *p++;
157 if (opcode != 0xF6 && opcode != 0xF7) {
158 return out;
159 }
160 if (p >= limit) {
161 return out;
162 }
163 unsigned char modrm = *p++;
164 unsigned mod = modrm >> 6;
165 unsigned reg = (modrm >> 3) & 7u;
166 unsigned rm = modrm & 7u;
167 if (reg != 6u && reg != 7u) {
168 return out;
169 }
170 if (mod != 3u) {
171 bool have_sib = rm == 4u;
172 unsigned sib_base = 0;
173 if (have_sib) {
174 if (p >= limit) {
175 return out;
176 }
177 sib_base = static_cast<unsigned>(*p++ & 7u);
178 }
179 if ((mod == 0u && rm == 5u) || (have_sib && mod == 0u && sib_base == 5u) ||
180 mod == 2u) {
181 if (p + 4 > limit) {
182 return out;
183 }
184 p += 4;
185 } else if (mod == 1u) {
186 if (p + 1 > limit) {
187 return out;
188 }
189 ++p;
190 }
191 }
192 int length = static_cast<int>(p - code);
193 if (length < 2 || length > 15) {
194 return out;
195 }
196 out.length = length;
197 if (opcode == 0xF6) {
198 out.width = 1;
199 } else if (rex_w) {
200 out.width = 8;
201 } else if (operand16) {
202 out.width = 2;
203 } else {
204 out.width = 4;
205 }
206 return out;
207}
208
210 uintptr_t *ip;
211 uintptr_t *ax;
212 uintptr_t *dx;
213};
214
215static X86DivRegs x86_div_regs(void *scp) {
216 X86DivRegs regs{nullptr, nullptr, nullptr};
217 if (scp == nullptr) {
218 return regs;
219 }
220 auto *ctx = static_cast<ucontext_t *>(scp);
221#if defined(__linux__) && defined(__x86_64__)
222 regs.ip = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext.gregs[REG_RIP]);
223 regs.ax = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext.gregs[REG_RAX]);
224 regs.dx = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext.gregs[REG_RDX]);
225#elif defined(__linux__) && defined(__i386__)
226 regs.ip = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext.gregs[REG_EIP]);
227 regs.ax = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext.gregs[REG_EAX]);
228 regs.dx = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext.gregs[REG_EDX]);
229#elif defined(__APPLE__) && defined(__x86_64__)
230 if (ctx->uc_mcontext != nullptr) {
231 regs.ip = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext->__ss.__rip);
232 regs.ax = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext->__ss.__rax);
233 regs.dx = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext->__ss.__rdx);
234 }
235#elif defined(__APPLE__) && defined(__i386__)
236 if (ctx->uc_mcontext != nullptr) {
237 regs.ip = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext->__ss.__eip);
238 regs.ax = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext->__ss.__eax);
239 regs.dx = reinterpret_cast<uintptr_t *>(&ctx->uc_mcontext->__ss.__edx);
240 }
241#else
242 (void)ctx;
243#endif
244 return regs;
245}
246
247// Advance past a faulting DIV/IDIV and clear its quotient. Returns false
248// when IP does not point at a divide (INTO's trap IP is already next).
249static bool advance_integer_div(void *scp) {
250 X86DivRegs regs = x86_div_regs(scp);
251 if (regs.ip == nullptr || regs.ax == nullptr || regs.dx == nullptr) {
252 return false;
253 }
254 const auto *code = reinterpret_cast<const unsigned char *>(*regs.ip);
255 DecodedDiv div = decode_div_insn(code, sizeof(void *) == 8);
256 if (div.length == 0) {
257 return false;
258 }
259 if (div.width <= 2) {
260 *regs.ax &= ~uintptr_t{0xFFFFu};
261 if (div.width == 2) {
262 *regs.dx &= ~uintptr_t{0xFFFFu};
263 }
264 } else {
265 *regs.ax = 0;
266 *regs.dx = 0;
267 }
268 *regs.ip += static_cast<uintptr_t>(div.length);
269 return true;
270}
271
272static void write_fault_rip(void *scp) {
273#if defined(__linux__) && defined(__x86_64__)
274 auto *ctx_log = static_cast<ucontext_t *>(scp);
275 unsigned long rip =
276 static_cast<unsigned long>(ctx_log->uc_mcontext.gregs[REG_RIP]);
277 char hex[] = "FPE rip=0x0000000000000000\n";
278 for (int i = 0; i < 16; ++i) {
279 unsigned nibble = static_cast<unsigned>((rip >> (4 * (15 - i))) & 0xFu);
280 hex[10 + i] =
281 static_cast<char>(nibble < 10 ? '0' + nibble : 'a' + (nibble - 10));
282 }
283 write(STDERR_FILENO, hex, sizeof(hex) - 1);
284#else
285 (void)scp;
286#endif
287}
288
289static void fpe_signal_handler(int sig, siginfo_t *sip, void *scp) {
290 // Async-signal-safe only: write(2) and sig_atomic_t. No iostream, malloc,
291 // backtrace, or fenv helpers (fedisableexcept / feclearexcept are not
292 // async-signal-safe). All continue-state is written into the saved ucontext
293 // so it is restored on sigreturn.
294 //
295 // x86 cannot "continue" past a trapped FP op by clearing sticky flags:
296 // flags are bits 0-5 of MXCSR/swd, but the exception MASK bits live at
297 // MXCSR 7-12. Clearing 0x3F leaves trapping armed, so the faulting
298 // instruction re-executes on the same operands and re-raises forever
299 // (report, sigreturn, refault) -- multi-GB identical stderr lines and a
300 // client stuck at ~100% CPU. Mask the class in the restored MXCSR so
301 // re-execution produces the IEEE default (Inf/NaN) and proceeds.
302 static volatile sig_atomic_t reported_div = 0;
303 static volatile sig_atomic_t reported_inv = 0;
304 static volatile sig_atomic_t reported_ovf = 0;
305 static volatile sig_atomic_t reported_unk = 0;
306 static volatile sig_atomic_t reported_int = 0;
307
308 static constexpr char prefix[] = "FPE (continuing, masking further): ";
309 static constexpr char msg_div[] = "division by zero\n";
310 static constexpr char msg_inv[] = "invalid operation\n";
311 static constexpr char msg_ovf[] = "overflow\n";
312 static constexpr char msg_unk[] = "unknown\n";
313
314 // Default: mask all three classes we enable at startup, so an unknown
315 // si_code cannot leave trapping armed and re-storm.
316 unsigned mxcsr_mask_bits = MXCSR_MASK_IM | MXCSR_MASK_ZM | MXCSR_MASK_OM;
317 volatile sig_atomic_t *reported = &reported_unk;
318 const char *msg = msg_unk;
319 size_t msg_len = sizeof(msg_unk) - 1;
320
321 // Integer #DE has no mask bit. Returning here re-executes the divide.
322 if (sip->si_code == FPE_INTDIV || sip->si_code == FPE_INTOVF) {
323 if (reported_int == 0) {
324 reported_int = 1;
325 static constexpr char iprefix[] = "FPE (continuing, skipping divide): ";
326 static constexpr char msg_idiv[] = "integer divide\n";
327 static constexpr char msg_iovf[] = "integer overflow\n";
328 const char *imsg = sip->si_code == FPE_INTDIV ? msg_idiv : msg_iovf;
329 size_t imsg_len = sizeof(msg_iovf) - 1;
330 if (sip->si_code == FPE_INTDIV) {
331 imsg_len = sizeof(msg_idiv) - 1;
332 }
333 write(STDERR_FILENO, iprefix, sizeof(iprefix) - 1);
334 write(STDERR_FILENO, imsg, imsg_len);
335 write_fault_rip(scp);
336 }
337 if (!advance_integer_div(scp) && sip->si_code == FPE_INTDIV) {
338 static constexpr char stuck[] =
339 "FPE integer divide: could not skip faulting instruction\n";
340 write(STDERR_FILENO, stuck, sizeof(stuck) - 1);
341 _exit(128 + SIGFPE);
342 }
343 return;
344 }
345
346 switch (sip->si_code) {
347 case FPE_FLTDIV:
348 reported = &reported_div;
349 msg = msg_div;
350 msg_len = sizeof(msg_div) - 1;
351 mxcsr_mask_bits = MXCSR_MASK_ZM;
352 break;
353 case FPE_FLTINV:
354 reported = &reported_inv;
355 msg = msg_inv;
356 msg_len = sizeof(msg_inv) - 1;
357 mxcsr_mask_bits = MXCSR_MASK_IM;
358 break;
359 case FPE_FLTOVF:
360 reported = &reported_ovf;
361 msg = msg_ovf;
362 msg_len = sizeof(msg_ovf) - 1;
363 mxcsr_mask_bits = MXCSR_MASK_OM;
364 break;
365 default:
366 break;
367 }
368
369 if (*reported == 0) {
370 *reported = 1;
371 write(STDERR_FILENO, prefix, sizeof(prefix) - 1);
372 write(STDERR_FILENO, msg, msg_len);
373 // First-fault RIP for post-mortem addr2line / offline diagnosis.
374 write_fault_rip(scp);
375 }
376
377#if defined(__linux__) && (defined(__x86_64__) || defined(__i386__))
378 ucontext_t *ctx = static_cast<ucontext_t *>(scp);
379 if (ctx->uc_mcontext.fpregs) {
380 // Clear sticky exception FLAGS (bits 0-5) and arm the MASK bit(s) for
381 // the fault class (bits 7-12). Mask sticks after sigreturn because the
382 // restored MXCSR becomes the live CPU state.
383 ctx->uc_mcontext.fpregs->swd &= ~0x3Fu;
384 ctx->uc_mcontext.fpregs->mxcsr &= ~0x3Fu;
385 ctx->uc_mcontext.fpregs->mxcsr |= mxcsr_mask_bits;
386 // x87 control word: mask bits are 0-5 of cwd (IM, DM, ZM, OM, UM, PM).
387 // Set the matching masks so a legacy x87 fault cannot re-storm either.
388 if (mxcsr_mask_bits & MXCSR_MASK_ZM) {
389 ctx->uc_mcontext.fpregs->cwd |= (1u << 2); // x87 ZM
390 }
391 if (mxcsr_mask_bits & MXCSR_MASK_IM) {
392 ctx->uc_mcontext.fpregs->cwd |= (1u << 0); // x87 IM
393 }
394 if (mxcsr_mask_bits & MXCSR_MASK_OM) {
395 ctx->uc_mcontext.fpregs->cwd |= (1u << 3); // x87 OM
396 }
397 }
398#elif defined(__linux__) && defined(__aarch64__)
399 // ARM polarity is the opposite of MXCSR: FPCR trap-enable bits SET mean
400 // trap. Clearing sticky FPSR flags alone re-executes with trapping still
401 // armed on aarch64. Clear the matching FPCR enables.
402 constexpr uint32_t kFpsimdMagic = 0x46508001u;
403 constexpr unsigned kFpcrIoe = 1u << 8;
404 constexpr unsigned kFpcrDze = 1u << 9;
405 constexpr unsigned kFpcrOfe = 1u << 10;
406 unsigned fpcr_clear = kFpcrIoe | kFpcrDze | kFpcrOfe;
407 if (mxcsr_mask_bits == MXCSR_MASK_ZM) {
408 fpcr_clear = kFpcrDze;
409 } else if (mxcsr_mask_bits == MXCSR_MASK_IM) {
410 fpcr_clear = kFpcrIoe;
411 } else if (mxcsr_mask_bits == MXCSR_MASK_OM) {
412 fpcr_clear = kFpcrOfe;
413 }
414 auto *ctx = static_cast<ucontext_t *>(scp);
415 unsigned char *p =
416 reinterpret_cast<unsigned char *>(ctx->uc_mcontext.__reserved);
417 unsigned char *end = p + sizeof(ctx->uc_mcontext.__reserved);
418 struct A64Head {
419 uint32_t magic;
420 uint32_t size;
421 };
422 struct Fpsimd {
423 A64Head head;
424 uint32_t fpsr;
425 uint32_t fpcr;
426 };
427 while (p + sizeof(A64Head) <= end) {
428 auto *h = reinterpret_cast<A64Head *>(p);
429 if (h->magic == 0 || h->size < sizeof(A64Head)) {
430 break;
431 }
432 if (h->magic == kFpsimdMagic && h->size >= sizeof(Fpsimd) &&
433 p + h->size <= end) {
434 auto *f = reinterpret_cast<Fpsimd *>(p);
435 f->fpsr &= ~0x1Fu;
436 f->fpcr &= ~fpcr_clear;
437 break;
438 }
439 if (h->size == 0) {
440 break;
441 }
442 p += h->size;
443 }
444#elif defined(__APPLE__) && defined(__x86_64__)
445 // Darwin restores SSE state from uc_mcontext->__fs. Mask bits have the
446 // same polarity as Linux MXCSR: set means the class does not trap. Without
447 // that update the faulting instruction re-executes and re-raises forever.
448 auto *ctx = static_cast<ucontext_t *>(scp);
449 if (ctx->uc_mcontext) {
450 auto &fs = ctx->uc_mcontext->__fs;
451 fs.__fpu_mxcsr &= ~0x3Fu;
452 fs.__fpu_mxcsr |= mxcsr_mask_bits;
453 if (mxcsr_mask_bits & MXCSR_MASK_ZM) {
454 fs.__fpu_fcw.__zdiv = 1;
455 }
456 if (mxcsr_mask_bits & MXCSR_MASK_IM) {
457 fs.__fpu_fcw.__invalid = 1;
458 }
459 if (mxcsr_mask_bits & MXCSR_MASK_OM) {
460 fs.__fpu_fcw.__ovrfl = 1;
461 }
462 fs.__fpu_fsw.__invalid = 0;
463 fs.__fpu_fsw.__denorm = 0;
464 fs.__fpu_fsw.__zdiv = 0;
465 fs.__fpu_fsw.__ovrfl = 0;
466 fs.__fpu_fsw.__undfl = 0;
467 fs.__fpu_fsw.__precis = 0;
468 }
469#elif defined(__APPLE__) && defined(__aarch64__)
470 constexpr unsigned kFpcrIoe = 1u << 8;
471 constexpr unsigned kFpcrDze = 1u << 9;
472 constexpr unsigned kFpcrOfe = 1u << 10;
473 unsigned fpcr_clear = kFpcrIoe | kFpcrDze | kFpcrOfe;
474 if (mxcsr_mask_bits == MXCSR_MASK_ZM) {
475 fpcr_clear = kFpcrDze;
476 } else if (mxcsr_mask_bits == MXCSR_MASK_IM) {
477 fpcr_clear = kFpcrIoe;
478 } else if (mxcsr_mask_bits == MXCSR_MASK_OM) {
479 fpcr_clear = kFpcrOfe;
480 }
481 auto *ctx = static_cast<ucontext_t *>(scp);
482 if (ctx->uc_mcontext) {
483 ctx->uc_mcontext->__ns.__fpsr &= ~0x1Fu;
484 ctx->uc_mcontext->__ns.__fpcr &= ~fpcr_clear;
485 }
486#endif
487 (void)sig;
488}
489#endif
490
491void enableFPE() {
492#ifdef _WIN32
493 // Register Windows SEH handler for FPE reporting
494 SetUnhandledExceptionFilter(windowsFPEHandler);
495 // Enable floating-point exceptions on Windows
496 _controlfp_s(nullptr, 0, _MCW_EM);
497 _controlfp_s(nullptr, ~(_EM_ZERODIVIDE | _EM_INVALID | _EM_OVERFLOW),
498 _MCW_EM);
499#elif defined(__unix__)
500 // Enable floating-point exceptions on Unix
501 feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
502#elif defined(__APPLE__) && defined(__aarch64__)
503 // ARM: trap-enable bits live in FPCR (IOE/DZE/OFE), not FPSR flags.
504 fenv_t env;
505 fegetenv(&env);
506 env.__fpcr |= ((1u << 8) | (1u << 9) | (1u << 10));
507 fesetenv(&env);
508#elif defined(__APPLE__) && defined(__x86_64__)
509 // Enable floating-point exceptions on Intel macOS
510 _MM_SET_EXCEPTION_MASK(
511 _MM_MASK_MASK &
512 ~(_MM_MASK_INVALID | _MM_MASK_DIV_ZERO | _MM_MASK_OVERFLOW));
513#else
514 fprintf(stderr, "FPE trapping not supported on this platform.\n");
515#endif
516
517#ifndef _WIN32
518 // Register POSIX signal handler
519 struct sigaction act;
520 act.sa_sigaction = fpe_signal_handler;
521 sigemptyset(&act.sa_mask);
522 act.sa_flags = SA_SIGINFO;
523 sigaction(SIGFPE, &act, nullptr);
524#endif
525}
526
528#ifdef _WIN32
529 // Mask all floating-point exceptions (restore default behavior)
530 unsigned int control;
531 _controlfp_s(&control, _MCW_EM, _MCW_EM);
532#elif defined(__unix__)
533 fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
534#elif defined(__APPLE__)
535 fenv_t env;
536 fegetenv(&env);
537#if defined(__aarch64__)
538 env.__fpcr &= ~((1u << 8) | (1u << 9) | (1u << 10));
539#elif defined(__x86_64__)
540 // enableFPE clears MXCSR IM/ZM/OM. Restoring that environment unchanged
541 // leaves the traps armed.
542 env.__mxcsr |= (MXCSR_MASK_IM | MXCSR_MASK_ZM | MXCSR_MASK_OM);
543 env.__mxcsr &= ~0x3Fu;
544 env.__control = static_cast<unsigned short>(env.__control | (1u << 0) |
545 (1u << 2) | (1u << 3));
546 env.__status = static_cast<unsigned short>(env.__status & ~0x3Fu);
547#endif
548 fesetenv(&env);
549#if defined(__x86_64__)
550 _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() | _MM_MASK_INVALID |
551 _MM_MASK_DIV_ZERO | _MM_MASK_OVERFLOW);
552#endif
553#endif
554}
555
557 std::lock_guard<std::mutex> lock(mutex_);
558 feholdexcept(&orig_feenv);
559}
560
562 std::lock_guard<std::mutex> lock(mutex_);
563 fesetenv(&orig_feenv);
564}
565
566} // namespace eonc
std::mutex mutex_
Definition fpe_handler.h:43
RAII resource manager for the ARTn C library with global synchronization.
static void write_fault_rip(void *scp)
static void fpe_signal_handler(int sig, siginfo_t *sip, void *scp)
static bool advance_integer_div(void *scp)
static constexpr unsigned MXCSR_MASK_ZM
constexpr std::uint32_t maskWindowsMxcsrForContinue(std::uint32_t mxcsr)
Definition fpe_handler.h:24
void enableFPE()
static constexpr unsigned MXCSR_MASK_OM
static X86DivRegs x86_div_regs(void *scp)
static DecodedDiv decode_div_insn(const unsigned char *code, bool long_mode)
void disableFPE()
static constexpr unsigned MXCSR_MASK_IM