Sun Studio linking gcc libs: exceptions do not work

1.5k views Asked by At

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
)
2

There are 2 answers

4
antlersoft On BEST ANSWER

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.

0
Beginner On

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:

gcc (libstdc++ in particular) happens to use additional non-standard _Unwind calls which are not present in Solaris libc naturally, implementation details of Unwind implementation in libc differs to that of libgccs, so when all the standard _Unwind routines are resolved into Solaris version and one non-standard _Unwind routine is resolved into gcc version you get a problem (most likely that is what happens with you) (see here for more information)

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:

In Oracle Solaris Studio 12.4, the C++ compiler supports C++11, a new language and ABI (Application Binary Interface). In C++ 11 mode, the CC compiler uses the g++ ABI and a version of the g++ runtime library that is supplied with Oracle Solaris Studio. For this release, version 4.8.2 of the g++ runtime library is used. (reference)