【问题标题】:How does a program know where the bss segment is located程序如何知道bss段在哪里
【发布时间】:2017-10-09 08:56:08
【问题描述】:

据我所知,x86 有专门的寄存器用于指向代码、数据和堆栈段的指针,但没有 bss 和堆段。计算机如何记住这些片段的位置?尤其是堆,因为 bss 直接在数据之后,但堆通常放在内存中的不同位置。

【问题讨论】:

  • 您在这里有一个严重的误解。这些段不需要在运行时记住。它们仅用于分配变量。每个变量都有一个位于该段中的地址。
  • 但是如果相同的代码没有存储段“偏移量”的指针,怎么能在内存的不同位置运行呢?
  • 除非编译为“可重定位”,否则它不能。
  • 堆通常由与您的代码链接(静态或动态)的 C 运行程序创建。它决定虚拟地址中的地址,调用操作系统提供的系统调用来映射页面并将地址存储在一些数据结构中,malloc(和函数族)将其用作堆。
  • 如果在windows上运行dumpbin /HEADERS binary.exe,你可以看到bss段的地址和它的大小。在 Linux 上,您可以使用 objdump。我相信所需的标志是-x

标签: c memory-management


【解决方案1】:

堆通常由与您的代码(静态或动态)链接的 C 运行程序创建。它决定虚拟地址中的一个地址,调用操作系统提供的系统调用来映射页面,并将地址存储在一些数据结构中,malloc(和函数族)将其用作堆。 所有这些代码要么在调用 main 之前执行,要么在二进制文件中静态初始化。

至于 bss 部分,如您所知,它被全零填充。该二进制文件包含有关 .bss 部分的大小和基地址的信息。加载器将页面映射到这个虚拟地址并用零清除它们(以一种有效的方式)。

如果在 Windows 上运行dumpbin /HEADERS binary.exe,您可以查看 bss 段的地址及其大小。在 Linux 上,您可以使用 objdump。我相信所需的标志是-x

关于你的问题,如果偏移量在指令中被硬编码,它们如何移动 -

二进制文件还有一个称为重定位表的表,其中包含访问特定部分上这些值的所有指令的地址。加载器可能决定将段放在其他地方(通常在您在 Linux 中加载多个 dll 或共享库时发生)。 在这种情况下,它会修补所有查看重定位表的指令。它实际上改变了指令中的偏移量。这是由加载程序在执行main之前完成的。

当然,这会产生开销,并且只有在重定位信息可用时才能完成。一些二进制文件可能会选择省略重定位表,在这种情况下,如果该部分无法放置在指定位置,则二进制文件加载失败。

希望这能消除一些困惑。

【讨论】:

  • 感谢您的详细解释。这个主题有什么名字,我可以用它来查找资源吗?编译器和操作系统如何管理内存的话题
  • 您可以阅读“链接器和加载器”。我真的没有资源链接。对于二进制文件的结构,您可以阅读 ELF 文件格式。它是最常用的。另一种是COFF。
  • 最后一个问题。如果这是代码运行的过程,并且代码对段在哪里有静态知识,那为什么还要有段寄存器呢?
  • 段寄存器确实与这些段没有任何关系。段寄存器用于分段内存模型。现代架构(如 x86_64)使用分页和平面内存模型,程序可以访问整个虚拟地址,并且可以决定如何使用它。简而言之,不再使用段寄存器。在 x86_64 上,cs、ds 段的基数始终设置为 0。
  • 如果在 x86 上您的程序确实使用了段寄存器,则 bss 和堆通常与其他 .data 和 .rodata 段结合在一起,作为数据段下内存的一部分。 cs 用于代码(二进制文件中的 .text 部分),stack 非常适合堆栈!
【解决方案2】:

实际上,简单的答案是“bss 段”只是可执行文件中的一个数字,它告诉加载程序至少要为零初始化的全局数据保留多少数据。仅此而已。

在保留此内存并将其设置为零后,程序的运行时启动(无论是从 Fortran、C 或特定平台编译的)现在保留更多内存并在那里设置堆,并且next 决定堆栈的去向。在更多特定于平台的初始化之后,控制最终被转移到可执行文件中指示的程序入口点,并且控制被转移到那里。直到现在,节目才“直播”。

【讨论】:

    【解决方案3】:

    据我所知,x86 有专门的寄存器用于指向代码、数据和堆栈段的指针,但没有 bss 和堆段。计算机如何记住这些片段的位置?尤其是堆,因为 bss 直接在数据之后,但堆通常放在内存中的不同位置。

    您正在为重叠的术语而苦恼。段可以指分段内存模型下的内存段(如在 64 位之前的 X86 中使用的那样)。段也可以引用由链接器创建的具有公共访问属性的内存块。

    您的问题似乎是指第二种用法。

    您似乎还对记忆的看法过于简单化了。首先,没有堆段。堆是一个或多个读/写数据块。

    其次,链接器可以创建多个需求零 (bss) 段。链接器还可以将段按任何顺序放入内存中

    第三,段的知识只在加载时才需要。加载段后,它们只是内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-31
      相关资源
      最近更新 更多