Chinaunix首页 | 论坛 | 博客
  • 博客访问: 83914
  • 博文数量: 18
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 118
  • 用 户 组: 普通用户
  • 注册时间: 2017-03-25 16:28
个人简介

***

文章分类

全部博文(18)

文章存档

2017年(18)

我的朋友

分类: C/C++

2017-03-31 06:52:31

转载:http://blog.csdn.net/dj0379/article/details/17011105

背景

前不久在调试一个与导出符号相关的bug,问题大概如此:

模块A.so在堆上构造了一个对象即 CTest *one = new CTest; , CTestA.so定义,后来使用one->AMemFunc(),即调用一个成员函数时崩溃。原来在另一个模块B.so(A.so先加载)中,也有一个同名的CTest定义,但是却没有一个叫AMemFunc的成员函数,因此崩溃。

那为什么A.soCTest会被解析到B.so呢?


全局符号介入

这种一个共享对象里面的全局符号被另一个共享对象的同名全局符号覆盖的现象又被称为共享对象全局符号介入(Global Symbol Interpose)。对于全局符号介入这个问题,下的动态连接器是这样处理的:它定义了一个规则,那就是当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。

其实,在上述的情形里,B.soCTest是不需要导出的,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. }

点击(此处)折叠或打开

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

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

点击(此处)折叠或打开

  1. func_1
  2. func_3

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

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

点击(此处)折叠或打开
  1. {
  2. global:
  3. func_1;
  4. func_2;
  5. local: *;
  6. };
生成共享库
点击(此处)折叠或打开
  1. gcc a1.c -shared -o liba1.so -Wl,--retain-symbols-file=a1.sym -Wl,--version-script=a1.map
查看符号表

点击(此处)折叠或打开

  1. 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

点击(此处)折叠或打开

  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




阅读(2032) | 评论(0) | 转发(0) |
0

上一篇:bios中断大全

下一篇:GCC复杂好用的宏

给主人留下些什么吧!~~