共享库中的内部异常终止最终用户应用程序

Posted

技术标签:

【中文标题】共享库中的内部异常终止最终用户应用程序【英文标题】:Internal exceptions in shared library terminate end user application 【发布时间】:2013-08-02 11:24:37 【问题描述】:

我正在构建一个内部使用 Boost.thread 的共享库。因此,Boost.system 也被使用,因为 Boost.thread 依赖于它。我的共享库导出了一个 C 接口,所以我想对最终用户隐藏我所有的内部异常处理和线程使用等。可以这么说,它应该是一个黑匣子。但是,当我与客户端应用程序链接时,当程序运行良好时——只要通过调用库函数来停止处理,我就会得到:

在抛出 'boost::thread_interrupted' 实例后调用终止

我在库内部捕获了这个异常,所以我不知道为什么它实际上没有被捕获。最终用户的程序并不意味着以任何方式了解或处理 Boost 异常。在构建共享库时,我对 Boost.thread 和 Boost.system 都使用静态链接,因此外部世界永远不会看到它们。我在 Ubuntu 12 上使用 GCC 4.7。在 Windows 上,我没有这样的问题(MSVC 或 MinGw 都没有)。

(编辑)

根据 cmets 中的请求,我正在编辑问题以显示重现问题的简约示例。

这里首先是testlib.cpp和testlib.h的代码。

testlib.cpp:

#include 无效线程函数() 而(1) boost::this_thread::interruption_point(); 无效 do_processing() // 启动一个线程来执行上面的函数。 boost::thread worker(thread_func); // 出于本示例的目的,我们假设线程正确启动。 // 现在让我们中断线程。 worker.interrupt(); // 现在让我们等待它完成。 worker.join();

现在是 testlib.h:

#ifndef TESTLIB_H #define TESTLIB_H 无效做处理(); #万一

我使用以下命令将其构建到共享库中:

g++ -static-libgcc -static -s -DNDEBUG -I /usr/boost_1_54_0 -L /usr/boost_1_54_0/stage/lib -Wall -shared -fPIC -o libtestlib.so testlib.cpp -lboost_thread -lboost_system -lpthread -O3

然后,我有一个简单的客户端程序的代码,如下所示:

#include “testlib.h” #include 主函数() 做处理(); printf("执行正确完成。\n"); 返回0;

我构建客户端如下:

g++ -DNDEBUG -I /usr/boost_1_54_0 -L ./ -Wall -o client client.cpp -ltestlib -O3

当我运行客户端时,我得到:

在抛出 'boost::thread_interrupted' 实例后调用终止 中止(核心转储)

我没有明确捕获线程中断异常,但根据 Boost 文档,Boost.thread 会这样做并仅终止给定线程。我尝试从 thread_func 函数中显式捕获异常,但这没有任何区别。

(编辑结束)

(编辑 2)

值得注意的是,即使打开了 -fexceptions,问题仍然存在。此外,我尝试抛出和捕获与捕获和抛出异常的代码在同一翻译单元中定义的异常,但没有任何改进。简而言之,所有异常似乎都没有在共享库中被捕获,即使我肯定有它们的捕获处理程序。当我将客户端文件和 testlib 文件编译为单个程序的一部分时,也就是说,没有将 testlib 变成共享库,一切都按预期工作。

(编辑结束 2)

有什么建议吗?

【问题讨论】:

我建议做一个 minimal 测试来演示这个问题(想想 5 行 Makefile)。把它放在 github/gist 上。很有可能,您会在此过程中找到原因/解决方法。如果没有,你实际上会有一些东西给我们看。 @sehe cmets 应该无限赞成,尤其是对于“很有可能,您会在此过程中找到原因/解决方法”。无数次我开始一个关于 SO 的问题,只是为了找到它自己解决,同时试图获得最小的复制代码。 @stijn 我知道。老实说,我只是这么说,因为我们真的别无选择,除了疯狂的猜测,就像现在一样。我真的很愿意帮助调查此事,但没有什么可继续的。 我只是按照要求用一个简约的例子编辑了这个问题。我很难弄清楚如何将某些部分标记为代码,所以看起来有点奇怪。它似乎将我的 # 符号视为标题,就像在降价中一样。 再次编辑它以尝试改进代码部分。 【参考方案1】:

我终于明白了。当指定 -shared 时,不应指定 -static 标志。我的信念是,它只是告诉链接器更喜欢它链接的库的静态版本,而是使生成的动态库不适合动态链接,这有点讽刺。但它就在那里。删除 -static 解决了我所有的问题,并且我可以在我的动态库中静态链接 Boost,它可以完美地处理异常。

【讨论】:

【参考方案2】:

也许this?

如果你有一个抛出 E 的库 L,那么 L 和 应用程序 A 必须与 X 链接,该库包含 E的定义。

也尝试将可执行文件与 boost 链接起来。

【讨论】:

不幸的是,这对我不起作用。该库适用于最终用户,所有内部依赖项都是静态链接的,因此客户端应用程序不必担心它们。强制对 Boost 的依赖是不可行的。 为了进一步澄清,应用程序 (a) 并不意味着了解 e。 l 应该在自身的不同部分同时抛出和捕获 e,但 a 甚至不应该看到 e。【参考方案3】:

本身包含静态链接库的共享库并不是一个好主意,我认为 GNU 工具链不能很好地支持这种情况。

我认为您的特定问题来自选项-static-libgcc,但我无法使用您的选项在我的机器上编译它。并不是说静态链接到libpthread.so 听起来是个好主意......如果主可执行文件想要创建自己的线程会发生什么?会用-pthread编译吗?如果是,那么您将链接两次线程函数;如果不是,它将具有函数,但没有预编译器宏或线程安全库函数。

我的建议是不要静态编译你的库,这不是 Linux 的方式。

实际上这应该不是一个真正的问题,即使您不想依赖于 boost 的分发版本:针对共享的 boost 库编译您的程序并部署所有这些文件(libboost_thread.so.1.54.0libboost_system.so.1.54.0libtestlib.so) 到同一目录。然后用LD_LIBRARY_PATH=<path-to-so-files> 运行你的程序。由于客户端不打算直接使用 boost,它不需要 boost 标头,也不需要在编译器命令中链接它们。你仍然有你的黑匣子,但现在它由 3 个 *so 文件组成,而不是只有 1 个。

【讨论】:

即使是在我的库中抛出并在该库中声明的异常也未被捕获。这发生在根本不包括 Boost 的情况下,并且当 -static-libgcc 选项不存在时发生。强迫用户包含 Boost 不是我想做的事情,如果用户必须关心我的内部依赖关系,它不再是一个黑匣子。动态链接到 pthread 很好,但 Boost 是另一回事。尽管正如我所提到的,即使没有 Boost,即使在库本身中定义、抛出和捕获异常类型,也会出现异常问题。

以上是关于共享库中的内部异常终止最终用户应用程序的主要内容,如果未能解决你的问题,请参考以下文章

无法从 main.cpp 中的共享库中捕获异常

C++ 静态库中的共享全局变量:Linux

彻底解决win7安装oracle 10g时发生“程序异常终止,发生内部错误”

C++异常处理机制

C++异常处理机制

SQL Server 2000怎么以单用户模式使用RESTORE DATABASE操作异常终止 怎么办?