内容当然包括代码、数据,还包括链接时所必须的信息:符号表、调试信息、字符串
一般把这些信息按照不同的属性,以节(Section)的形式存储,有时候也叫做段(Segment)
编译后的代码放在代码段:.code
或 .text
全局变量和局部静态变量数据放在数据段 .data
未初始化的全局变量和局部静态变量放在 .bss
(Block Started by Symbol) 因为他们都是 0,所以放在 data 段占内存没必要,做一个预留内存的标记就行了,这样也没占多少空间
为什么要把程序的指令和数据分开放?
当程序被装载的时候,数据和指令分别映射到两个虚拟内存的区域,可以分别设置权限防止程序指令被改写等等提升安全性
对提高 CPU 缓存命中有提升
最重要的原因:当系统运行着多个程序的的副本的时候,他们的指令都是一样的,所以内存中只要保存一份指令就行了,只要数据不同就行,这对内存的节省提升是巨大的
只读数据段 .rodata
还有一些其他常用的段名
gcc 提供了一个拓展机制,可以让程序员制定变量所处的段:
解析一个 elf 可执行文件,通过 elf 文件头可以找到段表的偏移和段表的字符表在段表里的下标,通过段表可以解析出整个 elf 文件段结构
readelf -s main.o
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 .data
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 .bss
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6 .rodata
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7 FOO
7: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 static_var.1
8: 0000000000000004 4 OBJECT LOCAL DEFAULT 4 static_var2.0
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 5 global_init_var
10: 0000000000000000 4 OBJECT GLOBAL DEFAULT 4 global_uninit_var
13: 0000000000000000 51 FUNC GLOBAL DEFAULT 1 main
ndx 是代码所属的段