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:
parent
5d36bdf476
commit
47732a0075
3 changed files with 122 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
96
ircd/fpe.cc
96
ircd/fpe.cc
|
@ -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 "?????";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue