在同客户做对接时遇到了符号冲突的问题。
我司为客户提供sdk包供开发使用,就是几个so文件,在so文件中我司封装了tinyxml2这个库,客户再做开发时也时候用了tinyxml2这个库,但是所使用的版本是不同的,造成了再运行时,会崩溃。应该是在程序运行时,先加载了他们的tinyxml库,然后我们的sdk在寻找tinyxml库的相关符号是找到的是他们库的符号。
SIGSEGV : 段错误
16 stack frames.
./test.out() [0x804e314]
[0x618400]
/lib/libc.so.6() [0x7a07b0]
./test.out(_ZN8tinyxml28DynArrayIPKcLi10EE14EnsureCapacityEi+0x4f) [0x8054c87]
./test.out(_ZN8tinyxml28DynArrayIPKcLi10EE4PushES2_+0x1e) [0x80548a4]
./test.out() [0x8052fac]
./test.out(_ZN8tinyxml210XMLPrinter10VisitEnterERKNS_10XMLElementEPKNS_12XMLAttributeE+0x21) [0x80536f1]
./test.out(_ZNK8tinyxml210XMLElement6AcceptEPNS_10XMLVisitorE+0x29) [0x8051fcf]
./libnmchelper.so(+0x61336) [0xc8a336]
./libnmchelper.so(+0x421df) [0xc6b1df]
./libnmchelper.so(+0x38fd6) [0xc61fd6]
./libnmchelper.so(+0x37a91) [0xc60a91]
./libnmchelper.so(nmc_login+0x56) [0xc7d85c]
./test.out() [0x804ee09]
/lib/libc.so.6(__libc_start_main+0xe6) [0x680ce6]
./test.out() [0x804e0e1]
*** glibc detected *** ./test.out: munmap_chunk(): invalid pointer: 0xbf9252c4 ***
======= Backtrace: =========
/lib/libc.so.6[0x6dae31]
/usr/lib/libstdc++.so.6(_ZdlPv+0x22)[0x3a26552]
./test.out(_ZN8tinyxml211XMLDocumentD0Ev+0x1c)[0x805229c]
./libnmchelper.so(_ZN8tinyxml211XMLDocument5ParseEv+0x85)[0xbdc987]
./libnmchelper.so(+0x626e4)[0xbdc6e4]
./libnmchelper.so(+0x3da59)[0xbb7a59]
./libnmchelper.so(+0x33fc9)[0xbadfc9]
./libnmchelper.so(+0x32715)[0xbac715]
./libnmchelper.so(nmc_login+0x65)[0xbcbc46]
./test.out[0x804ee09]
/lib/libc.so.6(__libc_start_main+0xe6)[0x680ce6]
./test.out[0x804e0e1]
经过一段探索,先解决方法如下:
首先,我们要求so文件优先使用自己的库文件内的符号,因此在编译是使用-Wl,-Bsymbolic参数,这是个链接参数,会被传递给连接器ld使用,告诉so,优先使用库内符号。
译:
-Bsymbolic
When creating a shared library, bind references to global symbols to the definition within the shared library, if any. Normally, it is possible for a program linked against a shared library to override the definition within the shared library. This option is only meaningful on ELF platforms which support shared libraries.
当创建一个动态库时,如果由对全局符号的引用,则把引用绑定到动态库内的定义上。通常,程序在链接到一个动态库时由可能会覆盖这个动态库的符号定义。这个选项只在支持ELF格式动态库的平台有用。
其次,我们还要考虑我们自身库的符号先得到加载的话,不会去覆盖其他库或者程序的符号,因此这里需要将不必导出的符号进行隐藏,只导出外部需要使用的符号。
这里我们在编译时使用-fvisibility=hidden参数来隐藏符号,但是只这样的话会把库内的所有的符号都隐藏了,包括调用者需要的函数,于是我们在需要导出的的函数和变量前加上
__attribute__ ((visibility ("default")))属性,这样就可以使用导出的函数了。
为了方便,宏定义如下定义
#ifdef WIN32 //windows platform #ifdef NMC_USER_MODULE_EXPORTS #define NMC_API __declspec(dllexport) #else #define NMC_API __declspec(dllimport) #endif #ifndef NMC_CALL_TYPE #define NMC_CALL_TYPE __stdcall #endif #else //linux platform #ifndef NMC_API #define NMC_API __attribute__ ((visibility ("default"))) #endif #ifndef NMC_CALL_TYPE #define NMC_CALL_TYPE #endif #endif