I need to build an application with Sun Studio. This application uses a shared library which can only be build with Gnu C++. The shared lib has a C Interface, so that the code is callable by the Sun Compiler (this is to avoid name mangling issues, see also this question).
Everything besides exception handling works fine. When an exception is thrown in the shared library, the program segfaults. This happens only when the main program is compiled using the Sun Studio Compiler. Compiling the minimal example below with the Gnu C++ compiler, the program works fine and the shared lib detects the exception.
Plan A: link dynamically Here is an illustration of the setup:
GCC SOLARIS STUDIO
shared
c_layer.so <----- application
(no exceptions) (uses exceptions sol studio)
|
| use flag -static -static-libstdc++ -static-lib-gcc
v
gcc_only_lib.so
libstdc++.so
(uses gcc exceptions)
Result: segmentation violation once an exception is thrown (see code below).
Plan B: link statically
as above, but building c_layer.a
Result:
Undefined first referenced symbol
in file __cxa_allocate_exception libs/cInterface/libcInterface.a(c_layer.cpp.o) std::string::~std::basic_string () libs/cInterface/libcInterface.a(c_layer.cpp.o) __cxa_end_catch libs/cInterface/libcInterface.a(c_layer.cpp.o) __cxa_free_exception libs/cInterface/libcInterface.a(c_layer.cpp.o) __cxa_begin_catch libs/cInterface/libcInterface.a(c_layer.cpp.o) __cxa_throw libs/cInterface/libcInterface.a(c_layer.cpp.o)
Question: Why doesn't the exeption handling work with Sun Studio?
If I enforce the gcc runtime like this:
LD_PRELOAD=/usr/sfw/lib/amd64/libgcc_s.so ./example
it crashes differently:
$> terminate called after throwing an instance of 'std::runtime_error' $> terminate called recursively
(dbx) where 1 __lwp_sigqueue(0x1, 0x6, 0xffffc1000bae5060, 0xffffffff, 0x0, 0xffff80ffbffff810), at 0xffff80ffbf51e70a [2] thr_kill(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbf512ec8 [3] raise(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbf4c291d [4] abort(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbf497ff2 [5] __gnu_cxx::__verbose_terminate_handler(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9de911 [6] __cxxabiv1::__terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9dbd5b [7] std::terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9dbda3 [8] __cxa_rethrow(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9dc02d [9] __gnu_cxx::__verbose_terminate_handler(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9de8d4 [10] __cxxabiv1::__terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9dbd5b [11] std::terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9dbda3 [12] __cxa_throw(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffbd9dbfd6 [13] clayerCall(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), at 0xffff80ffb9991116 =>[14] main(argc = 1, argv = 0xffff80ffbffffa78), line 6 in "exampleMain.cpp"
Here is a minimal example to reproduce the problem:
exampleMain.cpp:
#include <clayer.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (!clayerCall())
printf("got exception\n");
else
printf("OK\n");
}
shared lib header:
extern "C" {
bool clayerCall();
} // end extern "C"
shared lib source:
#include "clayer.h"
#include <exception>
#include <stdexcept>
#include <stdio.h>
extern "C" {
bool clayerCall()
{
try
{
throw std::runtime_error("hhh");
return true;
}
catch (std::exception &ex)
{
return false;
}
}
} // end extern c
The cmake files look like this:
for the executable
project(exampleMain)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)
add_definitions(-m64 -fPIC)
include_directories(../stackoverflow)
link_directories (
../stackoverflow
)
add_executable(example exampleMain.cpp)
target_link_libraries(
example
stdc++
clayer
)
for the library
project(clayer)
cmake_minimum_required(VERSION 2.8)
cmake_policy(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)
add_library(
clayer SHARED
clayer.cpp
)
It is not possible to load a shared library which is built with gcc into an executable built with solaris studio 12.3., if exceptions are not disabled.
The problem is that exceptions are not part of the C ABI. The Solaris Studio runtime and the gcc runtime use different implementations of _Unwind calls:
It is not possible to execute one process which different C++ runtimes for the parts built with solaris studio and the gcc part. Therefore loading a shared library built with gcc into an executable built with Solaris Studio is not possible.
The good news is that from solaris studio 12.4 on, if you turn on C++11 support, it might actually work:
Exception handling requires library and linker support which differs between Sun Studio C++ tool chain and Gnu C++ (In this way it is like name mangling, which you've already noted differs between the two tool chains). Using "C" linkage doesn't help you here, because the implementations of the functions you are linking to depend on that exception handling facility. You can't in general use C++ code built with two distinct tool chains within the same executable.
If you have to use Sun Studio because you are using closed-source libraries that are only compatible with Sun Studio, your easiest way forward is probably to get the library that "only builds with GNU C++" to build with the Sun C++ compiler, presuming that that library is open source. This might not be trivial and you might need to get support from the library's authors. I've done this when I had to by writing little scripts that look like GNU C++ commands that invoke the Sun compiler with the correct flags.
If that is out of the question, you might have to wrap the library you're trying to use in a service and use an RPC mechanism of some sort to access it from your Sun Studio compiled code.
Edit: Since the library linked to is specifically boost, this question might be helpful. In summary, some pieces of boost might build with your version of the Sun compiler.