据我所知,x86具有专用寄存器,用于指向代码,数据和堆栈段,但不包括bss和堆段。计算机如何记住这些段的位置?特别是堆,因为bss直接在数据之后,但堆通常被放置在内存中的不同位置。
答案 0 :(得分:3)
堆通常由C runtine创建,它与您的代码(静态或动态)链接。它决定虚拟地址中的地址,调用操作系统提供的系统调用来映射页面,并将地址存储在某些数据结构中,该数据结构由malloc(和函数族)用作堆。 所有这些代码都在调用main之前执行,或者在二进制文件中静态初始化。
至于bss部分,正如你所知,它充满了全部的零。二进制文件包含有关.bss部分大小和基址的信息。加载程序将页面映射到此虚拟地址并用零清除它们(以有效的方式)。
如果在Windows上运行dumpbin /HEADERS binary.exe
,您可以看到bss段的地址及其大小。在Linux上,您可以使用objdump
。我相信所需的标志是-x
。
关于你的问题,如果在指令中对偏移进行了硬编码,它们如何被移动?
二进制文件还有一个名为重定位表的表,该表包含在特定部分访问这些值的所有指令的地址。加载器可能决定将段放在其他位置(通常在Linux中加载多个dll或共享库时会发生)。
在这种情况下,它会修补查看重定位表的所有指令。它实际上改变了指令中的偏移量。这是在执行main
之前由加载程序完成的。
当然,这有一个开销,只有在重定位信息可用时才能完成。某些二进制文件可能会选择省略重定位表,在这种情况下,如果无法将该部分放在指定位置,则二进制文件将无法加载。
希望能够解决一些困惑。
答案 1 :(得分:0)
实际上,简单的答案是" bss段"只是可执行文件中的一个数字,它告诉加载程序至少为零初始化的全局数据保留多少数据。没什么。
保留并将此内存设置为零后,程序的运行时启动(无论是从Fortran,C还是针对特定平台编译),现在可以保留更多内存并在那里设置堆接下来决定堆栈的去向。在更多平台特定初始化之后,控制最终转移到可执行文件中指示的程序入口点,并且控制转移到那里。只有现在该计划"直播"。
答案 2 :(得分:0)
据我所知,x86具有专用寄存器,用于指向代码,数据和堆栈段,但不包括bss和堆段。计算机如何记住这些段的位置?特别是堆,因为bss直接在数据之后,但堆通常被放置在内存中的不同位置。
你正在为重复的术语而苦恼。段可以指代分段存储器模型下的存储器段(如在64位前的X86中使用的那样)。段也可以引用具有由链接器创建的公共访问属性的内存块。
您的问题似乎是指第二种用法。
您似乎也会因内存过于简单而受到影响。首先,没有堆段。堆是一个或多个读/写数据块。
其次,链接器可以创建多个需求零(bss)段。链接器还可以按内存中的任何顺序放置段
第三,只有在加载时才需要知道段。加载段后,它们只是内存。