GCC编译之如何控制共享文件导出符号

原文链接:https://blog.csdn.net/passers_b/article/details/7582535

背景

前不久在调试一个与导出符号相关的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文件


   
   
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. void func_1()
  4. {
  5. printf( “a1 :: func_1\n”);
  6. }
  7. void func_2()
  8. {
  9. printf( “a1 :: func_2\n”);
  10. }
  11. void func_3()
  12. {
  13. printf( “a1 :: func_3\n”);
  14. }

这是a1.sym文件,控制静态导出符号


   
   
  1. func_1
  2. func_3

这是a1.map文件,控制动态导出符号

global表示要导出的符号,local表示不导出的,*表示都不导出


   
   
  1. {
  2. global:
  3. func_1;
  4. func_2;
  5. local: *;
  6. };

生成共享库

gcc a1.c -shared -o liba1.so -Wl,--retain-symbols-file=a1.sym -Wl,--version-script=a1.map

   
   

查看符号表

readelf -s liba1.so
   
   

   
   
  1. Symbol table '.dynsym' contains 7 entries:
  2.    Num:    Value  Size Type    Bind   Vis      Ndx Name
  3.       0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
  4.       1: 00000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2 .1 .3 ( 2)
  5.       2: 00000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2 .0 ( 3)
  6.       3: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
  7.       4: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
  8.       5: 00000400     20 FUNC    GLOBAL DEFAULT   11 func_2
  9.       6: 000003ec     20 FUNC    GLOBAL DEFAULT   11 func_1
  10. Symbol table '.symtab' contains 27 entries:
  11.    Num:    Value  Size Type    Bind   Vis      Ndx Name
  12.       0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
  13.       1: 00000114     0 SECTION LOCAL  DEFAULT     1 
  14.       2: 00000138     0 SECTION LOCAL  DEFAULT     2 
  15.       3: 0000015c     0 SECTION LOCAL  DEFAULT     3 
  16.       4: 000001cc     0 SECTION LOCAL  DEFAULT     4 
  17.       5: 00000232     0 SECTION LOCAL  DEFAULT     5 
  18.       6: 00000240     0 SECTION LOCAL  DEFAULT     6 
  19.       7: 00000270     0 SECTION LOCAL  DEFAULT     7 
  20.       8: 000002c0     0 SECTION LOCAL  DEFAULT     8 
  21.       9: 000002d0     0 SECTION LOCAL  DEFAULT     9 
  22.     10: 00000300     0 SECTION LOCAL  DEFAULT   10 
  23.     11: 00000330     0 SECTION LOCAL  DEFAULT   11 
  24.     12: 00000468     0 SECTION LOCAL  DEFAULT   12 
  25.     13: 00000482     0 SECTION LOCAL  DEFAULT   13 
  26.     14: 000004ac     0 SECTION LOCAL  DEFAULT   14 
  27.     15: 000004d8     0 SECTION LOCAL  DEFAULT   15 
  28.     16: 00001f0c     0 SECTION LOCAL  DEFAULT   16 
  29.     17: 00001f14     0 SECTION LOCAL  DEFAULT   17 
  30.     18: 00001f1c     0 SECTION LOCAL  DEFAULT   18 
  31.     19: 00001f20     0 SECTION LOCAL  DEFAULT   19 
  32.     20: 00001fe8     0 SECTION LOCAL  DEFAULT   20 
  33.     21: 00001ff4     0 SECTION LOCAL  DEFAULT   21 
  34.     22: 00002008     0 SECTION LOCAL  DEFAULT   22 
  35.     23: 0000200c     0 SECTION LOCAL  DEFAULT   23 
  36.     24: 00000000     0 SECTION LOCAL  DEFAULT   24 
  37.     25: 00000414     20 FUNC    LOCAL  DEFAULT   11 func_3
  38.     26: 000003ec     20 FUNC    GLOBAL DEFAULT   11 func_1

显而易见:动态符号表中是函数func_2,func_1,静态符号表中是func_3,func_1

参考

1.程序员的自我修养—连接,装载与库

2.http://gcc.gnu.org/wiki/Visibility 

3.version-script

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值