【问题标题】:Polymorphically catching an exception in a -fno-rtti shared library on Mac OS X在 Mac OS X 上的 -fno-rtti 共享库中多态地捕获异常
【发布时间】:2011-04-07 23:36:57
【问题描述】:

我正在与f-no-rtti 建立一个共享库。在内部,这个库抛出 std:invalid_argument 并捕获 std::exception,但从未输入过 catch 子句。

以下代码重现了问题(g++ 4.2,Mac OS X 10.6):

// library.cpp: exports f(), compiled with -fno-rtti
#include <stdexcept>
#include <iostream>
extern "C" {
    void f() {
        try {
            throw std::invalid_argument("std::exception handler");
        } catch( std::exception& e) {
            std::cout << e.what() << "\n";
        } catch(...) {
            std::cout << "... handler\n";
        }
    }
}

// main.cpp: the main executable, dynamically loads the library
#include <dlfcn.h>
typedef void(*fPtr)();

int main() {
    void* handle = dlopen( "./libexception_problem.dylib", RTLD_LAZY );
    fPtr p_f = reinterpret_cast<fPtr>( dlsym( handle, "f" ) );
    p_f();
}

输出:

MacBook-Pro:teste pfranco$ # works fine with rtti
MacBook-Pro:teste pfranco$ g++ -c library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ main.cpp -o main && ./main
std::exception handler
MacBook-Pro:teste pfranco$ # breaks with -fno-rtti
MacBook-Pro:teste pfranco$ g++ -c -fno-rtti library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ -fno-rtti main.cpp -o main && ./main
... handler
MacBook-Pro:teste pfranco$ #-no_dead_strip_inits_and_terms doesn't change anything
MacBook-Pro:teste pfranco$ g++ -c -no_dead_strip_inits_and_terms -fno-rtti library.cpp && g++ -no_dead_strip_inits_and_terms -shared -o libexception_problem.dylib library.o && g++ -fno-rtti -no_dead_strip_inits_and_terms main.cpp -o main && ./main
... handler
MacBook-Pro:teste pfranco$ # linking against the shared library works, but this isn't always an option
MacBook-Pro:teste pfranco$ g++ -c -fno-rtti library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ -fno-rtti main.cpp -o main -L. -lexception_problem && ./main
std::exception handler

仅当抛出的代码位于 共享库 中,并且仅当捕获的类型是实际异常的 基类 时才会发生这种情况 - catch(std::invalid_argument&amp;) 有效好吧,std::logic_error&amp; 没有。

有趣的是,这在 Linux 上不会发生,即使运行完全相同的命令也是如此。

问题:

  1. 为什么会发生这种情况?这是一个错误、未定义的行为还是设计使然?
  2. 如果没有链接到库,我怎样才能使它工作?

非常感谢。

【问题讨论】:

  • 如果您所说的“未定义行为”是指根据 C++ 标准,那么它(充其量)是实现定义的,当您使用将编译器置于非兼容模式的编译器选项时会发生什么。我怀疑这对您有多大帮助,但是您不能禁用标准的某些部分,然后期望标准对您有所帮助;-)

标签: c++ macos gcc shared-libraries rtti


【解决方案1】:

来自 gcc 的信息页面(我的亮点)。

-fno-rtti 使用虚拟禁用生成有关每个类的信息 供 C++ 运行时类型识别功能使用的函数 (dynamic_casttypeid)。如果你不使用这些部分 语言,您可以使用此标志节省一些空间。 注意 该异常处理使用相同的信息,但它会 根据需要生成它。 dynamic_cast 运算符仍然可以 用于不需要运行时类型信息的强制转换,即 转换为 void * 或明确的基类。

RTTI 是该语言的核心部分。如果编译器允许您禁用它,那么您的工作超出了语言的规则,因此不一定会按照您的预期进行。

【讨论】:

  • 是的,我读到了,但“它会根据需要生成它”不是关键部分吗?谢谢。
  • @Pedro d'Aquino:说实话,我不知道。困难在于您现在使用的是由 gcc 的实际行为隐式定义的语言变体。我建议不要尝试禁用 RTTI。
  • 我正要发布同样的内容。它向我建议,就 GCC 而言,应该 生成所有必要的类型信息,无论是否指定了 -fno-rtti。但是我没有在 OSX 或 BSD 系统上运行 man,所以它可能会说一些不同的东西。可能是他们故意改变了行为(并记录在某处),可能是他们默默地改变了它(并且没有在任何地方记录它 - 非常顽皮),或者这可能是他们的 gcc fork 和/或他们的 C++ 运行时支持。
  • @Pedro:还梳理了 OSX 上 dll 的文档——它可能会在那里提到一些东西,因为显然它只发生在动态链接中。否则,听起来像是一个错误的候选者。这是编译器中的错误(“-fno-rtti 不应该破坏 catch”)还是只是在文档中(“-fno-rtti 应该提到它破坏了 catch”)可能取决于它是错误还是它们的真正限制实施。
【解决方案2】:

原来这是 Apple 的 gcc 上的一个错误。不过,他们最近回复了我的错误报告说它不会被修复。

【讨论】:

    猜你喜欢
    • 2014-03-11
    • 2012-11-27
    • 2021-03-27
    • 2015-11-18
    • 1970-01-01
    • 1970-01-01
    • 2012-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多