0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-15 22:41:12 +01:00

ircd::fpe: Add experimental exception-throwing SIGFPE w/ -fnon-call-exceptions.

This commit is contained in:
Jason Volk 2018-11-11 18:29:06 -08:00
parent 5d36bdf476
commit 47732a0075
3 changed files with 122 additions and 0 deletions

View file

@ -411,6 +411,14 @@ dnl CXXFLAGS+=" -fcheck-pointer-bounds"
CXXFLAGS+=" -fchkp-instrument-marked-only"
CXXFLAGS+=" -fstack-protector-explicit"
dnl !!! Experimental !!!
dnl Allows a signal handler to throw a C++ exception if the signal is from
dnl a CPU trap on an instruction (ex. SIGFPE from a floating point divide
dnl by zero). This allows the signal handler to transfer control to the
dnl catch block wrapping the area which errored, thereby aborting any further
dnl execution after the SIGFPE was received which would have to manually check
dnl for such an error after each floating point operation and throw.
CXXFLAGS+=" -fnon-call-exceptions"
dnl -ffriend-injection allows us to deduplicate declarations of friend
dnl functions in both the friend class and the enclosing namespace

View file

@ -11,10 +11,17 @@
#pragma once
#define HAVE_IRCD_FPE_H
extern "C"
{
struct sigaction;
};
namespace ircd::fpe
{
struct errors_handle;
struct errors_throw;
string_view reflect_sicode(const int &);
string_view reflect(const ushort &flag);
string_view reflect(const mutable_buffer &, const ushort &flags);
void throw_errors(const ushort &flags);
@ -32,3 +39,14 @@ struct ircd::fpe::errors_handle
errors_handle();
~errors_handle() noexcept(false);
};
struct ircd::fpe::errors_throw
{
custom_ptr<struct sigaction> their_sa;
long their_fenabled;
fexcept_t their_fe;
public:
errors_throw();
~errors_throw() noexcept;
};

View file

@ -8,10 +8,88 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#include <signal.h>
#ifndef __GNUC__
#pragma STDC FENV_ACCESS on
#endif
//
// errors_throw
//
namespace ircd::fpe
{
constexpr const size_t &SA_HEAP_MAX {64};
thread_local allocator::fixed<struct sigaction, SA_HEAP_MAX> sa_heap;
extern const std::exception_ptr sigfpe_eptr;
extern "C" void ircd_fpe_handle_sigfpe(int signum, siginfo_t *const si, void *const uctx);
}
decltype(ircd::fpe::sigfpe_eptr)
ircd::fpe::sigfpe_eptr
{
std::make_exception_ptr(std::domain_error
{
"Floating Point Exception"
})
};
__attribute__((noreturn))
void
ircd::fpe::ircd_fpe_handle_sigfpe(int signum,
siginfo_t *const si,
void *const uctx)
{
assert(si);
assert(signum == SIGFPE);
std::rethrow_exception(sigfpe_eptr); //TODO: still malloc()'s :/
}
//
// errors_throw::errors_throw
//
ircd::fpe::errors_throw::errors_throw()
:their_sa
{
new (sa_heap().allocate(1)) struct sigaction,
[](auto *const ptr)
{
sa_heap().deallocate(ptr, 1);
}
}
,their_fenabled
{
syscall(::fegetexcept)
}
{
struct sigaction ours {0};
ours.sa_sigaction = ircd_fpe_handle_sigfpe;
ours.sa_flags |= SA_SIGINFO;
ours.sa_flags |= SA_NODEFER; // Required to throw out of the handler
syscall(std::fegetexceptflag, &their_fe, FE_ALL_EXCEPT);
syscall(std::feclearexcept, FE_ALL_EXCEPT);
syscall(::sigaction, SIGFPE, &ours, their_sa.get());
syscall(::feenableexcept, FE_ALL_EXCEPT);
}
ircd::fpe::errors_throw::~errors_throw()
noexcept
{
syscall(::fedisableexcept, FE_ALL_EXCEPT);
syscall(::sigaction, SIGFPE, their_sa.get(), nullptr);
syscall(::feenableexcept, their_fenabled);
syscall(std::fesetexceptflag, &their_fe, FE_ALL_EXCEPT);
}
//
// errors_handle
//
ircd::fpe::errors_handle::errors_handle()
{
syscall(std::fegetexceptflag, &theirs, FE_ALL_EXCEPT);
@ -98,3 +176,21 @@ ircd::fpe::reflect(const ushort &flag)
return "?????";
}
ircd::string_view
ircd::fpe::reflect_sicode(const int &code)
{
switch(code)
{
case FPE_INTDIV: return "INTDIV";
case FPE_INTOVF: return "INTOVF";
case FPE_FLTDIV: return "FLTDIV";
case FPE_FLTOVF: return "FLTOVF";
case FPE_FLTUND: return "FLTUND";
case FPE_FLTRES: return "FLTRES";
case FPE_FLTINV: return "FLTINV";
case FPE_FLTSUB: return "FLTSUB";
}
return "?????";
}