C++ 剥离linux共享库
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2088409/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Stripping linux shared libraries
提问by Adam Bowen
We've recently been asked to ship a Linux version of one of our libraries, previously we've developed under Linux and shipped for Windows where deploying libraries is generally a lot easier. The problem we've hit upon is in stripping the exported symbols down to only those in the exposed interface. There are three good reasons for wanting to do this
我们最近被要求发布我们的一个库的 Linux 版本,以前我们是在 Linux 下开发并为 Windows 发布的,在 Windows 中部署库通常要容易得多。我们遇到的问题是将导出的符号剥离为仅暴露接口中的符号。想要这样做有三个很好的理由
- To protect the proprietary aspects of our technology from exposure through the exported symbols.
- To prevent users having problems with conflicting symbol names.
- To speed up the loading of the library (at least so I'm told).
- 保护我们技术的专有方面免于通过导出的符号暴露。
- 防止用户遇到符号名称冲突的问题。
- 加速库的加载(至少我被告知是这样)。
Taking a simple example then:
那么举一个简单的例子:
test.cpp
测试.cpp
#include <cmath>
float private_function(float f)
{
return std::abs(f);
}
extern "C" float public_function(float f)
{
return private_function(f);
}
compiled with (g++ 4.3.2, ld 2.18.93.20081009)
使用 (g++ 4.3.2, ld 2.18.93.20081009) 编译
g++ -shared -o libtest.so test.cpp -s
and inspecting the symbols with
并检查符号
nm -DC libtest.so
gives
给
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
obviously inadequate. So next we redeclare the public function as
显然不够。所以接下来我们将公共函数重新声明为
extern "C" float __attribute__ ((visibility ("default")))
public_function(float f)
and compile with
并编译
g++ -shared -o libtest.so test.cpp -s -fvisibility=hidden
which gives
这使
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
which is good, except that std::abs is exposed. More problematic is when we start linking in other (static) libraries outside of our control, all of the symbols we use from those libraries get exported. In addition, when we start using STL containers:
这很好,除了 std::abs 暴露了。更有问题的是,当我们开始在我们控制之外的其他(静态)库中进行链接时,我们从这些库中使用的所有符号都会被导出。此外,当我们开始使用 STL 容器时:
#include <vector>
struct private_struct
{
float f;
};
void other_private_function()
{
std::vector<private_struct> v;
}
we end up with many additional exports from the C++ library
我们最终从 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()
NB: With optimisations on you'll need to make sure the vector is actually used so the compiler doesn't optimise the unused symbols out.
注意:通过优化,您需要确保实际使用了向量,以便编译器不会优化未使用的符号。
I believe my colleague has managed to construct an ad-hoc solution involving version files and modifying the STL headers (!) that appears to work, but I would like to ask:
我相信我的同事已经设法构建了一个涉及版本文件和修改 STL 标头 (!) 的临时解决方案,但我想问:
Is there a clean way to strip all unnecessary symbols (IE ones that are not part of the exposed library functionality) from a linux shared library?I've tried quite a lot of options to both g++ and ld with little success so I'd prefer answers that are known to work rather than believed to.
有没有一种干净的方法可以从 linux 共享库中去除所有不必要的符号(IE 那些不属于公开库功能的符号)?我已经对 g++ 和 ld 尝试了很多选择,但收效甚微,所以我更喜欢已知有效而不是相信有效的答案。
In particular:
特别是:
- Symbols from (closed-source) static libraries are not exported.
- Symbols from the standard library are not exported.
- Non-public symbols from the object files are not exported.
- 不导出(闭源)静态库中的符号。
- 标准库中的符号不会导出。
- 目标文件中的非公共符号不会被导出。
Our exported interface is C.
我们导出的接口是C。
I'm aware of the other similar questions on SO:
我知道关于 SO 的其他类似问题:
- NOT sharing all classes with shared library
- How to REALLY strip a binary in MacOs
- GNU linker: alternative to --version-script to list exported symbols at the command line?
but have had little success with the answers.
但对答案收效甚微。
采纳答案by Adam Bowen
So the solution we have for now is as follows:
所以我们现在的解决方案如下:
test.cpp
测试.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:
*;
};
compiled with
编译与
g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version
gives
给
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
Which is fairly close to what we're looking for. There are a few gotchas though:
这与我们正在寻找的非常接近。但是有一些问题:
- We have to ensure we don't use the "exported" prefix (in this simple example "public", but obviously something more useful in our case) in the internal code.
- Many symbol names still end up in the string table, which appears to be down to RTTI, -fno-rtti makes them go away in my simple tests, but is a rather nuclear solution.
- 我们必须确保我们不在内部代码中使用“exported”前缀(在这个简单的例子中是“public”,但在我们的例子中显然更有用)。
- 许多符号名称仍然出现在字符串表中,这似乎归结为 RTTI,-fno-rtti 在我的简单测试中使它们消失,但它是一个相当核心的解决方案。
I'm happy to accept any better solutions anyone comes up with!
我很高兴接受任何人提出的任何更好的解决方案!
回答by joshperry
Your use of the default visibility attribute and -fvisibility=hidden should be augmented with -fvisibility-inlines-hidden.
您对默认可见性属性和 -fvisibility=hidden 的使用应使用 -fvisibility-inlines-hidden 进行扩充。
You should also forget about trying to hide stdlib exports, see this GCC bugfor why.
您还应该忘记尝试隐藏 stdlib 导出,请参阅此 GCC 错误以了解原因。
Also, if you have all of your public symbols in a specific headers you can wrap them in #pragma GCC visibility push(default)
and #pragma GCC visibility pop
instead of using attributes. Though if you are creating a cross platform library, take a look at Controlling Exported Symbols of Shared Librariesfor a technique to unify your windows DLL and Linux DSO export strategy.
此外,如果您在特定标题中拥有所有公共符号,则可以将它们包装起来,#pragma GCC visibility push(default)
而#pragma GCC visibility pop
不是使用属性。但是,如果您要创建跨平台库,请查看控制共享库的导出符号以了解统一 Windows DLL 和 Linux DSO 导出策略的技术。
回答by grrussel
Just to note that Ulrich Drepper wrote an essay regarding (all?) aspects of writing shared librariesfor Linux/Unix, which covers control of exported symbols amongst many other topics.
请注意,Ulrich Drepper 写了一篇关于为 Linux/Unix编写共享库的(所有?)方面的文章,其中涵盖了许多其他主题中导出符号的控制。
This was very handy in making it clear how to export only functions on a whitelist from a shared lib.
这对于明确如何从共享库中仅导出白名单上的函数非常方便。
回答by catwalk
If you wrap up your private part in an anonymous namespace then neither std::abs
nor private_function
can be seen in the symbol table:
如果您将私有部分包装在匿名命名空间中,则在符号表中既不可见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);
}
compiling (g++ 4.3.3):
编译(g++ 4.3.3):
g++ -shared -o libtest.so test.cpp -s
g++ -shared -o libtest.so test.cpp -s
inspecting:
检查:
# 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
回答by bmargulies
In general, across multiple Linux and Unix systems, the answer here is that there is no answer here at link time. it's fairly fundamental to how ld.so works.
一般来说,跨多个 Linux 和 Unix 系统,这里的答案是链接时这里没有答案。这是 ld.so 如何工作的基础。
This leads to some rather labor-intensive alternatives. For example, we rename STL to live in _STL instead of std
to avoid conflicts over STL, and we use namespaces high, low, and in-between to keep our symbols away from possible conflicts with other people's symbols.
这导致了一些相当劳动密集型的替代方案。例如,我们将 STL 重命名为存在于 _STL 中,而不是std
避免与 STL 发生冲突,并且我们使用命名空间 high、low 和 in-between 来防止我们的符号与其他人的符号可能发生冲突。
Here's a solution you won't love:
这是您不会喜欢的解决方案:
- Create a small .so with only your exposed API it.
- Have it open the real implementation with dlopen, and link with dlsym.
- 仅使用公开的 API 创建一个小的 .so。
- 让它用 dlopen 打开真正的实现,并用 dlsym 链接。
So long as you don't use RTLD_GLOBAL, you now have complete insulation if not particular secrecy .. -Bsymbolic might also be desirable.
只要您不使用 RTLD_GLOBAL,即使不是特别保密,您现在也可以完全隔离.. -Bsymbolic 也可能是可取的。
回答by vtomazzi
Actually, in the ELF structure there are 2 symbol tables: "symtab" and "dynsym" -> see this: Hiding symbol names in library
实际上,在 ELF 结构中有 2 个符号表:“symtab”和“dynsym”-> 请参阅: 在库中隐藏符号名称