前言:
在分析C语言全局未初始化变量时,发现在目标文件中全局未初始化变量并不是直接放在bss段中。
再后来发现在两个.c文件中定义同名的全局变量,链接时居然没有发生符号重定义错误。才知道C语言弱定义的概念。这在C++中是绝对不行的。
后来搜索到一篇博文说:
“全局未初始化变量没有被放到任何段,而是作为未定义的COMMON符号。这个和不同语言、编译器实现有关,有的编译器放到.bss 段,有的仅仅是预留一个COMMON符号,在链接的时候再在.bss段分配预留空间。编译单元内部可见的静态变量,比如在上述中加上static的 static int global_static_var则确实被放到了.bss,是因为这个仅仅是编译单元内部可见。”
由于最近对gcc反汇编的兴趣,不禁有一种用反汇编工具分析这个问题的冲动。本文看起来很长,其实是图片占据了大量篇幅。所以阅读量很小。懂这个问题的话,两三句话就能说清楚。但是为了几下我分析的过程,还是决定用这个篇幅来叙述一下。
零.几个名词解释:
弱定义:全局未初始化变量的定义或声明或者局部静态未初始化变量定义。(这是我理解的,实际上我没找到关于弱定义权威的说明,难道是个人发明?)
.data:全局初始化数据段。
.bss:全局未初始化数据段。
.rodata:只读数据段。
一.分析的对象
- 1.1全局未初始化变量
- 1.2全局未初始化变量和另外一个文件有同名变量定义
- 2.1静态全局未初始化变量
- 2.2静态全局未初始化变量+有本地同名的初始变量定义
- 3.静态局部未初始化变量
- 上面的情况遇到const
二.分析方法
1.查看C源代码对应的汇编代码;
2.用反汇编译工具objdump和ELF文件分析工具readelf依次分析目标文件和可执行文件,主要查看符号表中变量的位置。
Ps:虽然我们用到了反汇编,但是只是查看变量的符号,还是比较简单的。汇编代码我们也只看变量定义部分,所以还是很简单。
三.环境和工具
环境为Ubuntu12.04 x64 (具体的环境信息见附录A)
工具为gcc,objdump,readelf
用gcc -S产生汇编代码(.s后缀名)。
用gcc -c产生目标文件(.o后缀名)。
用gcc产生最终的可执行文件(人为的将可执行文件设置为.out后缀名)。
用objdump -t和readelf -s命令查看目标文件和可执行文件的符号表。
四.具体分析过程
1.1全局未初始化变量
源代码文件:1.1.c
汇编代码文件:1.1.s
目标文件:1.1.o
可执行文件:1.1.out
/* 1.1.c */
#include <stdio.h>
int a;
int main()
{
printf("%d\n",a);
return 0;
}
运行结果截图:
1.1.out运行结果.png
可以看出全局未初始化变量a的值是0。
再看汇编代码
;1.1.s中和变量a相关的部分
.file "1.1.c"
.comm a,4,4
从汇编代码看出a只是一个common符号。