强符号与弱符号
Posted 车子 chezi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了强符号与弱符号相关的知识,希望对你有一定的参考价值。
简而言之,在 C 语言中,函数和初始化的全局变量(包括显示初始化为 0)是强符号,未初始化的全局变量是弱符号。
在链接器进行链接的时候,有下面的规则
- 强符号不允许多次定义(即不同的目标文件中不能有同名的强符号)
- 强弱可以共存,共存时,强覆盖弱
- 都是弱符号时,选择占用空间最大的;
我们举例子说明。
强符号不允许多次定义
针对 1,代码片段是:
// a.c
int tea = 2; // strong
// b.c
int tea = 1; // strong
printf("tea = %d\\n",tea);
gcc a.c b.c
会报错:`tea’被多次定义
强弱可以共存
针对 2,代码片段是:
// a.c
int tea = 2; // strong
// b.c
int tea; // weak
printf("tea = %d\\n",tea);
gcc a.c b.c
编译通过,运行结果是:
tea = 2
可见,链接器采用的是 a.c 文件中的强定义。
都是弱符号
针对 3,都是弱符号时,选择占用空间最大的(似乎表现出了 union 的行为)。完整代码是:
// a.c
#include <stdio.h>
void foo(void); // 在 b.c 中定义
char tea;
int main(void)
{
foo();
printf("tea = %d\\n", tea);
printf("tea address = %p\\n",&tea);
return 0;
}
// b.c
#include <stdio.h>
int tea;
void foo(void)
{
tea = 0x12ff;
printf("tea = %d\\n", tea);
printf("tea address = %p\\n",&tea);
}
编译通过,运行结果是:
tea = 4863
tea address = 0x60103c
tea = -1
tea address = 0x60103c
可见,变量 tea 的地址都一样,但是因为在不同文件的变量类型不一样,呈现出了 union 的特性。第三行的 tea 只占用一个字节,0xff 就是 -1,第一行的 tea 占用四个字节,4863 就是 0x12ff。
有细心的读者会问,如果都是弱符号,且占用空间一样大,那么链接器如何选择呢?这个我也不知道,尝试做实验,也没有探究出什么结论。
对于函数,如果都是弱符号,采用哪一个符号和链接顺序有关。实验代码如下:
// a.c
__attribute__((weak)) void foo(void)
{
printf("I am in a.c\\n");
}
int main(int argc, const char *argv[])
{
foo();
return 0;
}
// b.c
__attribute__((weak)) void foo(void)
{
printf("I am in b.c\\n");
}
如果是这样编译:
$ gcc a.c b.c
运行结果是:
I am in a.c
如果是这样编译:
$ gcc b.c a.c
运行结果是:
I am in b.c
需要特别说明的是:**链接器允许开发者使用 __attribute__((weak))
来将一个强符号转变为弱符号。**在上面的例子里面,代码第 3 行强制函数 foo 为弱符号。
为了加深印象,我们查看一下符号表。
首先去掉 __attribute__((weak))
$ gcc -c a.c b.c
$ readelf -s a.o b.o
文件:a.o
Symbol table '.symtab' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 17 FUNC GLOBAL DEFAULT 1 foo
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
11: 0000000000000011 27 FUNC GLOBAL DEFAULT 1 main
文件:b.o
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS b.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 17 FUNC GLOBAL DEFAULT 1 foo
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
可以看到第 14、31 行,Bind 这列是 GLOBAL
我们再加上 __attribute__((weak))
$ gcc -c a.c b.c
$ readelf -s a.o b.o
文件:a.o
Symbol table '.symtab' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 17 FUNC WEAK DEFAULT 1 foo
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
11: 0000000000000011 27 FUNC GLOBAL DEFAULT 1 main
文件:b.o
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS b.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 17 FUNC WEAK DEFAULT 1 foo
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
可以看到第 14、31 行,Bind 这列变成了 WEAK
说明:本文的实验环境是 gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)
以上是关于强符号与弱符号的主要内容,如果未能解决你的问题,请参考以下文章