我需要使用Sun Studio构建应用程序。此应用程序使用只能使用Gnu C ++构建的共享库。共享库有一个C接口,因此Sun编译器可以调用代码(这是为了避免名称错位问题,另请参阅this question)。
除了异常处理之外的所有工作都可以。当共享库中抛出异常时,程序会出现段错误。仅当使用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() 库/ 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)
问题:为什么exeption处理不适用于Sun Studio?
如果我像这样强制执行gcc运行时:
LD_PRELOAD=/usr/sfw/lib/amd64/libgcc_s.so ./example
崩溃的方式不同:
$&GT;在抛出'std :: runtime_error'的实例后终止调用 $&GT;终止被称为递归
(dbx)其中1 __lpp_sigqueue(0x1,0x6,0xffffc1000bae5060, 0xffffffff,0x0,0xffff80ffbffff810),位于0xffff80ffbf51e70a [2] thr_kill(0x0,0x0,0x0,0x0,0x0,0x0),位于0xffff80ffbf512ec8 [3] 提升(0x0,0x0,0x0,0x0,0x0,0x0),位于0xffff80ffbf4c291d [4] 中止(0x0,0x0,0x0,0x0,0x0,0x0),位于0xffff80ffbf497ff2 [5] __gnu_cxx :: __ verbose_terminate_handler(0x0,0x0,0x0,0x0,0x0,0x0),位于0xffff80ffbd9de911 [6] __cxxabiv1 :: __ terminate(0x0,0x0,0x0, 0x0,0x0,0x0),位于0xffff80ffbd9dbd5b [7] std :: terminate(0x0,0x0, 0x0,0x0,0x0,0x0),位于0xffff80ffbd9dbda3 [8] __cxa_rethrow(0x0, 0x0,0x0,0x0,0x0,0x0),位于0xffff80ffbd9dc02d [9] __gnu_cxx :: __ verbose_terminate_handler(0x0,0x0,0x0,0x0,0x0,0x0),位于0xffff80ffbd9de8d4 [10] __cxxabiv1 :: __ terminate(0x0,0x0,0x0, 0x0,0x0,0x0),位于0xffff80ffbd9dbd5b [11] std :: terminate(0x0,0x0, 0x0,0x0,0x0,0x0),位于0xffff80ffbd9dbda3 [12] __cxa_throw(0x0, 0x0,0x0,0x0,0x0,0x0),位于0xffff80ffbd9dbfd6 [13] clayerCall(0x0, 0x0,0x0,0x0,0x0,0x0),位于0xffff80ffb9991116 =&gt; [14] main(argc = 1,argv = 0xffff80ffbffffa78),“exampleMain.cpp”中的第6行
以下是重现问题的最小示例:
exampleMain.cpp:
#include <clayer.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (!clayerCall())
printf("got exception\n");
else
printf("OK\n");
}
共享lib标头:
extern "C" {
bool clayerCall();
} // end extern "C"
共享lib源:
#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
)
答案 0 :(得分:7)
异常处理需要库和链接器支持,这些支持在Sun Studio C ++工具链和Gnu C ++之间有所不同(这样就像名称修改一样,您已经注意到这两个工具链之间存在差异)。使用“C”链接对您没有帮助,因为您链接的函数的实现依赖于该异常处理工具。您通常不能使用在同一可执行文件中使用两个不同工具链构建的C ++代码。
如果您必须使用Sun Studio,因为您使用的是仅与Sun Studio兼容的闭源库,那么最简单的方法可能是使用“仅使用GNU C ++构建”的库来构建Sun C ++编译器,假设该库是开源的。这可能不是微不足道的,您可能需要得到图书馆作者的支持。当我不得不通过编写看起来像GNU C ++命令的小脚本来调用带有正确标志的Sun编译器时,我就完成了这个。
如果这是不可能的,您可能必须包装您尝试在服务中使用的库,并使用某种类型的RPC机制从Sun Studio编译的代码中访问它。
编辑:由于链接到的库是专门提升的,this question可能会有所帮助。总之,一些boost可能会使用您的Sun编译器版本构建。
答案 1 :(得分:5)
如果未禁用异常,则无法将使用gcc构建的共享库加载到使用solaris studio 12.3构建的可执行文件中。
问题是异常不是C ABI的一部分。 Solaris Studio运行时和gcc运行时使用_Unwind调用的不同实现:
gcc(特别是libstdc ++)碰巧使用了Solaris libc中不存在的其他非标准_Unwind调用,libc中Unwind实现的实现细节不同于 libgccs的,所以当所有标准的_Unwind例程都是 已解决为Solaris版本和一个非标准的_Unwind例程 解决成gcc版本你得到一个问题(最有可能的是什么 发生在你身上) (见here for more information)
对于使用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和g ++运行时库的一个版本 随Oracle Solaris Studio提供。对于此版本,版本 使用了4.8.2的g ++运行时库。 (reference)