在Mac OS X上的共享库中隐藏符号

时间:2021-02-02 12:26:28

We've been building a large open source software on a variety of platforms (Linux, Windows, Mac OS X, 32-bit and 64-bit) for several years without troubles. Lately however, the Mac OS X build (64-bit) stopped working correctly and started to crash randomly. It more or less coincided with an update of Mac OS X on our build machine from 10.7 to 10.8.2 (but the compiler toolchain didn't change, it's still llvm-gcc 4.2.1).

几年来,我们一直在各种平台(Linux、Windows、Mac OS X、32位和64位)上构建大型开源软件,没有遇到任何问题。然而,最近,Mac OS X构建(64位)停止了正常工作,并开始随机崩溃。它或多或少地与我们的构建机器上Mac OS X从10.7更新到10.8.2相吻合(但是编译器工具链没有改变,它仍然是llvm-gcc 4.2.1)。

Our application is made of a couple of dynamic (shared) libraries and many executables using them. One of the shared library overrides the new and delete operators for a variety of reasons. On Mac OS X (and Linux), all symbols are exported by default, including our overloaded new and delete operators. The crashes on Mac OS X seem related to some memory being allocated with one memory subsystem (not ours) then freed through our own (and incompatible) delete implementation.

我们的应用程序由几个动态(共享)库和许多使用它们的可执行文件组成。由于各种原因,其中一个共享库覆盖了新的和删除操作符。在Mac OS X(和Linux)上,所有符号都默认导出,包括重载的new和delete操作符。Mac OS X上的崩溃似乎与一些内存分配给一个内存子系统(不是我们的),然后通过我们自己的(不兼容的)删除实现释放有关。

The sanest solution seems to be preventing the overloaded operators from being visible to the users of the shared library. This can be accomplished in two ways: marking the operators with __attribute__((visibility("hidden"))), or using the -unexported_symbols_list linker command line option to prevent some symbols from being exported. The first solution unfortunately doesn't work: gcc emits warnings saying that the operators have been declared differently (in <new>) and thus the attributes will be ignored. From my readings in various places, the second solution seems to be the right one to this problem. However for some reason we can't make it work.

最明智的解决方案似乎是防止重载操作符对共享库的用户可见。这可以通过两种方式实现:使用__attribute__(可视性(“隐藏”)标记操作符,或者使用-unexported_symbols_list链接器命令行选项来防止某些符号被导出。第一个解决方案不幸地不起作用:gcc发出警告说操作符被声明为不同的(在 中),因此属性将被忽略。根据我在不同地方的阅读资料,第二个解决方案似乎是这个问题的正确答案。但是由于某种原因,我们不能让它工作。

When linking the shared library, we're passing the -Wl,-unexported_symbols_list unexported_symbols_list.txt option to g++, which in turns should be passed to ld. The unexported_symbols_list.txt file contains the following list of symbols:

在链接共享库时,我们传递-Wl、-unexported_symbols_list unexported_symbols_list。g++的txt选项,应该依次传递给ld. unexported_symbols_list。txt文件包含以下符号列表:

__ZdaPv
__ZdaPvRKSt9nothrow_t
__ZdlPv
__ZdlPvRKSt9nothrow_t
__ZdlPvS_
__Znam
__ZnamRKSt9nothrow_t
__Znwm
__ZnwmPv
__ZnwmRKSt9nothrow_t

These are all the variations of new and delete that we override and want to be hidden. We found these symbols by doing nm libappleseed.dylib then unmangling the symbol names using c++filt.

这些都是我们重写并希望隐藏的新和删除的所有变体。我们通过做nm libappleseed找到了这些符号。然后,dylib使用c++filt解压缩符号名称。

Here's the command line generated by CMake to link libappeseed.dylib:

下面是CMake生成的链接libappese. 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 [...]

Unfortunately, despite all our efforts it appears that the symbols remain (as nm shows).

不幸的是,尽管我们做了很多努力,这些符号仍然存在(如nm所示)。

Any idea what we are doing wrong? Is there another approach that we could try?

知道我们做错了什么吗?我们还有其他方法可以尝试吗?


UPDATE Dec. 19, 2012:

2012年12月19日更新:

Our problem and the supposed solution are well covered in this technical note from Apple: http://developer.apple.com/library/mac/#technotes/tn2185/_index.html (section "Overriding new/delete").

我们的问题和假定的解决方案在苹果的技术说明中得到了很好的介绍:http://developer.apple.com/library/mac/#technotes/tn2185/_index.html(“重写新/删除”一节)。

Pointers to relevant source code:

指向相关源代码的指针:

  • operator new and operator delete overrides: allocator.cpp
  • 操作符新建和操作符删除重写:allocator.cpp
  • Macros to control symbol visibility in the shared library: dllvisibility.h
  • 用于控制共享库中符号可见性的宏:dllvisibility.h

Fragment of nm's output after building libappleseed.dylib with -fvisibility=hidden and running strip -x libappleseed.dylib:

构建libappleseed后nm输出的片段。使用-fvisibility=隐藏和running strip -x libapplesed .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
...

2 个解决方案

#1


5  

You should be building with -fvisibility=hidden and then export only what you want. Have a read here:

您应该使用-fvisibility=隐藏,然后只导出您想要的。在这里有一个阅读:

http://gcc.gnu.org/wiki/Visibility

http://gcc.gnu.org/wiki/Visibility

It also explains -fvisibility-inlines-hidden. Many large libraries (Qt, for example) make use of this. The benefits are quite substantial.

这也解释了-fvisibility-inlines-hidden。许多大型的库(例如Qt)都在使用它。好处是相当可观的。

#2


0  

You may take a look at symbol maps/versioning (--version-script ld option)

您可以查看符号映射/版本控制(——版本-脚本ld选项)

http://accu.org/index.php/journals/1372

http://accu.org/index.php/journals/1372

#1


5  

You should be building with -fvisibility=hidden and then export only what you want. Have a read here:

您应该使用-fvisibility=隐藏,然后只导出您想要的。在这里有一个阅读:

http://gcc.gnu.org/wiki/Visibility

http://gcc.gnu.org/wiki/Visibility

It also explains -fvisibility-inlines-hidden. Many large libraries (Qt, for example) make use of this. The benefits are quite substantial.

这也解释了-fvisibility-inlines-hidden。许多大型的库(例如Qt)都在使用它。好处是相当可观的。

#2


0  

You may take a look at symbol maps/versioning (--version-script ld option)

您可以查看符号映射/版本控制(——版本-脚本ld选项)

http://accu.org/index.php/journals/1372

http://accu.org/index.php/journals/1372