初学者对x86堆栈的困惑

首先,我想知道这个模型是否是堆栈“框架”过程的准确表示。

我被告知,从概念上讲,堆栈就像一个可乐瓶。 糖在底部,你把它填满了顶部。 考虑到这一点,如果EIP在另一个瓶子中(它在代码段,而不是堆栈段),Call如何告诉EIP寄存器“定位”被调用的函数? 我在YouTube上观看了一段video,称“RAM的代码段”(保存function的地方)是EIP寄存器所在的位置。

通常,计算机程序使用四种存储区域(也称为区段或段):

  • text部分:包含程序代码。 当程序由操作系统加载时保留。 此区域是固定的,在程序运行时不会更改。 这最好被称为“代码”部分,但名称有历史原因。
  • data部分:包含程序的变量。 程序加载并初始化为程序员定义的值时保留它。 程序在执行时可以更改这些值。
  • 堆栈 :这是一个动态的内存区域。 它用于存储函数调用的数据。 它基本上通过将值“推”到堆栈并从堆栈弹出来工作。 这也被称为“LIFO”:先进先出。 这是函数的局部变量所在的位置。 如果函数完成,则数据将从堆栈中删除并丢失(基本上)。
  • :这也是一个动态内存区域。 编程语言中有特殊function,可根据程序的要求“分配”(保留)该区域的一部分。 如果不再需要,可以使用另一个函数将此区域返回到堆中。 当数据被明确释放时,它可以用于存储比仅仅函数调用(不同于堆栈)更长寿的数据。

textdata部分的data存储在程序文件中(它们可以在Linux中找到,例如使用objdump (在名称中添加. )。 堆栈不会存储在文件中的任何位置,因为它们是动态分配的(在-demand)由程序本身。

通常,在加载程序之后,将重新定义的内存区域视为单个大块,其中堆栈都位于其中。 它们从该区域的另一端开始并朝向彼此生长。 对于大多数体系结构,堆从低到高的内存地址(升序)和堆栈向下(降序)增长。 如果它们相交,程序就会耗尽内存。 由于这可能未检测到,堆栈可能会损坏(更改外部数据)堆,反之亦然。 这可能会导致任何类型的错误,具体取决于数据/数据的变化情况。 如果堆栈被破坏,这可能导致程序疯狂(这实际上是木马可能工作的一种方式)。 然而,现代操作系统应该采取措施在这种情况变得严重之前对其进行检测。

这不仅适用于x86,也适用于大多数其他CPU系列和操作系统,特别是:ARM,x86,MIPS,MSP430(微控制器),AVR(微控制器),Linux,Windows,OS-X,iOS,Android(使用DOS OS),DOS。 对于微控制器,通常没有堆(在运行时分配所有内存),并且堆栈的组织可能有点不同; 对于基于ARM的Cortex-M微控制器也是如此。 但无论如何,这是一个非常特殊的主题。


免责声明:这是非常简化的,所以请不要评论“如何关于bss,const,myspecialarea”;-)。 对于这些区域,C标准也没有要求,特别是使用堆或堆栈。 实际上,有些实现都没有使用。 这些大多数时候是嵌入式系统,具有小型(8或16位)MCU或DSP。 现代架构也使用CPU寄存器而不是堆栈来传递参数并保留局部变量。 这些是在目标平台的应用程序二进制接口中定义的。


对于堆栈,您可能会阅读维基百科文章 。 注意在典型(微)处理器中实现的数据结构“堆栈”和“硬件堆栈”之间的实现差异。