为什么在这些不同情况下数据/ BSS大小会发生变化?

问题描述

我正在学习虚拟内存管理和进程的内存分配。并做一些实验。有一些令人困惑的地方,如下所示:

案例1

#include <iostream>

int main() {
    return 0;
}

编译代码并使用二进制文件运行size并得到以下输出

   text    data     bss     dec     hex filename
   1985     640       8    2633     a49 main

案例2:代码更改为:

#include <iostream>

int global;

int main() {
    return 0;
}

并重建它,大小输出为:

   text    data     bss     dec     hex filename
   1985     640      16    2641     a51 main

注意:data的存储部分未更改,但是bss8更改为16这个结果对我来说很有意义,因为int global定义了一个未初始化的全局变量

案例3: 然后将代码更改为:

#include <iostream>

int global = 5;

int main() {
    return 0;
}

我初始化了全局变量。然后再次分析二进制文件

   text    data     bss     dec     hex filename
   1985     644       4    2633     a49 main

这一次,更改对我而言没有意义。与case1相比,为什么data部分增加了4个字节,而bss部分减少了4?

解决方法

差异由链接器及其决定布局二进制文件的方式引起。 x86(GNU ld)上的G ++默认链接器。

要了解差异,让我们以三种不同的方式来编译测试用例:

$ for x in *.cpp ; do g++ -g3 $x -o $x.ld.exe ; done                  # GNU ld
$ for x in *.cpp ; do g++ -g3 $x -o $x.gold.exe -fuse-ld=gold ; done  # gold
$ for x in *.cpp ; do g++ -g3 $x -o $x.gold.exe -fuse-ld=lld ; done   # lld

现在,大小如下:

   text    data     bss     dec     hex filename
   1873     656       8    2537     9e9 test1.cpp.ld.exe
   1873     656      16    2545     9f1 test2.cpp.ld.exe
   1873     660       4    2537     9e9 test3.cpp.ld.exe

   text    data     bss     dec     hex filename
   1877     656       2    2535     9e7 test1.cpp.gold.exe
   1877     656       9    2542     9ee test2.cpp.gold.exe
   1877     660       2    2539     9eb test3.cpp.gold.exe

   text    data     bss     dec     hex filename
   1817     576       2    2395     95b test1.cpp.lld.exe
   1817     576       9    2402     962 test2.cpp.lld.exe
   1817     580       2    2399     95f test3.cpp.lld.exe

bss部分的行为可以解释如下:

这里要存储两个或三个变量(completed.0,可能是globalstd::__ioinit)。始终存在的两个大小和对齐方式各为1个字节。仅在情况2中存在的全局变量的大小和对齐方式为4个字节。这就是为什么在情况1和情况3中goldlld都需要bss段使用2个字节的原因。对于情况2,global的存储格式是这两个单字节值,在第一个1字节值和global之间需要额外的3个字节填充。

通常来说,GNU ld的功能类似,只是它使用bss部分来提供部分间的填充。在情况1和3中,实际上仅使用bss节的2个字节,而情况2使用bss节的9个字节。由于在情况3中data增加了4个字节,并且bssdata之后进行了布局,因此删除了其中的4个填充字节,以使后续节的偏移保持对齐。这也是为什么在引入bssglobal节的大小增加8个字节而不是4个字节的原因:GNU ld确保该节的末尾保持8个字节对齐。>

,

更短的版本是:.bss节通过写时复制被映射到零页-任何未明确初始化的 变量都将添加到{{1 }},它实际上并不存在于ELF 文件中,仅在更改其变量之一的内容时才会分配(我简化了,分配是按页进行的,因此对此有更多详细信息。)

为它提供默认值时,不能将其映射到零页:它的值必须存在于ELF文件本身中,并映射到内存中(同样,写时复制,因此如果然后您更改值,将为新页面分配变量的修改值。如果将其更改为.bss,它将被添加到const int global = 5;中,并且您会看到.rodata的大小增加了。

我过去曾在博客中介绍过.text命令:https://flameeyes.blog/2008/12/01/for-elves-size-matters/