为什么GCC在初始化为0时不分配静态变量

周永德

我将静态变量初始化为0,但是当我看到汇编代码时,我发现只有内存分配给该变量。该值未分配
。当我将静态变量初始化为其他数字时,我可以发现内存已分配了一个值。
我猜想GCC是否认为应该在使用内存之前由OS将内存初始化为0。

我使用的GCC选项是“ gcc -m32 -fno-stack-protector -c -o”

当我将静态变量初始化为0时,C代码和汇编代码为:

static int temp_front=0;
.local  temp_front.1909
.comm   temp_front.1909,4,4

当我将其初始化为其他数字时,代码为:

static int temp_front=1;
    .align 4
    .type   temp_front.1909, @object
    .size   temp_front.1909, 4
temp_front.1909:
    .long   1
迈克尔·凯彻

TL:DR:GCC知道BSS被保证在目标平台上被零初始化,因此它将零初始化的静态数据放在那里。

大图

大多数现代操作系统的程序加载器对于程序的每个部分(如数据部分)都有两种不同的大小。它得到的第一个大小是可执行文件(例如Windows上PE / COFF.EXE文件或Linux上的ELF可执行文件)中存储的数据大小,而第二个大小是程序运行时内存中数据部分的大小。 。

如果正在运行的程序的数据大小大于可执行文件中存储的数据量,则数据部分的其余部分将填充包含零的字节。在您的程序中,该.comm行告诉链接器保留4个字节而不初始化它们,以便OS在启动时将其零初始化。

gcc是做什么的?

gcc(或任何其他C编译器)在此.bss部分中分配具有静态存储持续时间的零初始化变量该部分中分配的所有内容都将在程序启动时初始化为零。对于分配,它使用comm伪指令,并且仅指定大小(4个字节)。

您可以使用size命令查看主要部分类型(代码,数据,bss)的大小。如果用1初始化变量,则它将包含在一个data节中,并在其中占据4个字节。如果将其初始化为零(或根本不初始化),则将其分配给.bss部分。

ld是做什么的?

ld将所有目标文件的所有数据类型节(甚至是静态库中的文件)合并到一个数据节中,然后合并所有.bss-type节。可执行输出包含操作系统程序加载器的简化视图。对于ELF文件,这是“程序头”。您可以使用objdump -p任意格式或readelfELF文件来查看它

程序头包含不同类型的条目。其中有几个条目,其类型PT_LOAD描述了操作系统要加载的“段”。这些PT_LOAD条目之一用于数据区(.data链接节的位置)。它包含一个条目p_filesz该条目指定在ELF文件中提供多少个用于初始化变量的字节,以及一个条目,p_memsz告诉加载程序应在地址空间中保留多少空间。关于哪些部分合并到链接器之间不同的PT_LOAD条目的细节取决于命令行选项,但是通常您会找到一个PT_LOAD条目,该条目描述了一个既可读又可写但不可执行的区域,其p_filesz值是小于p_memsz项(如果只有一个.bss,则没有.data则可能为零)。p_filesz是所有读写数据节的大小,而p_memsz较大则还为零初始化变量提供了空间。

数量p_memsz超过链接到可执行文件p_filesz的所有.bss部分的总和(由于与页面或磁盘块对齐,这些值可能会有些偏差)

有关程序头条目的描述,请参见System V ABI规范的第5章,尤其是第5-2和5-3页。

操作系统做什么?

Linux内核(或另一个兼容ELF的内核)遍历程序头文件中的所有条目。对于每个包含类型的条目,PT_LOAD它分配虚拟地址空间。它将地址空间的开头与可执行文件中的相应区域相关联,如果该空间可写,则启用写时复制。

如果p_memsz超过p_filesz,内核将剩余的地址空间安排为完全清零。因此,.bss由gcc在本节中分配的变量最终出现在ELF文件中可读写PT_LOAD条目的“尾部”中,内核提供了零。

任何没有备份数据的整页都可以从写时复制开始映射到共享的零物理页。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章