浅谈bss段的作用
在Linux下面,一个ELF二进制文件通过许多的节组织起来,例如:
$ readelf -S main
There are 36 section headers, starting at offset 0x43e0:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000318 00000318
000000000000001c 0000000000000000 A 0 0 1
[ ...]
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
对于这些节,作用简单描述如下:
- init节:程序初始化入口代码,在main() 之前运行。
- bss节:BSS段属于静态内存分配。通常是指用来存放程序中未初始化的全局变量和未初始化的局部静态变量。未初始化的全局变量和未初始化的局部静态变量默认值是0。程序在运行时,才会给BSS节里面的变量分配内存空间。 在目标文件(*.o)和可执行文件中,BSS段只是为未初始化的全局变量和未初始化的局部静态变量预留位置而已,它并没有内容,所以它不占据空间。
- data节:数据节,通常是指用来存放程序中已初始化的全局变量和已初始化的静态变量的一块内存区域。数据段属于静态内存分配。
- text节:代码节通常是指用来存放程序执行代码的一块内存区域。。
- rodata节:存放的是只读数据,比如字符串常量,全局const变量 和 #define定义的常量。
这里我们主要看一下BSS节的用途。
1. 目标文件布局
对于一个程序,数据存放的节如下:
#include <stdio.h>
int g_iInitVar = 100; //.data
int g_iUnInitVar; //.bss
//.text
void fun(int data)
{
printf("data is %d.\n", data);
}
//.text
int main(int argc, char* argv[])
{
static int s_iInitVar = 200; //.data
static int s_iUnInitVar; //.bss
fun(s_iInitVar + s_iUnInitVar);
fun(g_iInitVar + g_iUnInitVar);
return 0;
}
在ELF的布局如下:
$ objdump -t main | grep Var
0000000000004014 l O .data 0000000000000004 s_iInitVar.2322
000000000000401c l O .bss 0000000000000004 s_iUnInitVar.2323
0000000000004010 g O .data 0000000000000004 g_iInitVar
0000000000004020 g O .bss 0000000000000004 g_iUnInitVar
2. 关于BSS节
由于这些未初始化的全局变量和静态变量默认都是初始化为0的,所以为了减小ELF文件的大小,这些数据并不编译放在ELF文件中,在ELF文件中使用一个节专门用来标记这些数据(并不存放数据,只是标记数据大小,相当占位符)。
很多情况下,我们可能想要看一看BSS节里面的数据,如下:
$ objdump -s --section=.data main
main: file format elf64-x86-64
Contents of section .data:
4000 00000000 00000000 08400000 00000000 .........@......
4010 64000000 c8000000 d.......
$ objdump -s --section=.bss main
main: file format elf64-x86-64
objdump: section '.bss' mentioned in a -j option, but not found in any input file
$ objdump -s --section=.text main
main: file format elf64-x86-64
Contents of section .text:
1060 f30f1efa 31ed4989 d15e4889 e24883e4 ....1.I..^H..H..
1070 f050544c 8d05b601 0000488d 0d3f0100 .PTL......H..?..
1080 00488d3d e9000000 ff15522f 0000f490 .H.=......R/....
1090 488d3d81 2f000048 8d057a2f 00004839 H.=./..H..z/..H9
10a0 f8741548 8b052e2f 00004885 c07409ff .t.H.../..H..t..
10b0 e00f1f80 00000000 c30f1f80 00000000 ................
10c0 488d3d51 2f000048 8d354a2f 00004829 H.=Q/..H.5J/..H)
10d0 fe4889f0 48c1ee3f 48c1f803 4801c648 .H..H..?H...H..H
10e0 d1fe7414 488b0505 2f000048 85c07408 ..t.H.../..H..t.
10f0 ffe0660f 1f440000 c30f1f80 00000000 ..f..D..........
1100 f30f1efa 803d0d2f 00000075 2b554883 .....=./...u+UH.
1110 3de22e00 00004889 e5740c48 8b3de62e =.....H..t.H.=..
1120 0000e819 ffffffe8 64ffffff c605e52e ........d.......
1130 0000015d c30f1f00 c30f1f80 00000000 ...]............
1140 f30f1efa e977ffff fff30f1e fa554889 .....w.......UH.
1150 e54883ec 10897dfc 8b45fc89 c6488d3d .H....}..E...H.=
1160 a00e0000 b8000000 00e8e2fe ffff90c9 ................
1170 c3f30f1e fa554889 e54883ec 10897dfc .....UH..H....}.
1180 488975f0 8b158a2e 00008b05 8c2e0000 H.u.............
1190 01d089c7 e8b0ffff ff8b1571 2e00008b ...........q....
11a0 057b2e00 0001d089 c7e89bff ffffb800 .{..............
11b0 000000c9 c3662e0f 1f840000 00000090 .....f..........
11c0 f30f1efa 41574c8d 3deb2b00 00415649 ....AWL.=.+..AVI
11d0 89d64155 4989f541 544189fc 55488d2d ..AUI..ATA..UH.-
11e0 dc2b0000 534c29fd 4883ec08 e80ffeff .+..SL).H.......
11f0 ff48c1fd 03741f31 db0f1f80 00000000 .H...t.1........
1200 4c89f24c 89ee4489 e741ff14 df4883c3 L..L..D..A...H..
1210 014839dd 75ea4883 c4085b5d 415c415d .H9.u.H...[]A\A]
1220 415e415f c366662e 0f1f8400 00000000 A^A_.ff.........
1230 f30f1efa c3 .....
objdump: section ‘.bss’ mentioned in a -j option, but not found in any input file 从上面,我们可以发现ELF文件中并没有BSS节,但是BSS确实是存在的,如下:
$ objdump -t main | grep .bss
0000000000004018 l d .bss 0000000000000000 .bss
0000000000004018 l O .bss 0000000000000001 completed.8060
000000000000401c l O .bss 0000000000000004 s_iUnInitVar.2323
0000000000004020 g O .bss 0000000000000004 g_iUnInitVar
0000000000004028 g .bss 0000000000000000 _end
0000000000004018 g .bss 0000000000000000 __bss_start
为什么会这样呢?因为BSS节,并没有真正的节数据,只有一个BSS的 section 头部,如下:
$ objdump -h --section=.bss main
main: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
25 .bss 00000010 0000000000004018 0000000000004018 00003018 2**2
ALLOC
在应用程序被装载的时候,装载器只需要根据这个大小来给BSS的数据分配相应大小的空间即可,因此在ELF文件中并没有BSS节的数据。