【问题标题】:Exceptions across binary boundary跨越二进制边界的异常
【发布时间】:2012-06-02 23:43:37
【问题描述】:

我知道,这个问题已经被问过很多次了,但是我找不到我的问题的解决方案。

我有以下情况:

   A
  / \
 /   \
B <-- C
  • A 是一个共享库,其中包含类EException
  • B 和 C 链接到 A
  • C 也是一个共享库
  • B 在运行时动态加载 C

在某些时候,C 抛出了一个 EException 的实例:

void doSometing() {
    throw EException("test-message");
}

B 我想捕捉这个异常:

try {
    doSomething();
} catch (const EException& ex) {
    // Not reached
} catch (...) {
    // Not reached
}

但正如代码中所提到的,没有一个 catch 子句被调用。相反,执行此代码的线程被中止。

我尝试了以下方法:

  • 编译A时EException的可见性属性设置为“默认”
  • EException 头文件仅包含声明
  • 我在 A、B 和 C 中使用链接器选项 -fvisibility=hidden
  • 我在 C 中使用链接器选项 -E

使用nm 我得到A

0000000000066260 T EException::EException(QString const&)
0000000000066306 T EException::EException(EException const&)
00000000000661d0 T EException::EException() 
0000000000066260 T EException::EException(QString const&) 
0000000000066306 T EException::EException(EException const&) 
00000000000661d0 T EException::EException() 
00000000000664de T EException::~EException()
000000000006641e T EException::~EException() 
000000000006641e T EException::~EException() 
00000000000663b6 T EException::operator=(EException const&)
<...>
000000000028de40 V typeinfo for EException
000000000028dd80 V typeinfo for EException*
000000000007342b V typeinfo name for EException
0000000000072ab7 V typeinfo name for EException*
000000000028de00 V vtable for EException

对于B

U EException::EException(QString const&)
U EException::~EException()
<...>
0000000000726f60 V typeinfo for EException

对于C

U EException::EException(QString const&)
U EException::~EException()
<...>
U typeinfo for EException

问题可能是,B 使用自己的类型信息EException,而C 使用A 提供的类型信息?我该如何解决这个问题?

我的环境:

  • x86_64-linux-gnu 上的 gcc 4.6.3
  • 使用 Qt

感谢您的帮助!

【问题讨论】:

  • catch(...) 似乎并不意味着您的错误与您的异常类本身(或其 ctor 中的错误)无关。您是否尝试过调试它并查看该投掷线上会发生什么?
  • 在 throw 行上,EException 的复制构造函数被调用。复制构造函数中没有错误,并且在它完成后线程中止。
  • 另见 GCC 常见问题解答中的 dynamic_cast, throw, typeid don't work with shared libraries。它告诉您避免 -Bsymbolic 链接器选项,并使用-E 链接器选项。我也猜测 B 应该使用 --exclude-libs ALL

标签: c++ exception-handling g++ shared-libraries


【解决方案1】:

我在 gcc

如前所述,EException 的 vtable(包含 typeinfo 对象的条目)似乎在某些翻译单元中重复,这绝对是 gcc A 中定义一个虚拟的外联析构函数(它必须是标头中的第一个虚函数声明)来锚定EException 的vtable,这对我有用。

发布EException 的完整头文件也可能会有所帮助。

【讨论】:

    【解决方案2】:

    检查

    -fvisibility=隐藏

    链接器设置中的选项。 如果已设置,请将其更改为

    -fvisibility=默认

    【讨论】:

      【解决方案3】:

      恕我直言,这与编译器标志无关。

      将您的异常对象声明为 extern,并且除了在您的主二进制文件中之外,不要在任何地方提供任何实现。

      这将强制链接器(动态链接器顺便说一句)使用唯一可能的实现。

      仅在外部定义上不生成类型信息。

      【讨论】:

        【解决方案4】:

        B 使用它自己的 EException 类定义 V typeinfo for EException 而 C 似乎使用的是 Unresolved (U 表示该类型在当前翻译单元中未定义,必须由加载器和动态链接器解析)。

        验证 B 仍然是一个共享库,它不是静态链接到 A 而是动态链接,防止 C 找到 A 的东西,因为我看不到 B 不会链接与 A 相同的类型。照顾你的标题^^.

        【讨论】:

        • 我理解你的第一部分。但是B 是我的主要应用程序,没有共享库。 BC 在编译时链接 A。据我了解,我应该阻止B 使用它自己的EException 类型信息,因为BC 在运行时都会自动加载A,然后应该使用As 类型信息。
        【解决方案5】:

        您可以尝试编译ABC-rdynamic(在链接阶段)

        GCC 手册页介绍了 -rdynamic:

        在支持它的目标上将标志 -export-dynamic 传递给 ELF 链接器。这指示链接器将所有符号(不仅是使用的符号)添加到动态符号表中。对于“dlopen”的某些用途或允许从程序中获取回溯,需要此选项。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-30
          • 2013-07-13
          • 2015-06-02
          • 1970-01-01
          • 2019-11-02
          • 2013-05-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多