计算机系统篇之链接(10):.bss、.data 和 .rodata sections 之间的区别
Author: stormQ
Created: Friday, 08. May 2020 10:20PM
Last Modified: Thursday, 05. November 2020 11:02PM
本文通过示例剖析了.bss
、.data
和.rodata
sections 之间的区别。
Section 名称 | 区别1:用途不同 | 区别2:在目标文件中占用的空间不同 |
---|---|---|
.rodata | 用于维护只读数据,比如:常量字符串、带 const 修饰的全局变量和静态变量等 | 在目标文件中占用空间 |
.data | 用于维护初始化的且初始值非0的全局变量和静态变量(不带 const 修饰) | 在目标文件中占用空间 |
.bss | 用于维护未初始化的或初始值为0的全局变量和静态变量(不带 const 修饰) | 不占用目标文件的空间 |
注:
对于未初始化的全局变量和静态变量的初始值在运行期会被赋为0。从而,达到 .bss section 用于减少目标文件的大小的目的。
临时变量(即局部非静态变量)既不出现在.data
中,也不出现在.bss
中。它由运行期栈维护。
严格地讲,gcc 将 C 程序(以 .c 结尾的源文件)中的未初始化的全局变量标记为COMMON
,而不是放到.bss
section 中。对于 C++ 程序,无论是 gcc 还是 g++,都会将程序中未初始化的全局变量放到.bss
section 中。关于这一点,有兴趣的可以自己验证下。
区别 1 和区别 2 的验证过程:
# 查看 sum.cpp
$ cat sum.cpp
int sum(int a, int b)
{
static int val_1;
static int val_2 = 0;
static int val_3 = 1;
static int val_4 = 0;
static int val_5 = 2;
const static int val_6 = 0;
return a + b;
}
# 生成可重定位目标文件 sum.o
$ g++ -c sum.cpp -o sum.o
# 查看 sum.o 的 sections
$ readelf -S sum.o
There are 12 section headers, starting at offset 0x340:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
0000000000000014 0000000000000000 AX 0 0 1
[ 2] .data PROGBITS 0000000000000000 00000054
0000000000000008 0000000000000000 WA 0 0 4
[ 3] .bss NOBITS 0000000000000000 0000005c
000000000000000c 0000000000000000 WA 0 0 4
[ 4] .rodata PROGBITS 0000000000000000 0000005c
0000000000000004 0000000000000000 A 0 0 4
[ 5] .comment PROGBITS 0000000000000000 00000060
0000000000000033 0000000000000001 MS 0 0 1
[ 6] .note.GNU-stack PROGBITS 0000000000000000 00000093
0000000000000000 0000000000000000 0 0 1
[ 7] .eh_frame PROGBITS 0000000000000000 00000098
0000000000000038 0000000000000000 A 0 0 8
[ 8] .rela.eh_frame RELA 0000000000000000 000002c8
0000000000000018 0000000000000018 I 10 7 8
[ 9] .shstrtab STRTAB 0000000000000000 000002e0
000000000000005c 0000000000000000 0 0 1
[10] .symtab SYMTAB 0000000000000000 000000d0
0000000000000180 0000000000000018 11 15 8
[11] .strtab STRTAB 0000000000000000 00000250
0000000000000078 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
从上面的 sum.o 的 sections 信息,可以看出:
1).data
的大小为 8(Size 列的值为 0000000000000008),在目标文件中占用 8 字节(00000054~0000005c);
2).bss
的大小为 12(Size 列的值为 000000000000000c),在目标文件中占用 0 字节(.bss
在目标文件中的偏移量为 0000005c,.bss
下一个 section——comment
在目标文件中的偏移量为 0000005c)。因此,可以得出结论:.bss
不占用目标文件的空间。
3).rodata
的大小为 4(Size 列的值为 0000000000000004),在目标文件中占用 4 字节(0000005c~00000060)。从而,验证了"区别2:在目标文件中占用的空间不同"。
4)sum.cpp 中初始化的且初始值非0的变量(不带 const 修饰)有两个:val_3、val_5,占用 8 字节,与.data
的大小一致;未初始化的或初始值为0的变量(不带 const 修饰)有三个:val_1、val_2、val_4,占用 12 字节,与.bss
的大小一致;带 const 修饰的全局变量和静态变量有一个:val_6,占用 4 字节,与.rodata
的大小一致。从而,验证了"区别1:用途不同"。
最后,可以总结出对于 C++ 程序中一个全局变量或静态变量到底存放在.rodata
、.data
、.bss
中哪一个 section 的判断规则为:首先判断全局变量或静态变量是否带 const 修饰。如果带 const 修饰, 无论初始值是否为0,都会存放到.rodata
section 中;否则,判断是否已初始化且初始值是否为0。如果已初始化且初始值非0,则存放到.data
section 中;否则,存放到.bss
section 中。
下一篇:计算机系统篇之链接(11):为什么要避免在 C 和 C++ 中使用全局变量
上一篇:计算机系统篇之链接(9):位置无关代码(下)——真正理解 PIC 函数调用的工作原理(Linux X86-64 示例)