visibility用于设置动态链接库中函数的可见性,将变量或函数设置为hidden,则该符号仅在本so中可见,对外不可见,其他库无法找到此函数实现,即可见性隐藏。
Why is the new C++ visibility support so useful?
为什么说C++ visibility如此有帮助
- It very substantially improves load times of your DSO (Dynamic Shared Object). For example, a huge C++ template-based library which was tested (the TnFOX Boost.Python bindings library) now loads in eight seconds rather than over six minutes!
- It lets the optimiser produce better code. PLT indirections (when a function call or variable access must be looked up via the Global Offset Table such as in PIC code) can be completely avoided, thus substantially avoiding pipeline stalls on modern processors and thus much faster code. Furthermore when most of the symbols are bound locally, they can be safely elided (removed) completely through the entire DSO. This gives greater latitude especially to the inliner which no longer needs to keep an entry point around “just in case”.
- It reduces the size of your DSO by 5-20%. ELF’s exported symbol table format is quite a space hog, giving the complete mangled symbol name which with heavy template usage can average around 1000 bytes. C++ templates spew out a huge amount of symbols and a typical C++ library can easily surpass 30,000 symbols which is around 5-6Mb! Therefore if you cut out the 60-80% of unnecessary symbols, your DSO can be megabytes smaller!
- Much lower chance of symbol collision. The old woe of two libraries internally using the same symbol for different things is finally behind us with this patch. Hallelujah!
GNU作者给出以上四点说明:
- 显著提升动态库的加载速度
- GCC优化器生成更高效代码
- 减少动态库大小
- 减少符号表冲突(大多数人关注的问题)
How to use the new C++ visibility support
如何实现C++ visibility(可见性)
包含两种实现方式:
1.函数实现的地方添加__attribute__ ((visibility ("${hidden_value}")))修饰,并在编译时添加-fvisibility=hidden参数
其中${hidden_value}的取值可以为:hidden,default
#if defined _WIN32 || defined __CYGWIN__
#ifdef BUILDING_DLL
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllexport))
#else
#define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
#endif
#else
#ifdef __GNUC__
#define DLL_PUBLIC __attribute__ ((dllimport))
#else
#define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
#endif
#endif
#define DLL_LOCAL
#else
#if __GNUC__ >= 4
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#define DLL_LOCAL __attribute__ ((visibility ("hidden")))
#else
#define DLL_PUBLIC
#define DLL_LOCAL
#endif
#endif
extern "C" DLL_PUBLIC void function(int a);
class DLL_PUBLIC SomeClass
{
int c;
DLL_LOCAL void privateMethod(); // Only for use within this DSO
public:
Person(int _c) : c(_c) { }
static void foo(int a);
};
2.在函数声明处添加 #pragma GCC visibility
此方法更便于旧代码改造,在函数头文件中添加此宏定义即可,举例如下:
extern void foo(int);
#pragma GCC visibility push(hidden)
extern void someprivatefunct(int);
#pragma GCC visibility pop
详细介绍请参考GNU官方文档:https://gcc.gnu.org/wiki/Visibility