内存模型与elf文件
bss段
bss段(bss segment) 用于存放程序中 未经初始化的全局变量和静态局部变量 。在目标文件中,这个段并不占据实际空间,它仅仅只是一个占位符。
bss段属于 静态内存分配 。
data段
数据段(data segment) 通常是指用来存放程序中 已初始化的全局变量和静态局部变量 的一块内存区域,读写属性 。
数据段属于 静态内存分配。
text段
代码段(code segment/text segment) 通常是指用来存放程序 执行代码的一块内存区域 ,也有可能包含一些只读的 常数变量(例如字符串常量等),只读属性。
这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
堆(heap)
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
栈(stack)
栈又称堆栈,是用户存放程序临时创建的局部变量,
也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。
由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场。
从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
解剖工具、命令
## 解剖 Linux C 程序.
file #查看文件属性
xxd #二进制格式文件转hex格式 例: xxd t1.o > t1.hex
#ELF 文件基本介绍
readelf
objdump
# 如何精简 Linux C 小程序
stdlibc #介绍 (gnulibc), 如何去掉链接stdlibc
注:mac os X下没有这两个命令,可以用brew来安装,brew update && brew install binutils,然后用greadelf和gobjdump。
附:一个程序本质上都是由 bss段、data段、text段三个组成的。
这样的概念,不知道最初来源于哪里的规定,但在当前的计算机程序设计中是很重要的一个基本概念。
而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题。
在采用段式内存管理的架构中(比如intel的80x86系统),bss段通常是指用来存放程序中未初始化的全局变量的一块内存区域,
一般在初始化时bss 段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。
比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。
text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;
而bss段不在可执行文件中,由系统初始化。
全局的未初始化变量存在于.bss段中,具体体现为一个占位符;
全局的已初始化变量存于.data段中;
而函数内的自动变量都在栈上分配空间;
.bss是不占用文件空间的,其内容由操作系统初始化(清零);
.data却需要占用,其内容由程序初始化。因此造成了上述情况。
bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小;
bss段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在数据段后面。
data段(已手动初始化的数据)则为数据分配空间,数据保存在目标文件中;
data段包含经过初始化的全局变量以及它们的值。当这个内存区进入程序的地址空间后全部清零。
包含data段和bss段的整个区段此时通常称为数据区。
【例】
// t1.c
int a = 1;
static int b = 4;
int c;
static int d;
char *s1 = "1234";
int main() {
int e = 2;
char *s2 = "3456";
return 0;
}
编译调试文件
gcc test1.c -o test1.o //编译
objdump -sx t1.o //调试
摘取相关信息如下:
Sections:
Idx Name Size VMA LMA File off Algn
……
15 .rodata 0000001a 0000000000400570 0000000000400570 00000570 23
CONTENTS, ALLOC, LOAD, READONLY, DATA
……
24 .data 00000018 0000000000601020 0000000000601020 00001020 23
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000010 0000000000601038 0000000000601038 00001038 2**2
ALLOC
Sections展示了不同的段的大小。
SYMBOL TABLE:
000000000060102c l O .data 0000000000000004 b
000000000060103c l O .bss 0000000000000004 d
……
0000000000601030 g O .data 0000000000000008 s1
0000000000601040 g O .bss 0000000000000004 c
0000000000601028 g O .data 0000000000000004 a
SYMBOL TABLE展示了不同的变量是存在哪的。这里就可以看到
data段有:
- 初始化了的全局变量a
- 初始化了的静态变量b
- 常量字符串s1
bss段有:
- 未初始化的全局变量c
- 未初始化的静态变量d
Contents of section .rodata:
400550 01000200 31323334 00333435 3600 ....1234.3456.
Contents of section .data:
6008a8 00000000 00000000 00000000 00000000 ................
6008b8 01000000 04000000 54054000 00000000 ........T.@.....
可以看到rodata段中有我们定义的1234和3456
而data段里有1和4(小端)分别对应a和b
还可以读取elf文件,命令是readelf