天天看點

動态連結庫so檔案中的函數名隐藏

     在我們提供給别人使用的動态連結庫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)
           

這兩種方式等價