C语言全局未初始化数据段分析
前言:
在分析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符号。
对目标文件1.1.o进行反汇编
objdump -t 1.1.o
objdump -t 1.1.o.png
从上图中看出变量a在目标文件中也只是一个COMMON符号,不属于任何段。
再用readelf分析一下
readelf -s 1.1.o
readelf -s 1.1.o.png
可以看出a是全局(GLOBAL)的COMMON符号。
对最终的可执行文件进行反汇编
objdump -t 1.1.out
objdump -t 1.1.out.png
可以看出变量a在可执行目标文件中属于bss段
readelf -s 1.1.out
readelf -s 1.1.out.png
可以看出a是全局的(GLOBAL)
1.1观察结果小结:全局未初始化变量a在编译和汇编阶段都不属于任何段,只是一个COMMON符号,在链接时链接器ye没发现其他同名的变量定义或声明,将其放入了bss段中。
1.2全局未初始化变量+另外一个文件有同名变量定义
分析的对象:1.1.c 、1.2.c;1.1.s、1.2.s;1.1.o、1.2.o;1.out
1.1.c前面列出过,这里不再赘叙。
/*1.2.c*/
int a = 100;
/*代码很简单,只是定义了一个全局变量*/
运行结果结果截图
1.out运行结果.png
1.1.s前面贴出过,这里不再赘叙。
;1.2.s中变量a相关的部分
.file "1.2.c"
.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 100
可以看出在1.2.s中变量a属于全局初始化数据段(.data),初始化值为100.
对目标文件进行反汇编分析
1.1.o在前面分析过,这里不再赘叙。只叙述1.2.o的分析情况
Objdump -t 1.2.o
obdump -t 1.2.o.png
readelf -s 1.2.o
readelf -s 1.2.o.png
从上面两张图得出的结果和从1.2.s汇编代码中得出的结论一直,在1.2.o中a属于.data段。
对可执行文件进行反汇编分析
objdump -t 1.out
objdump -t 1.out.png
可以看出a在.data段
readelf -s 1.out
readelf -s 1.out.png
可以看出a是全局的。
1.2观察结果小结:全局未初始化变量a在1.1.o不属于任何段,但由于在1.2.o中有一个同名的全局初始化变量定义,所以在最终的执行文件中a也变成了全局初始化变量,属于bss段了。
2.1.静态的全局未初始化变量
分析对象:2.1.c、2.1.s、2.1.o、2.1.out
由于静态变量不会和其他文件中的变量有什么关系,所以分析单个文件程序就够了。
/*2.1.c*/
#include <stdio.h> static int a; int main()
{
printf("%d\n",a);
return 0;
}
运行结果:
2.1.out运行结果.png
2.1.c对应的汇编代码:
;2.1.s中和a相关的部分
.file "2.1.c"
.local a
.comm a,4,4
可以看出a是本地COMMON符号。
对目标文件2.1.o进行反汇编
objdump -t 2.1.o和readelf -s 2.1.o结果如下:
objdump -t readelf -s 2.1.o.png
从上图分析结果可以看出静态全局未初始化变量a作用域为本地(LOCAL),并在bss段中。
2.1情况观察结果小结:因为静态变量链接时不会和其他文件中的变量产生联系,所以静态全局未初始化变量a在汇编阶段就能确定属于bss段。
2.2静态全局未初始化变量+有本地同名的初始变量定义
从2.1的分析结果可以看出,静态全局未初始化变量a在汇编阶段就能确定是在bss段中了。假如在变量a的同一个文件中还有一个a的同名初始化变量定义呢?按照弱定义的规则,这种情况a在汇编阶段就能确定在.data段中。下面验证一下:
分析对象:2.2.c、2.2.s、2.2.o、2.2.out
/* 2.2.c */
#include <stdio.h> static int a;
static int a = 100; int main()
{
printf("%d\n",a);
return 0;
}
运行结果截图:
2.2. out执行结果.png
从运行结果看a的初始化值为100,说明static int a退化为声明,而static int a = 100才是变量a的定义。
对应的汇编代码
.file "2.2.c"
.data
.align 4
.type a, @object
.size a, 4
a:
.long 100
从汇编代码看出,在编译阶段就能确定a是在全局初始化数据段(.data)了。下面对目标文件和可执行文件的分析只是验证一下。
对目标文件2.2.o进行反汇编分析
objdump -t 2.2.o
Readelf -s 2.2.o
对目标文件2.2.o进行分析.png
可以看出a属于.data段。但作用域为本文件。
对最终的可执行文件进行反汇编分析
objdump -t 2.2.out
objdump -t 2.1.out.png
readelf -s 2.2.out
readelf -s 2.1.out .png
和对目标文件分析的结果一致。
2.2情况分析小结:上面的分析结果验证了在编译阶段就确定了a属于全局初始化数据段(.data)。
3.静态局部未初始化变量
静态局部未初始化变量是否和静态全局未初始化变量一样呢?在编译、汇编、链接阶段属于.data段、还是.bss段呢?
分析对象:3.c、3.s、3.o、3.out
3.c
#include <stdio.h>
int main()
{
static int a; printf("%d\n",a); return 0;
}
运行结果:
3.out运行结果.png
对应的汇编代码:
;3.s
.file "3.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl a.1804(%rip), %edx
movl $.LC0, %eax
movl %edx, %esi
movq %rax, %rdi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.local a.1804 ;a在这里
.comm a.1804,4,4
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
可以看出在汇编代码中a被改名为a.1084(局部变在编译阶段都会被改名的),而且是本地COMMON符号。
对目标文件3.o进行反汇编分析:
3.o的反汇编分析.png
可以看出a在汇编阶段就被放到bss段中了。
对最终的可执行文件进行反汇编分析
objdump -t 3.out
objdump -t 3.out.png
Readelf -s 3.out
readelf -s 3.out.png
结果和目标文件中的分析情况一致。
3情况分析小结:静态局部未出世化变量属于bss段,这在编译阶段就能确定。
如果是:静态局部未初始化变量+本地有静态同名初始化变量 的情况呢?
会不会和“2.2静态全局未初始化变量+有本地同名的初始变量定义”情况一样:static int a退化为声明,而static int a = 100才是定义?
验证的结果是在同一函数内部,同一作用域里同时出现static int a和static int a = 100根本就通不过编译,出现重声明错误。看来只有静态全局变量可以这么定义和声明啊。
4.上面所有情况遇到const:
分析过程和上面相似,但是据观察:静态全局未初始化变量,在编译阶段就已确定属于.rodata段;const静态局部未初始化变量规律和上面3种情况相同,属于.bss段。(从汇编代码中看出)。
最终结论:
弱定义变量有可能属于bss段,也有可能属于.data段。如果是静态变量,在编译阶段就能确定;如果是全局非静态变量,在链接阶段确定。至于到底属于哪个段:如果在同一作用域内遇到其他属于.data的同名定义,则属于.data段;如果没遇到,就属于.bss段了。const变量比较特殊,const静态全局未初始化变量属于.rodata段。
附录A:具体的环境信息
$ cat /etc/issue
Ubuntu 12.04.2 LTS \n \l
$ uname -a
Linux chao-AN408US 3.2.0-29-generic #46-Ubuntu SMP Fri Jul 27 17:03:23 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
$ cat /pro/version
Linux version 3.2.0-29-generic (buildd@allspice) (gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #46-Ubuntu SMP Fri Jul 27 17:03:23 UTC 2012
$ gcc --version
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright © 2011 Free Software Foundation, Inc.
本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保;
包括没有适销性和某一专用目的下的适用性担保。
附录B:参考资料
链接:Linux程序调试--查看二进制文件
http://www.cnblogs.com/androidme/archive/2013/04/08/3008615.html
Linux汇编教程
http://blog.csdn.net/yalizhi123/article/details/5752268
Linux进程地址空间详解(转载)
http://blog.sina.com.cn/s/blog_7321be1101013aua.html
书:《汇编语言程序设计》(美) Richard Blum 著
C语言全局未初始化数据段分析的更多相关文章
- C语言内存分布之数据段
不管我们以后是自己写代码还是读别人的代码,都应该想想这个变量默认存储的位置.在我们以后的嵌入式开发中,技巧性的代码越来越多的时候,我们可能把某一些代码放在一段.我们可以通过修改变量或者代码默认放置的段 ...
- C语言的未初始化的数组的值为什么是随机的
突然想起来前几天同学问我为什么没有初始化的数组的值是随机的,发现这个困惑自己也是存在的,所以自己总结的心得. 1. 首先,并不是所有未初始化的数组的值都是随机的.对于没有初始化的数组,分两种情况: ( ...
- (转-经典-数据段)C++回顾之static用法总结、对象的存储,作用域与生存期
转自:https://blog.csdn.net/ab198604/article/details/19158697相关知识补充:https://www.cnblogs.com/rednodel/p/ ...
- 静态库动态库的编译、链接, binutils工具集, 代码段\数据段\bss段解释
#1. 如何使用静态库 制作静态库 (1)gcc *.c -c -I../include得到o文件 (2) ar rcs libMyTest.a *.o 将所有.o文件打包为静态库,r将文件插入静态库 ...
- C语言中的未初始化变量的值
C语言中未初始化的变量的值是0么 全局变量 .静态变量初始值为0局部变量,自动变量初始值随机分配 C语言中,定义局部变量时如果未初始化,则值是随机的,为什么? 定义局部变量,其实就是在栈中通过移动栈指 ...
- C语言中内存分布及程序运行中(BSS段、数据段、代码段、堆栈)
BSS段:(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称.BSS段属于静态内存分配. 数据段 : ...
- 【转】linux代码段,数据段,BSS段, 堆,栈
转载自 http://blog.csdn.net/wudebao5220150/article/details/12947445 linux代码段,数据段,BSS段, 堆,栈 网上摘抄了一些,自己组 ...
- linux代码段,数据段,BSS段, 堆,栈(二)
//main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main() { int b; 栈 char s[] = "abc"; 栈 char ...
- 数据段、代码段、堆栈段、BSS段的区别
进程(执行的程序)会占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用 途 不一而不尽相同,有些内存是事先静态分配和统一回收的 ...
随机推荐
- synchronized关键字使用剖析
synchronized关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C. D等)正在用这个方法,有的话要等正在使用synchroniz ...
- ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
测试库一条update语句报错:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> ...
- <亲测好使>mac os 安装mcrypt扩展
以前安装opencart的时候倒是不需要mcrypt 这个库.但是新版本需要了.加上自己的 是mac环境.当时闲麻烦,就一直没装.这次下午就寻思给装上吧! 1.首先你要先安装xcode这个工具.不然没 ...
- spring中Bean的注入参数详解
字面值 一般指可用字符串表示的值,这些值可以通过<value>元素标签进行注入.在默认情况下,基本数据类型及其封装类.String等类型都可以采取字面值注入的方式,Spring容器在 ...
- mutable和volatile关键字
1.mutable 在C++中,mutable是为了突破const的限制而设置的.被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中,甚至结构体变量或者类对象为const,其 ...
- Careercup - Facebook面试题 - 5412018236424192
2014-05-01 01:32 题目链接 原题: Given a linked list where apart from the next pointer, every node also has ...
- asp.net中的mysql传参数MySqlParameter
注意在asp.net中传参 string sql="select name,id from user where id=@id"; //@idm不需要引号 MySqlParamet ...
- 3244: [Noi2013]树的计数 - BZOJ
Description 我们知道一棵有根树可以进行深度优先遍历(DFS)以及广度优先遍历(BFS)来生成这棵树的DFS序以及BFS序.两棵不同的树的DFS序有可能相同,并且它们的BFS序也有可能相同, ...
- ELF
http://www.360doc.com/content/11/0826/13/7588214_143424472.shtml 链接,装载都是基于数据结构ELF.
- 【CodeForces】【148D】Bag of mice
概率DP kuangbin总结中的第9题 啊……题目给的数据只有白鼠和黑鼠的数量,所以我们只能在这个上面做(gao)文(D)章(P)了…… 明显可以用两种老鼠的数量来作为状态= = 我的WA做法: 令 ...