動態庫(.so)隱藏函數名


一、偶遇 error: undefined reference to  xxx 問題

  嘗試封裝通用的接口到一個private.so,然后供客戶端使用,private.so編譯出來后由sample.cpp依賴調用其中封裝的接口,但是一直報error: undefined reference to  xxx的錯誤,並且檢查so、頭文件都依賴正確,c方式編譯的函數也用extern "C" 聲明。

#ifdef __cplusplus
extern "C" {
#endif

 xxx

#ifdef __cplusplus
}
#endif

  於是用如下方法查看so的符號表根本找不到定義的 xxx 函數:

readelf -s private.so
nm -D private.so

  從Android.mk定位問題發現編譯參數 CAMX_CFLAGS += -fvisibility=hidden ,此參數的作用就是將函數名隱藏,需要暴露給用戶的函數接口可以單獨通過 __attribute__((visibility ("default"))) 聲明避免被隱藏。

LOCAL_CFLAGS := $(CAMX_CFLAGS)
LOCAL_CPPFLAGS := $(CAMX_CPPFLAGS)

  因此如下聲明后,xxx函數就能被鏈接找到,編譯通過。

#ifdef __cplusplus
extern "C" { #endif __attribute__((visibility ("default"))) xxx #ifdef __cplusplus } #endif

  -fvisibility 參數具體說明如下:

man gcc:

-fvisibility=default|internal|hidden|protected
           Set the default ELF image symbol visibility to the specified option---all symbols are marked with this unless overridden within the code.  Using this feature can very
           substantially improve linking and load times of shared object libraries, produce more optimized code, provide near-perfect API export and prevent symbol clashes.  It is
           strongly recommended that you use this in any shared objects you distribute.

           Despite the nomenclature, "default" always means public; i.e., available to be linked against from outside the shared object.  "protected" and "internal" are pretty useless
           in real-world usage so the only other commonly used option is "hidden".  The default if -fvisibility isn't specified is "default", i.e., make every symbol public---this
           causes the same behavior as previous versions of GCC.

           A good explanation of the benefits offered by ensuring ELF symbols have the correct visibility is given by "How To Write Shared Libraries" by Ulrich Drepper (which can be
           found at <http://people.redhat.com/~drepper/>)---however a superior solution made possible by this option to marking things hidden when the default is public is to make the
           default hidden and mark things public.  This is the norm with DLLs on Windows and with -fvisibility=hidden and "__attribute__ ((visibility("default")))" instead of
           "__declspec(dllexport)" you get almost identical semantics with identical syntax.  This is a great boon to those working with cross-platform projects.

           For those adding visibility support to existing code, you may find #pragma GCC visibility of use.  This works by you enclosing the declarations you wish to set visibility
           for with (for example) #pragma GCC visibility push(hidden) and #pragma GCC visibility pop.  Bear in mind that symbol visibility should be viewed as part of the API interface
           contract and thus all new code should always specify visibility when it is not the default; i.e., declarations only for use within the local DSO should always be marked
           explicitly as hidden as so to avoid PLT indirection overheads---making this abundantly clear also aids readability and self-documentation of the code.  Note that due to ISO
           C++ specification requirements, "operator new" and "operator delete" must always be of default visibility.

           Be aware that headers from outside your project, in particular system headers and headers from any other library you use, may not be expecting to be compiled with visibility
           other than the default.  You may need to explicitly say #pragma GCC visibility push(default) before including any such headers.

           extern declarations are not affected by -fvisibility, so a lot of code can be recompiled with -fvisibility=hidden with no modifications.  However, this means that calls to
           "extern" functions with no explicit visibility use the PLT, so it is more effective to use "__attribute ((visibility))" and/or "#pragma GCC visibility" to tell the compiler
           which "extern" declarations should be treated as hidden.

           Note that -fvisibility does affect C++ vague linkage entities. This means that, for instance, an exception class that is be thrown between DSOs must be explicitly marked
           with default visibility so that the type_info nodes are unified between the DSOs.

           An overview of these techniques, their benefits and how to use them is at <http://gcc.gnu.org/wiki/Visibility>.

 

 

二、動態庫函數隱藏技巧

  向客戶提供動態鏈接庫(.so)時,有些關鍵的函數名不希望暴露出去,此時便可以通過gcc的-fvisibility=hidden選項對編譯生成的so進行函數符號隱藏,如:LOCAL_CPPFLAGS +=-fvisibility=hidden,執行編譯后,使用nm -D xxx.so命令或者readelf --symbols xxx.so查看函數名的確被隱藏,但此時是將所有函數名都隱藏了,那么客戶加載so時需要調用的接口函數名(xxx)也會找不到定義,導致編譯報undefined reference to xxx錯誤,所以需要暴露(導出)的函數前應該增加屬性__attribute__ ((visibility("default")))設置成可見。

例如:

__attribute__ ((visibility("default")))  
void hello(void)  
{  
}  

  實際項目開發中可以通過宏來控制,更加方便:

#ifdef DCIRDLL_EXPORTS
#ifdef PLATFORM_LINUX
#define MYDCIR_API __attribute__((visibility ("default")))  //Linux動態庫(.so)
#else
#define MYDCIR_API __declspec(dllexport)            //Windows動態庫(.dll)
#endif
#else
#define MTDCIR_API
#endif

  在頭文件中聲明即可:

 
         
MTDCIR_API const voide hello();

  

 -end-


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM