在我們提供給别人使用的動态連結庫so檔案時,其内部實作函數的名稱,特别是一些關鍵名稱我們是不希望别人見到然後反向的,這時候一般有兩種處理方式:一是把程式中關鍵詞修改了再編譯,比較蠢笨;二是通過編譯的方式将字元隐藏,gcc編譯器提供了這個選項,即在編譯選項中加入-fvisibility=hidden選項。
比如ndk裡這樣操作:LOCAL_CPPFLAGS +=-fvisibility=hidden。執行編譯後,使用nm -D xxx.so指令或者readelf --symbols xxx.so即可檢視so檔案中符号清單,此時所有符号已經隐藏了,好像似乎目的達到了,但是引用此so檔案時發現根本運作不起來,那麼問題出哪兒了?
其實,根據動态連結庫調用原理可知,程式在顯示或隐示調用so檔案時,跟靜态庫一樣是需要使用确定名稱的函數的,而執行-fvisibility=hidden編譯後,所有函數名稱都被隐藏了,這時候程式當然運作不起來了。
那麼正确的思路應該是暴露出要被調用的函數名稱,而隐藏不被外部使用的其他符号即可,具體操作為:
在需要暴露(導出)的函數前增加屬性__attribute__ ((visibility("default"))),例如,
__attribute__ ((visibility("default")))
void hello(void)
{
}
這樣就把函數hello導出來了,而其他沒有添加該屬性的,就被-fvisibility=hidden給隐藏了,到此我們的目标就實作了。
當然,為了友善使用,可以把該選項用宏定義,寫函數的時候就可以使用,比如:
#ifdef WIN32
# ifdef EXPORT
...
# else
....
# endif
# define DLL_LOCAL
#else
# ifdef __GNU__
# if (GCC_SUPPORTS_VISABLE == 1) /*defined by configure*/
# ifdef EXPORT
# define DLL_API __attribute__ ((visibility("default")))
# else
# define DLL_API __attribute__ ((visibility("default")))
# endif
# define DLL_LOCAL __attribute__ ((visibility("hidden")))
# else
# define DLL_API
# define DLL_LOCAL
# endif
# endif
#endif
這樣可以在函數前添加相應的屬性,DLL_API或DLL_LOCAL
另外,當使用cmake時,可以在CMakeLists.txt檔案中添加:
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_C_VISIBILITY_PRESET hidden)
還有一種方式是添加compile options選項,像這樣:
target_compile_options(${LIBNAME} PRIVATE -fvisibility=hidden)
這兩種方式等價