代码之家  ›  专栏  ›  技术社区  ›  Wouter Lievens

.bss节零初始化变量是否占用elf文件中的空间?

  •  43
  • Wouter Lievens  · 技术社区  · 16 年前

    如果我理解正确, .bss ELF文件中的节用于为零初始化变量分配空间。我们的工具链生成ELF文件,因此我的问题是:是否 BSS 部分实际上必须包含所有这些零?当我分配一个全局10兆字节数组时,它会在elf文件中产生10兆字节的零,这似乎是对空间的极大浪费。我在这里看到了什么问题?

    4 回复  |  直到 10 年前
        1
  •  67
  •   Johannes Schaub - litb    16 年前

    自从我和小精灵一起工作以来已经有一段时间了。但我想我还是记得这些东西。不,它在物理上不包含那些零。如果您查看一个ELF文件程序头,那么您将看到每个头都有两个数字:一个是文件中的大小。另一个是在虚拟内存中分配的部分的大小( readelf -l ./a.out ):

    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
      INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
          [Requesting program interpreter: /lib/ld-linux.so.2]
      LOAD           0x000000 0x08048000 0x08048000 0x00454 0x00454 R E 0x1000
      LOAD           0x000454 0x08049454 0x08049454 0x00104 0x61bac RW  0x1000
      DYNAMIC        0x000468 0x08049468 0x08049468 0x000d0 0x000d0 RW  0x4
      NOTE           0x000128 0x08048128 0x08048128 0x00020 0x00020 R   0x4
      GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
    

    类型标头 LOAD 是在加载文件以执行时复制到虚拟内存中的文件。其他头包含其他信息,如所需的共享库。如你所见, FileSize MemSiz 对于包含 bss 第二部分 负载 一)

    0x00104 (file-size) 0x61bac (mem-size)
    

    对于此示例代码:

    int a[100000];
    int main() { }
    

    ELF规范指出,MEM大小大于文件大小的段的一部分,只是在虚拟内存中用零填充。第二个的段到段映射 负载 标题如下:

    03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
    

    所以这里也有其他部分。对于C++构造函数/析构函数。Java也一样。然后它包含 .dynamic 节和其他对动态链接有用的东西(我相信这是一个包含所需共享库的地方,以及其他东西)。在那之后 .data 包含初始化的全局变量和局部静态变量的部分。最后, .bss 出现部分,加载时用零填充,因为文件大小不覆盖它。

    顺便说一下,您可以通过使用 -M 链接器选项。对于GCC,您使用 -Wl,-M 把选项传给链接器。上面的例子表明 a 分配范围 BSS . 它可能有助于您验证未初始化的对象是否真的以 BSS 而不是别的地方:

    .bss            0x08049560    0x61aa0
     [many input .o files...]
     *(COMMON) 
     *fill*         0x08049568       0x18 00
     COMMON         0x08049580    0x61a80 /tmp/cc2GT6nS.o
                    0x08049580                a
                    0x080ab000                . = ALIGN ((. != 0x0)?0x4:0x1) 
                    0x080ab000                . = ALIGN (0x4) 
                    0x080ab000                . = ALIGN (0x4) 
                    0x080ab000                _end = .
    

    默认情况下,为了与旧编译器兼容,gcc在公共部分中保留未初始化的全局,这些编译器允许在一个程序中定义两次全局,而不会出现多个定义错误。使用 -fno-common 要使gcc对对象文件使用.bss节(对最终链接的可执行文件没有影响,因为正如您看到的,它无论如何都会进入.bss输出节。这是由 连接脚本 . 用它显示 ld -verbose )但这不应该吓到你,这只是一个内部细节。参见GCC手册页。

        2
  •  21
  •   D.Shawley    16 年前

    这个 .bss ELF文件中的节用于静态数据, 未初始化 以编程方式,但保证在运行时设置为零。这里有一个小例子来解释这个区别。

    int main() {
        static int bss_test1[100];
        static int bss_test2[100] = {0};
        return 0;
    }
    

    在这种情况下 bss_test1 放入 BSS 因为它没有初始化。 bss_test2 但是被放入 .data 用一堆零分割。运行时加载程序基本上为 BSS 并在任何userland代码开始执行之前将其归零。

    你可以看到不同使用 objdump , nm 或类似的公用设施:

    moozletoots$ objdump -t a.out | grep bss_test
    08049780 l     O .bss   00000190              bss_test1.3
    080494c0 l     O .data  00000190              bss_test2.4
    

    这通常是第一个 惊奇 嵌入式开发人员遇到的…从不显式将静态初始化为零。运行时加载器(通常)负责处理这个问题。一旦显式初始化任何东西,您就告诉编译器/链接器在可执行映像中包含数据。

        3
  •  3
  •   mouviciel    16 年前

    .bss 节未存储在可执行文件中。最常见的部分( .text , .data , BSS ) 文本 (实际代码)和 数据 (初始化数据)存在于ELF文件中。

        4
  •  1
  •   Vijayendra Suman    16 年前

    这是正确的,.bss在文件中不是物理存在的,而是动态加载器为应用程序分配.bss节而存在的有关其大小的信息。 由于Thumb规则只加载,所以tls段获取应用程序的内存,其余部分用于动态加载程序。

    关于静态可执行文件,BSS部分在可执行文件中也有一定的空间。

    在没有加载程序的嵌入式应用程序中,这是常见的。

    苏曼

    推荐文章