背景前不久在调试一个与导出符号相关的bug,问题大概如此: 模块A.so在堆上构造了一个对象即 CTest *one = new CTest; , CTest在A.so定义,后来使用one->AMemFunc();,即调用一个成员函数时崩溃。原来在另一个模块B.so(比A.so先加载)中,也有一个同名的CTest定义,但是却没有一个叫AMemFunc的成员函数,因此崩溃。 那为什么A.so的CTest会被解析到B.so呢? 全局符号介入这种一个共享对象里面的全局符号被另一个共享对象的同名全局符号覆盖的现象又被称为共享对象全局符号介入(Global Symbol Interpose)。对于全局符号介入这个问题,linux下的动态连接器是这样处理的:它定义了一个规则,那就是当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。 其实,在上述的情形里,B.so的CTest是不需要导出的,GCC编译的so默认是导出所有符号,这与MS VS刚好相反。如何控制so文件的导出符号呢? 控制共享文件的导出符号常用方法有两种: 1.使用GCC 的C++ visibility 支持。 把__attribute__ ((visibility ("default"))) 放置在你希望导出的struct,class,function的声明处,然后修改你的GCC构建参数,使用-fvisibility=hidden参数编译每一个源代码文件。GCC编译源代码文件的visibility默认属性是public,所以默认所有符号都导出来了,设置为hidden后然后在需要导出的地方加__attribute__ ((visibility ("default"))),以达到控制导出符号的目的。在跨平台代码的编写中,常使用下面类似的宏定义来简化上述过程。 #if defined _WIN32 || defined __CYGWIN__ #ifdef BUILDING_DLL #ifdef __GNUC__ #define DLL_PUBLIC __attribute__ ((dllexport)) #else #define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. #endif #else #ifdef __GNUC__ #define DLL_PUBLIC __attribute__ ((dllimport)) #else #define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. #endif #endif #define DLL_LOCAL #else #if __GNUC__ >= 4 #define DLL_PUBLIC __attribute__ ((visibility ("default"))) #define DLL_LOCAL __attribute__ ((visibility ("hidden"))) #else #define DLL_PUBLIC #define DLL_LOCAL #endif #endif extern "C" DLL_PUBLIC void function(int a); class DLL_PUBLIC SomeClass { int c; DLL_LOCAL void privateMethod(); // Only for use within this DSO public: Person(int _c) : c(_c) { } static void foo(int a); }; 2.使用链接参数 --retain-symbols-file 控制静态符号表,--version-script 控制动态符号表,后面都是接含有导出符号的文件的名字。这两个参数在移植windows下的动态库很有用,windows下的DEF文件能控制导出符号,我们可以在linux下的构建脚本中解析DEF生成一个导出符号文件,然后作为retain-symbols-file,version-script的参数。示例如下: 这是a1.c文件 [cpp] view plaincopy
这是a1.sym文件,控制静态导出符号 [cpp] view plaincopy
这是a1.map文件,控制动态导出符号 global表示要导出的符号,local表示不导出的,*表示都不导出 [cpp] view plaincopy
生成共享库 [cpp] view plaincopy
查看符号表 [cpp] view plaincopy
[cpp] view plaincopy
显而易见:动态符号表中是函数func_2,func_1,静态符号表中是func_3,func_1 参考 |
|
来自: astrotycoon > 《链接加载》