标签:off cti 优先 his 参与 stat 静态库 ima wrapper
最近在适配newlib代码时遇到一个关于弱声明的问题, 研究了一下才发现自己对weak属性与链接时符号选择理解有误.
在一个库(liba.a)中重新定义了一个weak属性的函数(func_a), 在同一库中调用该函数时链接了弱声明的函数版本, 但是当通过另一个库(libb.a)的库函数(func_b)调用(func_a)时最终链接的是强声明的函数版本.
精简后的case如下:
[00:51:42] hansy@hansy:~/testcase$ cat weak.c
int __attribute__((weak)) test() {
return 0xFF;
}
[00:51:52] hansy@hansy:~/testcase$ cat strong.c
int test() {
return 0;
}
[00:51:56] hansy@hansy:~/testcase$ cat wrapper.c
int test();
int wrapper() {
return test();
}
[00:52:01] hansy@hansy:~/testcase$ cat main.c
extern int test();
extern int wrapper();
int main() {
return wrapper() + test();
}
一共4个文件, 其中weak.c与strong.c分别定义了test函数的两个版本. wrapper.c与main.c都调用了test函数.
精简后的编译命令如下:
[00:57:49] hansy@hansy:~/testcase$ cat build.sh
gcc weak.c -c -o weak.o
gcc strong.c -c -o strong.o
if [ -e test.a ]; then rm test.a; fi
ar rcs test.a weak.o strong.o
ranlib test.a
gcc wrapper.c -c -o wrapper.o
if [ -e wrapper.a ]; then rm wrapper.a; fi
ar rcs wrapper.a wrapper.o
ranlib wrapper.a
gcc main.c -c -o main.o
gcc main.o test.a wrapper.a -static -o indirect.out
objdump -D indirect.out > indirect.s
echo "indirect output"
./indirect.out && echo $?
gcc main.c strong.o weak.o wrapper.a -static -o direct.out
objdump -D direct.out > direct.s
echo "direct output"
./direct.out && echo $?
indirect.out是通过将weak.c与strong.c打包成test.a后链接生成的程序, direct.out是直接链接weak.o与strong.o生成的程序.
执行shell结果如下:
[01:01:43] hansy@hansy:~/testcase$ sh build.sh
indirect output
direct output
0
链接库的版本返回值是非0(即使用了weak.c的定义), 而直接链接的版本返回值是0(即使用了strong.c的定义).
一开始怀疑是我对weak声明的理解有误, 所以从gcc官网查找了一下说明.
The weak attribute causes a declaration of an external symbol to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions that can be overridden in user code, though it can also be used with non-function declarations. The overriding symbol must have the same type as the weak symbol. In addition, if it designates a variable it must also have the same size and alignment as the weak symbol. Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.
从官网的说明来看强定义默认覆盖弱定义, 那么为什么通过库链接的版本使用的是弱声明呢?
尝试通过nm
命令查看各个编译组件的符号定义:
test.a:
weak.o:
0000000000000000 W test
strong.o:
0000000000000000 T test
wrapper.a:
U _GLOBAL_OFFSET_TABLE_
U test
0000000000000000 T wrapper
main.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U test
U wrapper
可以看到test.a包含了两部分, 其中weak.o包含了test的weak定义, strong.o包含了test的strong定义, 而wrapper.a与main.o都包含了undefined的test引用.
即在编译到静态库后test.a仍包含了test的两种实现, 因此问题出在链接时符号选择. 在编译时添加-Wl,-trace-symbol=test
查看符号选择:
// indirect
main.o: reference to test
test.a(weak.o): definition of test
wrapper.a(wrapper.o): reference to test
//direct
/tmp/ccqKb5fZ.o: reference to test
strong.o: definition of test
wrapper.a(wrapper.o): reference to test
即编译indirect时选择链接test.a中的weak.o, 而编译direct时恰恰相反.
main.o: reference to test
test.a(strong.o): definition of test
wrapper.a(wrapper.o): reference to test
indirect output
0
/tmp/ccJCNbJC.o: reference to test
strong.o: definition of test
wrapper.a(wrapper.o): reference to test
direct output
0
替换顺序后strong.o排在weak.o前, 修改后indirect结果与direct结果保持一致(使用strong.o的定义).
main.o: reference to test
weak.o: definition of test
strong.o: definition of test
wrapper.a(wrapper.o): reference to test
direct output
0
替换顺序后尽管weak.o先被查找到, 但由于strong.o中是强定义, 结果仍然使用strong.o的定义.
wrapper.o:
U _GLOBAL_OFFSET_TABLE_
w test
0000000000000000 T wrapper
main.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
w test
U wrapper
main.o: reference to test
wrapper.a(wrapper.o): reference to test
indirect output
Segmentation fault (core dumped)
/tmp/ccSpseMq.o: reference to test
strong.o: definition of test
wrapper.a(wrapper.o): reference to test
direct output
0
仍然使用最初的脚本, 注意到修改声明后main.o中对test的引用由U(undefined)变为w(weak).
direct仍然使用strong.o的定义, 而indirect.o却报段错误(链接时没有找到test定义), 这是由于main中使用的是弱声明, 默认不会去库中查找符号定义.
更多的实验就不展开了, 这里写下实验结论:
标签:off cti 优先 his 参与 stat 静态库 ima wrapper
原文地址:https://www.cnblogs.com/Five100Miles/p/13070064.html