最近工作中常遇到符号表的问题,发现它与链接选项有着很密切关系,本文将借助一个实例,具体讲解一下,涉及到的概念包括动态链接(-l)、动态加载(dlopen)、全局动态符号表等。
一.讨论APP + dlopen一个so的情况
1.APP中的全局变量symbol是否导出
【测试一:ldtest1】
【main.cpp】:
【 testlib.cpp 】:
【 Makefile 】:
如果未加-Wl,-E 选项,则dlopen打开时会抱怨symbol abc找不着,打开失败。
2.APP中的函数名是否导出
【 测试二:ldtest2 】
【 main.cpp 】:
【 testlib.cpp 】:
【 Makefile 】:
如果 编译 主程序是带有rdynamic选项,则symbol func确实会导出,具体表现就是testing函数中会调用main.cpp中的func(),那么,如何告诉编译器调用testlib.cpp中的func呢,在编译so时加上-Wl,-Bsymbolic选项,表示绑定自己的symbol。
3.如果链接上一个动态库,APP中的函数名是否导出
如果不带rdynamic选项,symbol func不会导出,所以如果testlib.cpp中没有定义func(),则上面的ldtest2会报错:
dlopen error: ./testlib.so: undefined symbol: _Z4funcv。
【 testlib.cpp 】:
参考一下LD手册:
--export-dynamic
When creating a dynamically linked executable, add all symbols to the dynamic symbol table. The dynamic symbol table is the set of symbols which are visible from dynamic objects at run time. If you do not use this option, the dynamic symbol table will normally contain only those symbols which are referenced by some dynamic object mentioned in the link.
如果你没有用这个选项,那么动态符号表里面将只会包含动态链接对象引用过的符号,如果没有其他的动态链接对象,则什么也不包含。
上面的例子中,编译主程序时并未链接任何动态库,所以是后一种情况,下面我们作一个试验,人为链上一个库:
【测试:ldtest22】
【 main.cpp 】:
【 testlib.cpp 】:
【 sharelib.h 】:
#include <stdio.h>
void share();
【 sharelib.cpp 】:
【 Makefile 】:
此处编译主程序并未带有rdynamic选项,但symbol func依然会导出,主要原因就在于它动态链上一个共享库libsharelib.so,而在该库中引用了func()这个符号,使得其自动被导出。
二.讨论APP link一个so的情况
1.APP中的全局变量symbol是否导出
【测试三:ldtest3】
【 main.cpp 】:
【 testlib.h 】:
void testing();
【 testlib.cpp 】:
【 Makefile 】:
运行时设置LD_LIBRARY_PATH为当前路径即可
我们并未设置任何特殊编译选项,可见采用-ltestlib的方式,APP中的符号表对so是可见的。
2.APP中的函数名是否导出
【 测试四:ldtest4 】
【 main.cpp 】:
【 testlib.h 】:
void testing();
【 testlib.cpp 】:
【 Makefile 】:
symbol func确实会导出,具体表现就是testing函数中会调用main.cpp中的func(),那么,如何告诉编译器调用testlib.cpp中的func呢,在编译so时加上-Wl,-Bsymbolic选项,表示绑定自己的symbol。
三.LD选项说明
在前面测试中,我们谈到了诸如Wl,-E和-Wl,-Bsymbolic之类的选项,这里作一个统一说明:
Wl 表示后面的选项将被作为参数传给连接器LD
-E 等价于--export-dynamic,表示将执行程序中的符号表全部导出
-Bsymbolic 当创建动态库时,如果某一个符号在全局符号表中已经存在,强制采用本地的;而缺省情况下,本地定义会被覆盖。这一点,对于保证多个动态库之间互不影响是很重要的,起码,它保证了本地定义的优先性,不会在程序员不知情的时候,偷偷用外部定义换掉。此外,如果想保证某一个动态库不会影响其他的动态库,则可以采用后面谈到的version-script选项;
--version-script 通过version脚本来控制动态库中的符号是global,还是local的;
我们修改一下ldtest2:
【 Makefile 】:
【 version 】:
VERS {
global:
testing;
local: *;
};
我们同时采用了Bsymbolic和--version-script选项,在version script中指明,只有testing这一个符号将会被加入到动态符号表中,这样,就能够保证dlsym可以找到testing,并且load该动态库不会增添新的无用符号,因为前面local设的是通配符"*"。
四.几个相关命令
nm -C -D libtest.so
把libtest.so中的所有动态符号用demagle的形式打印出来
objdump -T libtest.so
同上,不过更详细一些
其中,-D选项特别有用,针对version-script选项,只打印global标志的symbol。
一.讨论APP + dlopen一个so的情况
1.APP中的全局变量symbol是否导出
【测试一:ldtest1】
【main.cpp】:
【 testlib.cpp 】:
【 Makefile 】:
如果未加-Wl,-E 选项,则dlopen打开时会抱怨symbol abc找不着,打开失败。
2.APP中的函数名是否导出
【 测试二:ldtest2 】
【 main.cpp 】:
【 testlib.cpp 】:
【 Makefile 】:
如果 编译 主程序是带有rdynamic选项,则symbol func确实会导出,具体表现就是testing函数中会调用main.cpp中的func(),那么,如何告诉编译器调用testlib.cpp中的func呢,在编译so时加上-Wl,-Bsymbolic选项,表示绑定自己的symbol。
3.如果链接上一个动态库,APP中的函数名是否导出
如果不带rdynamic选项,symbol func不会导出,所以如果testlib.cpp中没有定义func(),则上面的ldtest2会报错:
dlopen error: ./testlib.so: undefined symbol: _Z4funcv。
【 testlib.cpp 】:
参考一下LD手册:
--export-dynamic
When creating a dynamically linked executable, add all symbols to the dynamic symbol table. The dynamic symbol table is the set of symbols which are visible from dynamic objects at run time. If you do not use this option, the dynamic symbol table will normally contain only those symbols which are referenced by some dynamic object mentioned in the link.
如果你没有用这个选项,那么动态符号表里面将只会包含动态链接对象引用过的符号,如果没有其他的动态链接对象,则什么也不包含。
上面的例子中,编译主程序时并未链接任何动态库,所以是后一种情况,下面我们作一个试验,人为链上一个库:
【测试:ldtest22】
【 main.cpp 】:
【 testlib.cpp 】:
【 sharelib.h 】:
#include <stdio.h>
void share();
【 sharelib.cpp 】:
【 Makefile 】:
此处编译主程序并未带有rdynamic选项,但symbol func依然会导出,主要原因就在于它动态链上一个共享库libsharelib.so,而在该库中引用了func()这个符号,使得其自动被导出。
二.讨论APP link一个so的情况
1.APP中的全局变量symbol是否导出
【测试三:ldtest3】
【 main.cpp 】:
【 testlib.h 】:
void testing();
【 testlib.cpp 】:
【 Makefile 】:
运行时设置LD_LIBRARY_PATH为当前路径即可
我们并未设置任何特殊编译选项,可见采用-ltestlib的方式,APP中的符号表对so是可见的。
2.APP中的函数名是否导出
【 测试四:ldtest4 】
【 main.cpp 】:
【 testlib.h 】:
void testing();
【 testlib.cpp 】:
【 Makefile 】:
symbol func确实会导出,具体表现就是testing函数中会调用main.cpp中的func(),那么,如何告诉编译器调用testlib.cpp中的func呢,在编译so时加上-Wl,-Bsymbolic选项,表示绑定自己的symbol。
三.LD选项说明
在前面测试中,我们谈到了诸如Wl,-E和-Wl,-Bsymbolic之类的选项,这里作一个统一说明:
Wl 表示后面的选项将被作为参数传给连接器LD
-E 等价于--export-dynamic,表示将执行程序中的符号表全部导出
-Bsymbolic 当创建动态库时,如果某一个符号在全局符号表中已经存在,强制采用本地的;而缺省情况下,本地定义会被覆盖。这一点,对于保证多个动态库之间互不影响是很重要的,起码,它保证了本地定义的优先性,不会在程序员不知情的时候,偷偷用外部定义换掉。此外,如果想保证某一个动态库不会影响其他的动态库,则可以采用后面谈到的version-script选项;
--version-script 通过version脚本来控制动态库中的符号是global,还是local的;
我们修改一下ldtest2:
【 Makefile 】:
【 version 】:
VERS {
global:
testing;
local: *;
};
我们同时采用了Bsymbolic和--version-script选项,在version script中指明,只有testing这一个符号将会被加入到动态符号表中,这样,就能够保证dlsym可以找到testing,并且load该动态库不会增添新的无用符号,因为前面local设的是通配符"*"。
四.几个相关命令
nm -C -D libtest.so
把libtest.so中的所有动态符号用demagle的形式打印出来
objdump -T libtest.so
同上,不过更详细一些
其中,-D选项特别有用,针对version-script选项,只打印global标志的symbol。