源地址:http://blog.guorongfei.com/2018/04/11/symbol-visibility/
<h1 id="什麼是符號隱藏"><a href="#什麼是符號隱藏" class="headerlink" title="什麼是符號隱藏"></a>什麼是符號隱藏</h1><p>在同一個文件中,如果有一些函數我們並不想要讓外部訪問,我們通常會添加 static
修飾符,把它設置爲內部鏈接屬性。
1 | static void foo(); |
但是通常庫不太可能是單文件組成,這些文件中有些是做接口給外部使用,有些則單純的只是庫的內部實現。對於外部使用者來說,內部實現的這些符號沒有實際的作用,理論上我們完全可以像對待文件內部符號一樣把它們統統隱藏掉。但是在語言層面我們並沒有相關的語法用於表達這個概念(Java中的包訪問權限和C#中的internal類似這個概念)。不同的編譯器提供了不同的方式來完成這件事情,這篇文章總結了一種跨平臺的處理方式。
符號隱藏的作用
一般來說做符號隱藏有以下三個作用:
- 安全,去掉不必要的符號,可以增加逆向破解的難度。
- 壓縮空間,符號實際上是放在 dll 中的,去掉這些符號可以縮減 dll 的大小
- 性能,符號隱藏掉意味着它不會參與到動態鏈接過程,編譯器可以有更大的優化空間,可能會產生更好的性能。
如何做符號隱藏
符號隱藏可以採用下面幾個步驟(文中假定你使用MSVC或者4.0以上版本的GCC,低版本GCC不支持符號隱藏):
1. 動態庫
符號能否隱藏在於它在動態鏈接的過程中是否需要用到。靜態庫實際上是目標文件的集合,它並沒有完成鏈接過程。所以符號隱藏通常都是基於動態庫的,靜態庫的符號隱藏沒有很好的跨平臺方式,如果想要嘗試,可以參考下面這些鏈接。
2. 默認隱藏所有的符號
MSVC和GCC在動態庫符號的默認屬性上面有較大的差別,MSVC默認所有的符號都是隱藏的,而GCC默認所有的符號都是可見的。雖然我不太喜歡臃腫的MSVC,但是我不得不承認在這一點上,我更傾向於MSVC的選擇。
如果你使用MSVC
編譯器,這個步驟你可以什麼都不做,如果你使用GCC,你需要給你的編譯器加上-fvisibility=hidden
選項,你也可以加上-fvisibility-inlines-hidden
把內聯函數隱藏掉。如果你使用Autotool
,你可以通過設置LD_CXXFLAG
來控制默認隱藏,如果你使用CMake
,可以通過set(CMAKE_CXX_VISIBILITY_PRESET hidden)
來完成這一點。
3. 把你想要公開的接口的屬性設置爲外部可見
在MSVC中,我們通過在編譯的時候設置__declspec(dllimport)
和使用的時候設置__declspec(dllexport)
來完成這一點,在GCC中則簡單一些統一設置成__attribute__ ((visibility ("default")))
即可。
輔助宏
上面這些步驟比較繁瑣,通常會定義宏來協助處理這一部分內容,下面是來自GCC WIKI
的一個模板
1 | // Generic helper definitions for shared library support |
我們想要導出一個符號的時候使用FOX_API
:
1 | class FOX_API Fox {}; |
在編譯動態庫的時候,設置FOX_DLL
和FOX_DLL_EXPORTS
這兩個宏。在使用動態庫的是,定義FOX_DLL
這個宏。
</div>