1. 程式人生 > >編譯.so動態庫隱藏函式名

編譯.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"))),例如,

  1. __attribute__ ((visibility("default")))  
  2. void hello(void)  
  3. {  
  4. }  

    這樣就把函式hello匯出來了,而其他沒有新增該屬性的,就被-fvisibility=hidden給隱藏了,到此我們的目標就實現了。

    當然,為了方便使用,可以把該選項用巨集定義,寫函式的時候就可以使用,比如:

  1. #ifdef WIN32
  2. #   ifdef EXPORT
  3. ...  
  4. #   else
  5. ....  
  6. #   endif
  7. #   define DLL_LOCAL 
  8. #else
  9. #   ifdef __GNU__
  10. #       if (GCC_SUPPORTS_VISABLE == 1) /*defined by configure*/
  11. #           ifdef EXPORT
  12. #               define DLL_API __attribute__ ((visibility("default")))
  13. #           else
  14. #               define DLL_API __attribute__ ((visibility("default")))
  15. #           endif
  16. #           define DLL_LOCAL __attribute__ ((visibility("hidden")))
  17. #       else
  18. #           define DLL_API
  19. #           define DLL_LOCAL
  20. #       endif
  21. #   endif
  22. #endif