基本介绍:
在编译时,编译器输出每个全局符号给汇编器,或者是强(strong),或者是弱(weak),而汇编器把这个信息隐含地编码在可重定位目标文件的符号表里。函数和以函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。我们也可以通过GCC的"__attribute__((weak))"来定义任何一个强符号为弱符号。注意,强符号和弱符号都是针对定义来说的,不是针对符号的引用。比如我们有下面这段程序:
extern int ext;
int weak;
int strong = 1;
__attribute__((weak)) weak2 = 2;
int main()
{
return 0;
}
weak,weak2是弱符号。
strong,main是强符号,而ext既非强符号也非弱符号。因为它是一个外部变量的引用。
针对强弱符号的概念,链接器就会按如下规则处理与选择被多次定义的全局符号:
规则1:不允许强符号被多次定义(即不同的目标文件中不能有同名的强符号);如果有多个强符号定义,则链接器报符号重复定义错误。
规则2:如果一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,那么选择强符号。
规则3:如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一个。比如目标文件A定义全局变量global为int型,占4个字节;目标文件B定义global为double型,占8个字节,那么目标文件A和B链接后,符号global占8个字节(尽量不要使用多个不同类型的弱符号,否则容易导致很难发现的程序错误)。
示例:
1.
//bar.c
int main()
{
return 0;
}
//foo.c
int main()
{
return 0;
}
链接器将生成一条错误信息:
$ gcc foo.c bar.c
/tmp/ccgVHUSN.o: In function `main':
bar.c:(.text+0x0): multiple definition of `main'
/tmp/ccCZ9xqQ.o:foo.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
因为强符号main被定义了多次(规则1)。
2.修改一下:
//foo.c
int x = 2;
int main()
{
return 0;
}
//bar.c
int x = 1;
void f()
{
}
$ gcc foo.c bar.c
/tmp/cczbmyHW.o:(.data+0x0): multiple definition of `x'
/tmp/ccsAcjCn.o:(.data+0x0): first defined here
collect2: ld returned 1 exit status
因为强符号x被定义了多次(规则1)。
3.如果在一个模块里x未被初始化,那么链接器将安静的选择定义在另一个模块中的强符号(规则2)。
//bar.c
int x;
void f()
{
x = 100;
}
//foo.c
int x = 2;
void f();
int main()
{
f();
printf("x = %d\n",x);
return 0;
}
运行结果:x = 100。在运行时,函数f将x的值由2改为100。要注意这种情况。
4.如果x有两个弱定义,也会发生相同的事情。(规则3)
//foo.c
int x;
void f();
int main()
{
x = 2;
f();
printf("x = %d\n",x);
return 0;
}
//bar.c
int x;
void f()
{
x = 100;
}
5.当两个重复的符号类型不同时:
//bar.c
double x;
void f()
{
x = -0.0;
}
//foo.c
int x = 1;
int y = 2;
void f();
int main()
{
f();
printf("x = %x,y = %x\n",x,y);
return 0;
}
$ gcc bar.c foo.c
foo.c: In function ‘main’:
foo.c:9: warning: incompatible implicit declaration of built-in function ‘printf’
/usr/bin/ld: Warning: alignment 4 of symbol `x' in /tmp/ccVLU2rl.o is smaller than 8 in /tmp/ccUQq1xX.o
/usr/bin/ld: Warning: size of symbol `x' changed from 8 in /tmp/ccUQq1xX.o to 4 in /tmp/ccVLU2rl.o
$ ./a.out
x = 0,y = 80000000
x=-0.0将用负数的双精度浮点表示覆盖存储器中x和y的位置!关于浮点数的每个位的由来可以参考原书或者之前的笔记.
参考资料:
深入理解计算机系统