C++可見性控制支持 是GCC編譯器從4.0版本提供的特性
一歧蒋、背景
In ordinary C, if you want to limit the visibility of a function or variable to the current file, you apply the static keyword to it. In a shared library containing many files, though, if you want a symbol to be available in several files inside the library, but not available outside the library, hiding that symbol is more difficult. Most linkers provide convenient ways to hide or show all symbols in a module, but if you want to be more selective, it takes a lot more work.
引用自Controlling Symbol Visibility.
在C語言中阐虚,可以使用static關鍵字奥秆,用于限制函數(shù)或變量的可見性避矢,但只作用于當前文件。而共享庫(或動態(tài)庫)可能包含多個文件尺上,如果需要將某個符號在庫內的多文件間可見卑吭,隱藏符號會變得比較棘手富稻。許多鏈接器提供了將一個模塊的所有符號進行隱藏或導出的方法,但這卻失去了對符號的可見性控制宣蔚。
二向抢、目的
Put simply, it hides most of the ELF symbols which would have previously (and unnecessarily) been public.
主要用于隱藏某些ELF符號,而這些符號只有在之前某時段需要甚至完全不需要Be pulibc.
ELF說明:
ELF(Executable Linkable Format)是UNIX類操作系統(tǒng)中普遍采用的目標文件格式文件包含了代碼胚委、數(shù)據(jù)挟鸠,而有些更包含了符號表、調試信息篷扩、行號信息兄猩、字符串等茉盏。
目標形式:
目標文件有三種類型:
- 可重定位文件(Relocatable File) 包含適合于與其他目標文件鏈接來創(chuàng)建可執(zhí)行文件或者共享目標文件的代碼和數(shù)據(jù)鉴未。 (Linux的*.o 文件 Windows的 *.obj文件)
- 可執(zhí)行文件(Executable File) 包含適合于執(zhí)行的一個程序,此文件規(guī)定了 exec() 如何創(chuàng)建一個程序的進程映像鸠姨。(比如/bin/bash文件铜秆;Windows的*.exe)
- 共享目標文件(Shared Object File) 包含可在兩種上下文中鏈接的代碼和數(shù)據(jù)。首先鏈接編輯器可以將它和其它可重定位文件和共享目標文件一起處理讶迁,生成另外一個目標文件连茧。其次,動態(tài)鏈接器(Dynamic Linker)可能將它與某個可執(zhí)行文件以及其它共享目標一起組合巍糯,創(chuàng)建進程映像啸驯。
目標文件全部是程序的二進制表示,目的是直接在某種處理器上直接執(zhí)行(Linux的.so祟峦,如/lib/ glibc-2.5.so罚斗;Windows的DLL)
三、優(yōu)點
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!
可減少動態(tài)共享庫的載入時間
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".
可完全避免過程鏈接表(PLT)的間接性訪問過程:在PIC代碼中的函數(shù)調用或者變量的訪問需要通過全局偏移表用于物理位置的查找宅楞。因此可大幅減少對于處理器的管線停滯针姿,并且,大部分符號是本地綁定的厌衙,DSO可進行安全的移除操作距淫。
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!
減少動態(tài)庫大小。由于ELF導入的符號表格式中的完全獨立的標識使用了重量級的模板婶希,導致符號表格式所占空間甚大榕暇。基于此喻杈,減少不必要的全局符號將大大縮減動態(tài)共享庫彤枢,并且調用者的解析成本隨之而降;與此同時奕塑,還將大幅度降低符號沖突的幾率堂污。哈利路亞!龄砰!
四盟猖、使用
GCC4.0提供了-fvisibility編譯選項讨衣,其可選取值為default或者hidden:前者表示,編譯對象中對于沒有顯式地設置為隱藏的符號式镐,其屬性均表示對外可見反镇;后者則將隱藏沒有進行顯式設置的符號。對于編譯沒有顯式指定的情況娘汞,則GCC編譯前將使用-fvisibility=default歹茶,表示庫的符號均對外可見。
針對于編譯選項你弦,GCC使用優(yōu)先級更高的attribute機制用于設置符號的可見性屬性惊豺,其設置對象包括變量、函數(shù)禽作、模板尸昧、C++類等。所以旷偿,一般在共享庫烹俗,尤其是大型的動態(tài)庫中,編譯使用-fvisibility=default用于進行全局符號的隱藏設置萍程,將需要向外展示的符號則用attribute((visibility("default"))進行覆蓋性的設置幢妄。
代碼示例:
int a(int n) {return n;}
__attribute__((visibility("hidden"))) int b(int n) {return n;}
__attribute__((visibility("default"))) int c(int n) {return n;}
class X
{
public:
virtual ~X();
};
class __attribute__((visibility("hidden"))) Y
{
public:
virtual ~Y();
};
class __attribute__((visibility("default"))) Z
{
public:
virtual ~Z();
};
X::~X() { }
Y::~Y() { }
Z::~Z() { }
五、批量設置--pragmas
Another way to mark symbols as default or hidden is with a new pragma in GCC 4.0. The GCC visibility pragma has the advantage of being able to mark a block of functions quickly, without the need to apply the visibility attribute to each one.
GCC還提供了批量設置的方法茫负,即使用pragmas關鍵字剂娄,進行快速的塊符號標記寸谜。
void f() { }
# pragma GCC visibility push(default)
void g() { }
void h() { }
# pragma GCC visibility pop
六翰撑、符號可見性設置的場景
If your library exports a C++ interface, the symbols associated with that interface must be visible.
如果導出某c++接口犁嗅,與其關聯(lián)的符號必須設置為可見。
If your symbol uses runtime type identification (RTTI) information, exceptions, or dynamic casts for an object that is defined in another library, your symbol must be visible if it expects to handle requests initiated by the other library. For example, if you define a catch handler for a type in the C++ standard library, and you want to catch exceptions of that type thrown by the C++ standard library, you must make sure that your typeinfo object is visible.
如果某一符號使用了定義于其他庫的對象信息缔赠,RTTI衍锚,異常,動態(tài)轉換等嗤堰,那么在希望處理由其他庫發(fā)起的請求的時候戴质,該符號必須設置為visible。
If you expect the address of an inline function used in different code modules to be the same for each module, the function must be exported from each code module.
如果希望不同模塊都使用的某一內嵌函數(shù)其地址保持一致踢匣,則該函數(shù)必須設置為visibile告匠。
If your inline function contains a static object and you expect there to be only one copy of that object, your symbol for that static object must be visible.
如果某一內嵌函數(shù)包含了一個靜態(tài)對象,且希望該對象為單例离唬,則該對象必須設置為visible后专。
七、引申
關于GCC的attribute機制: