C++ 使用-fno-rtti在OS X上引发和捕获异常时出现问题

C++ 使用-fno-rtti在OS X上引发和捕获异常时出现问题,c++,macos,exception,shared-libraries,rtti,C++,Macos,Exception,Shared Libraries,Rtti,这个问题有点类似于,但公认的答案并没有真正提出解决方案或解决办法 在我们的项目中,我们有一个动态库和主可执行文件。动态库使用-fno rtti编译,而可执行文件使用rtti。当从动态库抛出异常(例如,std::bad_alloc)并在exe中捕获时,就会出现问题 (在您大喊“异常需要RTTI,所以您必须打开它!”之前,请注意,无论-frti或-fno-RTTI设置如何,都会生成异常所需的RTTI。这实际上记录在-fno-RTTI标志说明中。OS X上的问题是,它不是以相同的方式生成的) 经过调查

这个问题有点类似于,但公认的答案并没有真正提出解决方案或解决办法

在我们的项目中,我们有一个动态库和主可执行文件。动态库使用
-fno rtti
编译,而可执行文件使用rtti。当从动态库抛出异常(例如,
std::bad_alloc
)并在exe中捕获时,就会出现问题

(在您大喊“异常需要RTTI,所以您必须打开它!”之前,请注意,无论
-frti
-fno-RTTI
设置如何,都会生成异常所需的RTTI。这实际上记录在
-fno-RTTI
标志说明中。OS X上的问题是,它不是以相同的方式生成的)

经过调查,发现以下情况:

  • 在动态库(
    -fno rtti
    )中,存在异常的rtti结构的本地副本;特别是
    \uuuztist9bad\u alloc
    符号(
    typeinfo for std::bad\u alloc
  • exe(
    -frti
    )从
    libstdc++.6.dylib
    导入typeinfo符号,并且没有本地副本
由于异常处理代码依赖于比较typeinfo指针来确定异常匹配,因此匹配失败,只有
捕获(…)
成功

到目前为止,我看到了以下选项:

1) 使用
-frti
编译所有内容,或者至少编译引发和捕获异常的文件。这是可行的,但我不喜欢为所有内容生成RTTI的想法,即使我们不使用它;而且,处理异常的文件列表很容易过时

2) 链接动态库时,以某种方式使链接器从对象文件中丢弃弱异常定义,并使用
libstdc++.6.dylib
中的弱异常定义。到目前为止我还没有成功

3)

我做了一个小测试来说明这个问题

--- throw.cpp ---
#include <iostream>

#if defined(__GNUC__)
#define EXPORT __attribute__((visibility("default")))
#else
#define EXPORT __declspec(dllexport)
#endif

EXPORT void dothrow ()
{
   std::cout << "before throw" << std::endl;
   throw std::bad_alloc();
}

--- main.cpp ---
#include <stdio.h>
#include <iostream>

#if defined(__GNUC__)
#define IMPORT extern
#else
#define IMPORT __declspec(dllimport)
#endif

IMPORT void dothrow ();

int main (void) {
 try {
   std::cout << "trying lib->main exception" << std::endl;
   dothrow ();
 }
 catch ( const std::bad_alloc& )
 {
   std::cout << "caught bad_alloc in main - good." << std::endl;
 }
 catch (...)
 {
   std::cout << "caught ... in main - bad!" << std::endl;
 }
}

--- makefile ---
# for main exe
CFLAGS_RTTI=-m32 -frtti -fvisibility=hidden -fvisibility-inlines-hidden -shared-libgcc -funwind-tables
# for dylib
CFLAGS_NORTTI=-m32 -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden -shared-libgcc
# for linking; some switches which don't help
CLFLAGS=-Wl,-why_live,-warn_commons,-weak_reference_mismatches,error,-commons,error

all: test

test: libThrow.dylib main.o
    g++ $(CFLAGS_RTTI) -o test main.o -lthrow -L./ $(CLFLAGS)

main.o: main.cpp
    g++ $(CFLAGS_RTTI) -c -o main.o main.cpp

throw.o: throw.cpp
    g++ $(CFLAGS_NORTTI) -c -o throw.o throw.cpp

libThrow.dylib: throw.o
    g++ $(CFLAGS_NORTTI) -dynamiclib -o libThrow.dylib throw.o

clean:
    rm test main.o throw.o
所涉文件的符号:

$ nm -m throw.o | grep bad_alloc
000001be (__TEXT,__textcoal_nt) weak private external __ZNSt9bad_allocC1Ev
000001be (__TEXT,__textcoal_nt) weak private external __ZNSt9bad_allocC1Ev
00000300 (__TEXT,__eh_frame) weak private external __ZNSt9bad_allocC1Ev.eh
         (undefined) external __ZNSt9bad_allocD1Ev
00000290 (__DATA,__const_coal) weak external __ZTISt9bad_alloc
000002a4 (__TEXT,__const_coal) weak external __ZTSSt9bad_alloc
         (undefined) external __ZTVSt9bad_alloc

$ nm -m libThrow.dylib | grep bad_alloc
00000ce6 (__TEXT,__text) non-external __ZNSt9bad_allocC1Ev
         (undefined) external __ZNSt9bad_allocD1Ev (from libstdc++)
00001050 (__DATA,__const) weak external __ZTISt9bad_alloc
00000e05 (__TEXT,__const) weak external __ZTSSt9bad_alloc
         (undefined) external __ZTVSt9bad_alloc (from libstdc++)

$ nm -m main.o | grep bad_alloc
         (undefined) external __ZTISt9bad_alloc

$ nm -m test | grep bad_alloc
         (undefined) external __ZTISt9bad_alloc (from libstdc++)
注意:Linux和Windows上类似的编译选项可以正常工作。我可以从共享对象/dll抛出异常并在主exe中捕获它们,即使它们是使用不同的
-frti
/
-fno rtti
选项编译的


EDIT:下面是我如何解决
坏的\u alloc
的具体情况:

#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
 #define throw_nomem std::__throw_bad_alloc
#else
 #define throw_nomem throw std::bad_alloc
#endif

EXPORT void dothrow ()
{
   std::cout << "before throw" << std::endl;
   throw_nomem();
}
#如果已定义(uu GLIBCXX_uuuuuuuuuu| |已定义(_LIBCPP_版本)
#定义throw\u nomem std::\u throw\u bad\u alloc
#否则
#定义throw\u nomem throw std::bad\u alloc
#恩迪夫
导出void dothrow()
{

std::cout于2014年3月4日开始编辑 我认为Clang++编译器有更好的机会获得所需的异常处理。我发现此堆栈溢出帖子:。该帖子提供了有用的脚本行,用于修改
~/.bashrc
以更改Snow Leopard上的系统默认编译器设置以及如何使用LLVM GCC。对于Clang,请在
~/.bashrc
中添加:

# Set Clang as the default compiler for the system
export CC=clang
export CFLAGS=-Qunused-arguments
export CPPFLAGS=-Qunused-arguments
<> > BR/>如果C++ +/Cult>符号链接不存在,直接调用CLAN+++或按需要添加C++链接(例如

) )。最好通过运行以下命令来检查
/usr/bin
中的所有符号链接:

ls -l `which lynx` | more

在我的Mavericks命令行工具安装中,
c++
指向
clang++
cc
指向
clang
。编译器版本显示:

$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-   include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix
$clang++ --version
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

编译器版本的
clang++
说明:

$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-   include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix
$clang++ --version
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

请注意,
g++
包含目录路径设置为
/usr/include/c++/4.2.1
,可能不是解决问题所需的包含路径

MacPorts:希望是任何OS X版本的答案
要获得任何OS X版本的任何Clang++编译器版本,我能找到的最佳解决方案是使用名为的开源工具。中有大量文档。该应用程序名为
端口
,可以从OS X安装包安装,也可以获得源代码并在本地编译。以下内容来自正在将MacPorts暂停到Snow Leopard上。其他OS X版本应该类似。在获得MacPorts for Snow Leopard后,运行“端口搜索”命令以观察所有可用的与clang相关的不同端口。例如,它如下所示:

$port search clang 

雪豹10.6.8的部分搜索结果列表如下:

clang-2.9 @2.9_13 (lang)
    C, C++, Objective C and Objective C++ compiler

clang-3.0 @3.0_12 (lang)
    C, C++, Objective C and Objective C++ compiler

clang-3.1 @3.1_7 (lang)
    C, C++, Objective C and Objective C++ compiler

clang-3.2 @3.2_2 (lang)
    C, C++, Objective C and Objective C++ compiler

clang-3.3 @3.3_2 (lang)
    C, C++, Objective C and Objective C++ compiler

clang-3.4 @3.4 (lang)
    C, C++, Objective C and Objective C++ compiler

clang-3.5 @3.5-r202097 (lang)
    C, C++, Objective C and Objective C++ compiler

clang_select @0.1 (sysutils)
    common files for selecting default clang version

然后我成功地安装了clang-3.3,安装方式为:
sudo port install clang-3.3
。完成后,通过键入
port select--list clang
查看可用版本。然后运行

sudo port select --set clang mp-clang-3.3
或者类似。当我执行
clang++--version
时,它会显示(如预期的那样):


当执行
clang--version
命令时(关闭并重新启动终端后)相同:

许多OS X版本(如Leopard、Snow Leopard、Lion、Mountain Lion、Mavericks等)都有MacPorts安装包。我的搜索没有追溯到Leopard之前。如果使用比Leopard更早的OS X,请彻底查看MacPorts网站


如果我想知道在哪里可以找到Xcode 4.2的详细信息(或者曾经能够获得它),我找到了这篇关于为雪豹获得Xcode 4.2的帖子。然后是另外两篇:和。在尝试了几个链接,看看4.2 Xcode是否仍然可以为雪豹使用后,没有什么乐趣


完全支持C++11很可能需要MacPorts libc++安装。要安装最新版本,请执行
sudo port install libcxx
/usr/lib
的内容将被当前的C++11库覆盖(根据MacPorts票据42385:

如果仍然缺少libc++,请尝试以下方法:。然后使用以下方法:

$ export TRIPLE=-apple-
$ export MACOSX_DEPLOYMENT_TARGET=10.6
$ ./buildit

在OSXLion、MountainLion和Mavericks上,他们最近都在Apple开发者网站.T上下载了独立的命令行工具
clang version 3.3 (tags/RELEASE_33/final)
Target: x86_64-apple-darwin10.8.0
Thread model: posix
$ export TRIPLE=-apple-
$ export MACOSX_DEPLOYMENT_TARGET=10.6
$ ./buildit
// Thrower.cc
void DoThrow() {
  throw std::bad_alloc;
}

// LibraryNoRTTI.cc
void f() {
  DoThrow();
}

// main.cc
int main() {
  try {
    f();
  }
  catch(std::bad_alloc&) {}
  return 0;
}
// Thrower.cc
void Rethrow(const std::exception& e) {
  throw e;
}

// LibraryNoRTTI.cc
namespace {

void internal_stuff() {
  throw std::logical_error("something goes wrong!");
}

}  // namespace

// You even may explicitly specify the thrown exceptions in declaration:
void f() throw(std::logical_error) {
  try {
    internal_stuff();
  }
  catch(std::exception& e) {
    Rethrow(std::logical_error(std::string("Internal error: ") + e.what());
  }
}