5

我需要使用 Sun Studio 构建一个应用程序。此应用程序使用只能使用 Gnu C++ 构建的共享库。共享库有一个 C 接口,因此代码可由 Sun 编译器调用(这是为了避免名称混淆问题,另请参阅此问题)。

除了异常处理之外的一切都很好。当共享库中抛出异常时,程序会出现段错误。仅当使用 Sun Studio 编译器编译主程序时才会发生这种情况。使用 Gnu C++ 编译器编译下面的最小示例,程序运行良好,共享库检测到异常。

计划 A:动态链接以下是设置说明:

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)

结果:一旦抛出异常,就会违反分段(参见下面的代码)。

方案 B:静态链接

如上,但构建 c_layer.a

结果:

文件中未定义的第一个引用符号
__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。一个(c_layer.cpp.o)

问题为什么异常处理不适用于 Sun Studio?


如果我像这样强制执行 gcc 运行时:

LD_PRELOAD=/usr/sfw/lib/amd64/libgcc_s.so ./example

它以不同的方式崩溃:

$> 在抛出 'std::runtime_error' 的实例后调用终止 $> 递归调用终止

(dbx) 其中 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),在0xffff80ffbf4c291d [4] abort(0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0),at 0xffffffffffffffbf497ff2 [5] [6] __cxxabiv1::__terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), 在 0xffff80ffbd9dbd5b [7] std::terminate(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), 在 0xffff80ffbd9dbda3 [8] __cx_areth (0x0, 0x0, 0x0, 0x0, 0x0, 0x0), 在 0xffff80ffbd9dc02d [9] __gnu_cxx::__verbose_terminate_handler(0x0, 0x0, 0x0, 0x0, 0x0, 0x0), 在 0xffff80ffbd9de8d4 [10] __x0,0x1: 终止, 0x0, 0x0, 0x0, 0x0), 在 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, 0x00, 0x0, 0x)0, , 在 0xffff80ffb9991116 =>[14] main(argc = 1, argv = 0xffff80ffbffffa78), "exampleMain.cpp" 中的第 6 行


这是重现问题的最小示例:

示例Main.cpp:

#include <clayer.h> 
#include <stdio.h>

int main(int argc, char **argv)
{
    if (!clayerCall())
        printf("got exception\n");
    else
        printf("OK\n");
}

共享库头:

extern "C" {

bool clayerCall();

} // end extern "C" 

共享库源:

#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

cmake 文件如下所示:

对于可执行文件

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
)

为图书馆

project(clayer)
cmake_minimum_required(VERSION 2.8)
cmake_policy(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)

add_library(
    clayer SHARED
    clayer.cpp
)
4

2 回答 2

7

异常处理需要库和链接器支持,这在 Sun Studio C++ 工具链和 Gnu C++ 之间是不同的(这种方式就像名称修改,您已经注意到这两个工具链之间的差异)。在这里使用“C”链接对您没有帮助,因为您要链接的函数的实现取决于该异常处理工具。通常,您不能在同一个可执行文件中使用由两个不同工具链构建的 C++ 代码。

如果您必须使用 Sun Studio,因为您使用的是仅与 Sun Studio 兼容的闭源库,那么您最简单的方法可能是让“仅使用 GNU C++ 构建”的库使用 Sun C++ 编译器构建,假设该库是开源的。这可能不是微不足道的,您可能需要获得图书馆作者的支持。当我不得不写一些看起来像 GNU C++ 命令的小脚本来调用带有正确标志的 Sun 编译器时,我已经做到了这一点。

如果这是不可能的,您可能必须将您尝试在服务中使用的库包装起来,并使用某种 RPC 机制从您的 Sun Studio 编译代码中访问它。

编辑:由于链接到的库是专门提升的,这个问题可能会有所帮助。总之,您的 Sun 编译器版本可能会构建一些增强功能。

于 2014-12-15T18:23:08.457 回答
5

如果未禁用异常,则无法将使用 gcc 构建的共享库加载到使用 solaris studio 12.3 构建的可执行文件中。

问题是异常不是 C ABI 的一部分。Solaris Studio 运行时和 gcc 运行时使用不同的 _Unwind 调用实现:

gcc(特别是 libstdc++)碰巧使用了额外的非标准 _Unwind 调用,而这些调用在 Solaris libc 中自然不存在,在 libc 中 Unwind 实现的实现细节与 libgccs 不同,所以当所有标准 _Unwind 例程都被解析为 Solaris 版本和一个非标准的 _Unwind 例程被解析为 gcc 版本,您会遇到问题(很可能是您遇到的问题)(有关更多信息,请参见此处

对于使用 solaris studio 构建的部分和 gcc 部分,不可能执行一个具有不同 C++ 运行时的进程。因此,无法将使用 gcc 构建的共享库加载到使用 Solaris Studio 构建的可执行文件中。


好消息是,从 solaris studio 12.4 开始,如果您打开 C++11 支持,它实际上可能会起作用:

在 Oracle Solaris Studio 12.4 中,C++ 编译器支持 C++11、一种新语言和 ABI(应用程序二进制接口)。在 C++ 11 模式下,CC 编译器使用 g++ ABI 和随 Oracle Solaris Studio 提供的 g++ 运行时库版本。对于此版本,使用了 g++ 运行时库的 4.8.2 版。(参考

于 2014-12-17T13:54:22.713 回答