基本介绍:

在编译时,编译器输出每个全局符号给汇编器,或者是强(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的位置!关于浮点数的每个位的由来可以参考原书或者之前的笔记.

参考资料:

深入理解计算机系统