I'm using LibJIT to JIT-compile a programming language. My compiler is written in C++, so I have a C++ program calling the LibJIT library, which is written in C.
In my C++ code I have some functions that throw exceptions, and sometimes these get triggered on the far side of a LibJIT-compiled function. That is, my C++ program uses LibJIT to compile a function, calls it, and the JIT-compiled function in turn calls a C++ function that throws an exception. But instead of the exception being propagated back through the JIT-compiled function to my handler, the runtime calls std::terminate.
Apparently the exception-handling mechanism needs each function in the call stack to implement some kind of exception support. When compiling C code, the -fexceptions flag tells the compiler (at least gcc) to include this support.
Is there a way to include this exception support in my LibJIT-generated functions?
I tried using the LibJIT instructions to set up a "catcher" block that rethrows all exceptions, but that didn't work; it looks like the LibJIT's exception-handling model is separate from the C++ model.
Obviously I don't really want to throw C++ exceptions into my language's code, and I'll have to figure out some other error-handling strategy. But I'm wondering if it's even possible to get it to work.
Here's a sample program that demonstrates the issue. I'm sorry it's this long but LibJIT is pretty low-level so you have to write a lot of code to get anything done. I'm compiling on Linux with clang 10.0.0.
#include <array>
#include <exception>
#include <iostream>
#include <string>
#include <jit/jit.h>
class ex : public std::exception {};
extern "C" int bad(int arg) {
std::cout << "Called with arg=" << arg << std::endl;
throw ex();
}
jit_function_t build_function(jit_context_t context) {
// The signature of the compiled function and "bad" are the same.
std::array<jit_type_t, 1> params{jit_type_int};
jit_type_t signature = jit_type_create_signature(jit_abi_cdecl, jit_type_int, params.data(), params.size(), 1);
jit_function_t function = jit_function_create(context, signature);
jit_value_t arg_value = jit_value_get_param(function, 0);
jit_value_t bad_value =
jit_value_create_nint_constant(function, jit_type_void_ptr, (reinterpret_cast<jit_nint>(bad)));
std::array<jit_value_t, 1> bad_args{arg_value};
jit_value_t result = jit_insn_call_indirect(function, bad_value, signature, bad_args.data(), bad_args.size(), 0);
jit_insn_return(function, result);
jit_function_compile(function);
return function;
}
int main(int argc, char* argv[]) {
// This exception will be caught:
try {
bad(7);
} catch (const ex&) {
std::cout << "Caught exception from direct call" << std::endl;
}
// Initialize libjit.
// We're single-threaded so not going to bother with jit_context_build_start/jit_context_build_end.
jit_init();
jit_context_t context = jit_context_create();
jit_function_t function = build_function(context);
// This one will not be caught, calls std::terminate instead:
try {
int (*closure)(int) = reinterpret_cast<int (*)(int)>(jit_function_to_closure(function));
closure(7);
} catch (const ex&) {
std::cout << "Caught exception from libjit call" << std::endl;
}
return 0;
}
Output:
Called with arg=7
Caught exception from direct call
Called with arg=7
terminate called after throwing an instance of 'ex'
what(): std::exception
Aborted
Related
Attempting to compile the following code with vcvarsall.bat in command line throws a warning saying that the exception handler is needed in the code but not called before using /EHsc.
Code:
#include <iostream>
int main()
{
std::cout << "hello world" << std::endl;
return 0;
}
The batch file:
#echo off
cl C:\Development\..\basicmath.cpp
The warning:
C:\...\ostream(746): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
C:...\basicmath.cpp(10): note: see reference to function template instantiation 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const char *)' being compiled
Line 743 - 754 of ostream line 746 (from error) is _TRY:
if (!_Ok) {
_State |= ios_base::badbit;
} else { // state okay, insert
_TRY_IO_BEGIN
if ((_Ostr.flags() & ios_base::adjustfield) != ios_base::left) {
for (; 0 < _Pad; --_Pad) { // pad on left
if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) {
_State |= ios_base::badbit; // insertion failed, quit
break;
}
}
}
Adding /EHsc to my batch file will allow it to run but I would like to know why this is. Why does this block of code from the file for output require EHsc to be called?
MSDOCS says EHsc is for cleanup to prevent memory leaks, what is causing the leak and why do they need an external program to fix the leak instead of fixing it in the same file (this may sound rude but its just ignorant)?
Edit: thank you for pointing out its a warning and not an error.
Short answer:
Add /EHs or /EHsc to your compilation options as the documentation suggests. It's the most portable option regarding exceptions handling, if you will ever need to execute the same code on Unix machine.
Long answer:
There are two parts to this question. First is why the warning occurs in iostream and the second is what does the warning mean.
Why are there exceptions in iostream?
The default behaviour of streams in C++ is exceptionless - any failure is represented by setting an internal fail bit, accessible with eof(), fail() and bad() functions. However, you can change this behaviour to throwing exceptions on failure by using exceptions() method on stream. You can choose which fail bits trigger exceptions, but the main point is that the code must be there by standard. The warning seems to analyze only that - it notices a possible path where throw occurs and reports a warning.
What does the warning mean?
From the Microsoft documentation (emphasis mine):
By default (that is, if no /EHsc, /EHs, or /EHa option is specified), the compiler supports SEH handlers in the native C++ catch(...) clause. However, it also generates code that only partially supports C++ exceptions . The default exception unwinding code doesn't destroy automatic C++ objects outside of try blocks that go out of scope because of an exception.
The issue is that (for some reason) MSVC compiler by default generates assembly which is wrong according to the standard. Stack unwinding will not be perfomerd when exception is thrown, which may cause memory leaks and other unexpected behaviours.
An example correct C++ code, which has a memory leak under the default setting:
void foo()
{
std::string str = "This is a very long string. It definitely doesn't use Small String Optimization and it must be allocated on the heap."
std::cout << str;
throw std::runtime_error{"Oh no, something went wrong"};
}
int main()
{
try
{
foo();
}
catch (std::exception&)
{
// str in foo() was possibly not released, because it wasn't deleted when exception was thrown!
}
}
So the final answer would be:
If you plan to use Structured Exceptions (like divide-by-zero or invalid memory access errors) or use a library that uses them, use /EHa
If you don't need to catch SEs, choose /EHs for compatibility with C++ standard and portability
Never leave the defaults, always set /EH to one alternative or another, otherwise you will have to deal with strange behaviours when using exceptions.
That's a warning so your current program compiles fine. But problems come up in programs as such:
#include <exception>
#include <iostream>
struct A{
A(int x):x(x) {
std::cout<<"Contructed A::"<<x<<'\n';
}
~A() {
std::cout<<"Destructed A::"<<x<<'\n';
}
private:
int x;
};
void foo() {
A a{2};
throw std::bad_exception{};
}
int main()
{
A a {1};
try {
foo();
} catch(const std::bad_exception& ex) {
std::cout<<ex.what()<<'\n';
}
return 0;
}
Using cl test.cpp yields the output:
Contructed A::1
Contructed A::2
bad exception
Destructed A::1
While using cl test.cpp /EHsc yields:
Contructed A::1
Contructed A::2
Destructed A::2
bad exception
Destructed A::1
This behavior is explained by the documentation for the warning C4530:
When the /EHsc option isn't enabled, automatic storage objects in the
stack frames between the throwing function and the function where the
exception is caught don't get destroyed. Only the automatic storage
objects created in a try or catch block get destroyed, which can lead
to significant resource leaks and other unexpected behavior.
That explains a {2} not being destructed when the program wasn't compiled with /EHsc.
And of course,
If no exceptions can possibly be thrown in your executable, you may
safely ignore this warning.
So, for a program like
#include <cstdio>
int main()
{
std::printf("hello world\n");
return 0;
}
cl.exe quietly compiles.
In my application I'm using coroutine2 to generate some objects which I have to decode from a stream. These objects are generated using coroutines. My problem is that as soon as I reach the end of the stream and would theoretically throw std::ios_base::failure my application crashes under certain conditions.
The function providing this feature is implemented in C++, exported as a C function and called from C#. This all happens on a 32bit process on Windows 10 x64. Unfortunately it only reliably crashes when I start my test from C# in debugging mode WITHOUT the native debugger attached. As soon as I attach the native debugger everything works like expected.
Here is a small test application to reproduce this issue:
Api.h
#pragma once
extern "C" __declspec(dllexport) int __cdecl test();
Api.cpp
#include <iostream>
#include <vector>
#include <sstream>
#include "Api.h"
#define BOOST_COROUTINES2_SOURCE
#include <boost/coroutine2/coroutine.hpp>
int test()
{
using coro_t = boost::coroutines2::coroutine<bool>;
coro_t::pull_type source([](coro_t::push_type& yield) {
std::vector<char> buffer(200300, 0);
std::stringstream stream;
stream.write(buffer.data(), buffer.size());
stream.exceptions(std::ios_base::eofbit | std::ios_base::badbit | std::ios_base::failbit);
try {
std::vector<char> dest(100100, 0);
while (stream.good() && !stream.eof()) {
stream.read(&dest[0], dest.size());
std::cerr << "CORO: read: " << stream.gcount() << std::endl;
}
}
catch (const std::exception& ex) {
std::cerr << "CORO: caught ex: " << ex.what() << std::endl;
}
catch (...) {
std::cerr << "CORO: caught unknown exception." << std::endl;
}
});
std::cout << "SUCCESS" << std::endl;
return 0;
}
C#:
using System;
using System.Runtime.InteropServices;
namespace CoroutinesTest
{
class Program
{
[DllImport("Api.dll", EntryPoint = "test", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 test();
static void Main(string[] args)
{
test();
Console.WriteLine("SUCCESS");
}
}
}
Some details:
We are using Visual Studio 2015 14 and dynamically link the c++ runtime.
The test library statically links Boost 1.63.0.
We also tried to reproduce this behaviour with calling the functionallity directly from c++ and from python. Both tests have not been successful so far.
If you start the c# code with CTRL F5 (meaning without the .net debugger) everything will also be fine. Only if you start it with F5 (meaning the .NET Debugger attached) the visual studio instance will crash. Also be sure not to enable the native debugger!
Note: If we don't use the exceptions in the stream, everything seams to be fine as well. Unfortunately the code decoding my objects makes use of them and therefore I cannot avoid this.
It would be amazing if you had some additional hints on what might go wrong here or a solution. I'm not entirely sure if this is a boost bug, could also be the c# debugger interfering with boost-context.
Thanks in advance! Best Regards, Michael
I realize this question is old but I just finished reading a line in the docs that seemed pertinent:
Windows using fcontext_t: turn off global program optimization (/GL) and change /EHsc (compiler assumes that functions declared as extern "C" never throw a C++ exception) to /EHs (tells compiler assumes that functions declared as extern "C" may throw an exception).
This is just a guess but in your coroutine I think you are supposed to push a boolean to your sink (named as yield in your code) and the code is not doing it.
While reviewing bit of code, I came across a buggy std::terminate() handler
that wasn't terminating the program, but returning. Based on the documentation for std::set_terminate(), I think this falls within the realms of undefined - or at least implementation defined - behaviour.
Under Linux, and compiled using GCC, I found that cores were being dumped, which implies that some guardian angel was calling abort() or something similar on our behalf.
So I wrote the following test snippet, which confirmed my hunch. It looks like GCC or it's std library does wrap std::terminate() handlers so they do terminate the program.
#include <iostream>
#include <exception>
#include <dlfcn.h>
// Compile using
// g++ main.cpp -ldl
// Wrap abort() in my own implementation using
// dlsym(), so I can see if GCC generates code to
// call it if my std::terminate handler doesn't.
namespace std
{
void abort()
{
typedef void (*aborter)();
static aborter real_abort = 0x0;
if (0x0 == real_abort)
{
void * handle = 0x0;
handle = dlsym(RTLD_NEXT, "abort");
if (handle)
{
real_abort = (aborter )(handle);
}
}
std::cout << "2. Proof that GCC calls abort() if my buggy\n"
<< " terminate handler returns instead of terminating."
<< std::endl;
if (real_abort)
{
real_abort();
}
}
}
// Buggy terminate handler that returns instead of terminating
// execution via abort (or exit)
void buggyTerminateHandler()
{
std::cout << "1. In buggyTerminateHandler." << std::endl;
}
int main (int argc, char ** argv)
{
// Set terminate handler
std::set_terminate(buggyTerminateHandler);
// Raise unhandled exception
throw 1;
}
That a compiler (or library) would wrap std::terminate() handlers seems sensible to be, so at a guess I'd assume that most compilers do something along these lines.
Can anyone advise regarding the behaviour on Windows using Visual Studio or OS X using GCC?
I'm working on an application written in Visual Studio 6 (I know, FML) that is calling functions in a DLL using LoadLibrary and GetProcAddress. The newer code can't compile in VC6, and needs a newer compiler. The DLL has a few functions that construct a C++ object, and then the VC6 program uses the object through an abstract class.
This works just fine usually, but it runs into problems when the functions retrieved by GetProcAddress throw exceptions -- even when the exceptions are caught within the DLL. I've noticed that this doesn't happen when the abstract class's methods throw an exception. Things work normally in that case.
What am I doing wrong here? How can I make VC6 generate code to handle the exceptions properly?
Edit: Here's an example of a function that causes the program to crash:
extern "C" __declspec(dllexport) Box* getBox(const char* addr)
{
try {
return createBox(addr);
} catch (std::exception& ex) {
LOG_ERROR("Open failed: " << ex.what());
return 0;
} catch (...) {
LOG_ERROR("Error while opening.");
return 0;
}
}
You cannot do inheritance cross compiler versions like that. It almost works but exceptions and a few other things go crazy.
I'm trying to play with handling unexpected exceptions, but cannot make it to work. This is example from: C++ Reference
// set_unexpected example
#include <iostream>
#include <exception>
using namespace std;
void myunexpected () {
cerr << "unexpected called\n";
throw 0; // throws int (in exception-specification)
}
void myfunction () throw (int) {
throw 'x'; // throws char (not in exception-specification)
}
int main (void) {
set_unexpected (myunexpected);
try {
myfunction();
}
catch (int) { cerr << "caught int\n"; }
catch (...) { cerr << "caught other exception (non-compliant compiler?)\n"; }
return 0;
}
They say that output should be:
Output:
unexpected called
caught int
Which doesn't happen when I try it. My output is:
caught other exception (non-compliant compiler?)
I'm using VS2010 sp1
Documentation of unexpected says:
The C++ Standard requires that unexpected is called when a function throws an exception that is not on its throw list. The current implementation does not support this.
So the answer is that VC10 is a non-compliant compiler.
Visual Studio does not implement the standard correctly. See this MSDN page. The int is parsed, but not used.
Visual C++ always issues Warning C4290 when function has exception specifiers. From the same article in MSDN: "A function is declared using exception specification, which Visual C++ accepts but does not implement." So, this compiler does not comply with standard.