我们已经在各种平台(Linux,Windows,Mac OS X,32位和64位)上构建了一个大型开源software多年而没有遇到麻烦。然而,最近Mac OS X版本(64位)停止正常工作并开始随机崩溃。它或多或少与我们的构建机器上从10.7到10.8.2的Mac OS X更新一致(但编译器工具链没有改变,它仍然是llvm-gcc 4.2.1)。
我们的应用程序由几个动态(共享)库和许多使用它们的可执行文件组成。由于各种原因,其中一个共享库会覆盖new
和delete
运算符。在Mac OS X(和Linux)上,默认情况下会导出所有符号,包括我们重载的new
和delete
运算符。 Mac OS X上的崩溃似乎与一个内存子系统(不是我们的)分配的内存相关,然后通过我们自己的(和不兼容的)delete
实现释放。
最安静的解决方案似乎是阻止重载的操作员对共享库的用户可见。这可以通过两种方式实现:使用__attribute__((visibility("hidden")))
标记运算符,或使用-unexported_symbols_list
链接器命令行选项来防止导出某些符号。遗憾的是,第一个解决方案不起作用:gcc发出警告,说明运算符的声明方式不同(在<new>
中),因此属性将被忽略。根据我在不同地方的阅读,第二个解决方案似乎是解决这个问题的正确方法。但由于某种原因,我们无法使其发挥作用。
当链接共享库时,我们将-Wl,-unexported_symbols_list unexported_symbols_list.txt
选项传递给g ++,而g ++应该传递给ld。 unexported_symbols_list.txt
文件包含以下符号列表:
__ZdaPv
__ZdaPvRKSt9nothrow_t
__ZdlPv
__ZdlPvRKSt9nothrow_t
__ZdlPvS_
__Znam
__ZnamRKSt9nothrow_t
__Znwm
__ZnwmPv
__ZnwmRKSt9nothrow_t
这些是我们覆盖并希望隐藏的new
和delete
的所有变体。我们通过nm libappleseed.dylib
找到这些符号,然后使用c++filt
取消符号名称。
这是CMake生成的命令行,用于链接libappeseed.dylib
:
/usr/bin/g++ -g -Werror -dynamiclib -Wl,-headerpad_max_install_names -framework Cocoa -lcurl -Werror -Wl,-unexported_symbols_list -Wl,unexported_symbols_list.txt -o ../mac-gcc4/appleseed/libappleseed.dylib [...]
不幸的是,尽管我们付出了很多努力,但符号仍然存在(如nm所示)。
知道我们做错了什么吗? 我们可以尝试另一种方法吗?
更新2012年12月19日:
Apple的技术说明中详细介绍了我们的问题和所谓的解决方案:http://developer.apple.com/library/mac/#technotes/tn2185/_index.html(“覆盖新/删除”部分)。
相关源代码的指针:
operator new
和operator delete
覆盖:allocator.cpp 使用nm
构建libappleseed.dylib并运行-fvisibility=hidden
后strip -x libappleseed.dylib
输出的片段:
...
00000000002a41b0 T __ZdaPv
00000000002a41f0 T __ZdaPvRKSt9nothrow_t
00000000002a4190 T __ZdlPv
00000000002a41d0 T __ZdlPvRKSt9nothrow_t
00000000002a4060 T __Znam
00000000002a4130 T __ZnamRKSt9nothrow_t
00000000002a3ff0 T __Znwm
00000000002a40d0 T __ZnwmRKSt9nothrow_t
...
答案 0 :(得分:6)
您应该使用-fvisibility=hidden
构建,然后只导出您想要的内容。请在这里阅读:
http://gcc.gnu.org/wiki/Visibility
它还解释了-fvisibility-inlines-hidden
。许多大型库(例如Qt)都使用它。好处是相当可观的。
答案 1 :(得分:0)
您可以查看符号映射/版本控制(--version-script ld选项)