I had this funny idea last night, to trap hardware exceptions and throw a C++ exception
instead. Thought that might be useful for things like FPU exceptions
, which normally either crash, or silently return NaN
and then cause unexpected behaviour. A C++ exception
would be far more desirable here.
So I've been hacking all morning and finally got it to work. Well, almost. The compiler still doesn't realize that arithmetic operations can now throw C++ exceptions
, and will silently discard the try/catch
block around it. It does work when the exception occurs in a function.
void throw_exception()
{
throw std::runtime_error("Division by zero!");
}
__attribute__((noinline))
void try_div0()
{
cout << 1 / 0 << endl;
}
int main()
{
// this class traps a hardware exception (division by zero, in this case) and calls the supplied lambda function.
// uh, no, you probably don't want to see the assembly code behind this...
exception_wrapper div0_exc { 0, [] (exception_frame* frame, bool)
{
if (frame->address.segment != get_cs()) return false; // only handle exceptions that occured in our own code
frame->stack.offset -= 4; // sub <fault esp>, 4;
auto* stack = reinterpret_cast<std::uintptr_t *>(frame->stack.offset); // get <fault esp>
*stack = frame->address.offset; // mov [<fault esp>], <fault address>;
frame->address.offset = reinterpret_cast<std::uintptr_t>(throw_exception); // set return address to throw_exception()
return true; // exception handled!
} };
try
{
// cout << 1 / 0 << endl; // this throws, as expected, but calls std::terminate().
try_div0(); // this exception is caught.
}
catch (std::exception& e)
{
cout << "oops: " << e.what() << endl;
}
}
I realize this is an unusual question... but is there any way I could make this work? Some way to tell gcc
that exceptions can occur anywhere?
I'm compiling with djgpp
which (I believe) uses DWARF
exception handling.
edit: I just found gcc
flags -fnon-call-exceptions
and -fasynchronous-unwind-tables
, which appear to be what I'm looking for. But it still doesn't work...
edit: Now using the previously mentioned gcc
flags, it does catch when the exception occurs in between two function calls:
inline void nop() { asm(""); }
// or { cout << flush; } or something. empty function does not work.
int main()
{
/* ... */
try
{
nop();
cout << 1 / 0 << endl;
nop();
}
/* ... */
}
edit: Nested try/catch
blocks have the same effect, no exception is caught unless the trapped instruction is preceded by a function call.
inline void nop() { asm(""); }
void try_div(int i)
{
try
{
// this works, catches exception in try_div(0).
nop();
cout << 1 / i << endl;
try_div(i - 1);
// without the first nop(), calls std::terminate()
//cout << 1 / i << endl;
//try_div(i - 1);
// reverse order, also terminates.
//if (i != 0) try_div(i - 1);
//cout << 1 / i << endl;
//nop();
}
catch (std::exception& e)
{
cout << "caught in try_div(" << i << "): " << e.what() << endl;
}
}
int main()
{
/* ... */
try
{
try_div(4);
}
catch (std::exception& e)
{
cout << "caught in main(): " << e.what() << endl;
}
}
edit: I have submitted this as a possible bug in gcc, and reduced my code to a simple test case.
If an exception is raised in your program and that exception is not handled by an exception section in either the current or enclosing PL/SQL blocks, that exception is "unhandled." PL/SQL returns the error which raised an unhandled exception all the way back to the application environment from which PL/SQL was run.
When an exception occurred, if you don't handle it, the program terminates abruptly and the code past the line that caused the exception will not get executed.
C doesn't support exceptions. You can try compiling your C code as C++ with Visual Studio or G++ and see if it'll compile as-is. Most C applications will compile as C++ without major changes, and you can then use the try... catch syntax.
The sort answer to your question is that you need language support for exceptions in order to write them. Language support generally includes memory management; since an exception can be thrown anywhere and caught anywhere, there needs to be a way to dispose of objects that doesn't rely on the control flow.
It's been a while, but I finally figured it out... The throwing function needs to be marked as having a signal frame.
[[gnu::no_caller_saved_registers]]
void throw_exception()
{
asm(".cfi_signal_frame");
throw std::runtime_error("Division by zero!");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With