最近我们被要求发布我们的一个库的Linux版本,以前我们已经在Linux下开发并运行Windows,部署库通常要容易得多。 我们遇到的问题是将导出的符号仅剥离到暴露的界面中。 有三个很好的理由想要做到这一点
举一个简单的例子:
TEST.CPP
#include <cmath> float private_function(float f) { return std::abs(f); } extern "C" float public_function(float f) { return private_function(f); }
(g ++ 4.3.2,ld 2.18.93.20081009)
g++ -shared -o libtest.so test.cpp -s
并用符号来检查符号
nm -DC libtest.so
给
w _Jv_RegisterClasses 0000047c T private_function(float) 000004ba W std::abs(float) 0000200c A __bss_start w __cxa_finalize w __gmon_start__ 0000200c A _edata 00002014 A _end 00000508 T _fini 00000358 T _init 0000049b T public_function
明显不足。 所以接下来我们重新宣称公共职能为
extern "C" float __attribute__ ((visibility ("default"))) public_function(float f)
和编译
g++ -shared -o libtest.so test.cpp -s -fvisibility=hidden
这使
w _Jv_RegisterClasses 0000047a W std::abs(float) 0000200c A __bss_start w __cxa_finalize w __gmon_start__ 0000200c A _edata 00002014 A _end 000004c8 T _fini 00000320 T _init 0000045b T public_function
这是好的,除了std :: abs被暴露。 更麻烦的是当我们开始在我们控制之外的其他(静态)库中进行链接时, 我们从这些库中使用的所有符号都被导出 。 另外,当我们开始使用STL容器时:
#include <vector> struct private_struct { float f; }; void other_private_function() { std::vector<private_struct> v; }
我们最终得到了来自C ++库的许多额外的输出
00000b30 W __gnu_cxx::new_allocator<private_struct>::deallocate(private_struct*, unsigned int) 00000abe W __gnu_cxx::new_allocator<private_struct>::new_allocator() 00000a90 W __gnu_cxx::new_allocator<private_struct>::~new_allocator() 00000ac4 W std::allocator<private_struct>::allocator() 00000a96 W std::allocator<private_struct>::~allocator() 00000ad8 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::_Vector_impl() 00000aaa W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::~_Vector_impl() 00000b44 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_deallocate(private_struct*, unsigned int) 00000a68 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_get_Tp_allocator() 00000b08 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_base() 00000b6e W std::_Vector_base<private_struct, std::allocator<private_struct> >::~_Vector_base() 00000b1c W std::vector<private_struct, std::allocator<private_struct> >::vector() 00000bb2 W std::vector<private_struct, std::allocator<private_struct> >::~vector()
注意:在进行优化之前,您需要确保实际上使用了vector,以便编译器不会优化未使用的符号。
我相信我的同事已经设法构build了一个涉及版本文件的临时解决scheme,并修改了似乎可行的STL头文件(!),但我想问一下:
有没有一种干净的方法来从Linux共享库中去除所有不必要的符号(不是公开的库函数的一部分)? 我已经尝试了相当多的选项,以g + +和ld很less成功,所以我更喜欢已知的工作,而不是相信的答案。
尤其是:
我们的出口界面是C.
我意识到SO上的其他类似问题:
但是答案却没有取得什么成就。
所以我们现在的解决方案如下:
TEST.CPP
#include <cmath> #include <vector> #include <typeinfo> struct private_struct { float f; }; float private_function(float f) { return std::abs(f); } void other_private_function() { std::vector<private_struct> f(1); } extern "C" void __attribute__ ((visibility ("default"))) public_function2() { other_private_function(); } extern "C" float __attribute__ ((visibility ("default"))) public_function1(float f) { return private_function(f); }
exports.version
LIBTEST { global: public*; local: *; };
编译
g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version
给
00000000 A LIBTEST w _Jv_RegisterClasses U _Unwind_Resume U std::__throw_bad_alloc() U operator delete(void*) U operator new(unsigned int) w __cxa_finalize w __gmon_start__ U __gxx_personality_v0 000005db T public_function1 00000676 T public_function2
这与我们正在寻找的东西相当接近。 但是有一些小问题:
我很高兴接受任何人提出的更好的解决方案!
默认可见性属性和-fvisibility = hidden的使用应该使用-fvisibility-inlines-hidden来扩充。
你也应该忘记试图隐藏stdlib出口,看看为什么这个GCC错误 。
另外,如果您在特定头文件中包含所有公共符号,则可以将它们包装在#pragma GCC visibility push(default)
和#pragma GCC visibility pop
而不是使用属性。 虽然如果你正在创建一个跨平台的库,看看控制共享库的导出符号技术来统一你的Windows DLL和Linux DSO的出口策略。
只要注意到Ulrich Drepper写了一篇关于为Linux / Unix 编写共享库的 (全部)方面的文章 ,其中涵盖了对许多其他主题的导出符号的控制。
这很清楚如何从共享库中导出白名单上的功能。
如果你将你的私人部分封装在一个匿名的命名空间中,那么在符号表中不能看到std::abs
和private_function
:
namespace{ #include<cmath> float private_function(float f) { return std::abs(f); } } extern "C" float public_function(float f) { return private_function(f); }
编译(g ++ 4.3.3):
g++ -shared -o libtest.so test.cpp -s
检查:
# nm -DC libtest.so w _Jv_RegisterClasses 0000200c A __bss_start w __cxa_finalize w __gmon_start__ 0000200c A _edata 00002014 A _end 000004a8 T _fini 000002f4 T _init 00000445 T public_function
一般来说,在多个Linux和Unix系统中,这里的答案是在链接时没有答案。 ld.so的工作原理是相当重要的。
这导致一些相当劳动密集型的替代品。 例如,我们将STL重命名为_STL而不是std
以避免与STL发生冲突,我们使用高位,低位和中间名称空间来避免与其他人的符号可能发生冲突。
这是一个你不会喜欢的解决方案:
只要你不使用RTLD_GLOBAL,你现在已经完全隔离,如果不是特别保密的话。-symbolic也可能是可取的。