初学者对x86栈的困惑
Beginner's confusion about x86 stack
首先,我想知道这个模型是否是堆栈 "framing" 过程的准确表示。
有人告诉我,从概念上讲,堆栈就像一个可乐瓶。糖在底部,你把它填满到顶部。考虑到这一点,如果 EIP 在另一个瓶子中(它在代码段中,而不是堆栈段中), Call 如何告诉 EIP 注册到 "target" 被调用函数?我在YouTube上看了一个视频,说"Code Segment of RAM"(函数存放的地方)就是EIP寄存器所在的地方。
通常,计算机程序使用四种内存区域(也称为段或段):
text
部分:这包含程序代码。它在操作系统加载程序时被保留。该区域是固定的,在程序 运行ning 期间不会更改。这个最好叫做"code"节,但是这个名字有历史原因。
data
部分:这包含程序的变量。它在程序加载并初始化为程序员定义的值时保留。程序在执行时可以更改这些值。
- 堆栈:这是内存的动态区域。它用于存储函数调用的数据。它基本上通过 "pushing" 值入栈并从栈中弹出来工作。这也叫"LIFO":后进先出。这是函数的局部变量所在的位置。如果函数完成,数据将从堆栈中移除并丢失(基本上)。
- heap:这也是一个动态内存区域。编程语言中有特殊函数"allocate"(保留)根据程序的请求保留一块该区域。如果不再需要,另一个函数可用于 return 此区域到堆。由于数据是显式释放的,它可用于存储比函数调用(不同于堆栈)寿命更长的数据。
text
和 data
部分的数据存储在程序文件中(它们可以在 Linux 中找到,例如使用 objdump
(添加 .
到名称)。stack 和 heap 不存储在文件中的任何位置,因为它们是由程序本身。
通常情况下,程序加载后,内存区域扩充被视为一个大块,其中 stack 和 heap 位于。它们从该区域的另一端开始并向彼此生长。对于大多数体系结构,堆从低到高内存地址(升序)和堆栈向下(降序)增长。如果它们相交,则程序 运行 内存不足。由于这可能会在未被发现的情况下发生,因此堆栈可能会破坏(更改外部数据)堆,反之亦然。这可能会导致任何类型的错误,具体取决于 how/what 数据已更改。如果堆栈被破坏,这可能会导致程序失控(这实际上是特洛伊木马可能工作的一种方式)。然而,现代操作系统应该采取措施在这种情况变得严重之前检测到它。
这不仅适用于 x86,也适用于大多数其他 CPU 系列和操作系统,特别是:ARM、x86、MIPS、MSP430(微控制器)、AVR(微控制器),Linux , Windows, OS-X, iOS, Android(使用Linux OS), DOS。对于微控制器,通常没有堆(所有内存都在 运行-time 分配)并且堆栈的组织方式可能有点不同;基于 ARM 的 Cortex-M 微控制器也是如此。但无论如何,这是一个很特殊的课题。
免责声明:这是非常简化的,所以请不要评论 "how about bss, const, myspecialarea";-) 。 C 标准也没有要求这些领域,特别是使用堆或堆栈。事实上,有些实现也不使用。这些通常是带有小型(8 位或 16 位)MCU 或 DSP 的嵌入式系统。此外,现代体系结构使用 CPU 寄存器而不是堆栈来传递参数和保留局部变量。这些在目标平台的应用程序二进制接口中定义。
对于堆栈,你可能会阅读wikipedia article。请注意在典型(微)处理器中实现的数据结构 "stack" 和 "hardware stack" 之间的实现差异。
首先,我想知道这个模型是否是堆栈 "framing" 过程的准确表示。
有人告诉我,从概念上讲,堆栈就像一个可乐瓶。糖在底部,你把它填满到顶部。考虑到这一点,如果 EIP 在另一个瓶子中(它在代码段中,而不是堆栈段中), Call 如何告诉 EIP 注册到 "target" 被调用函数?我在YouTube上看了一个视频,说"Code Segment of RAM"(函数存放的地方)就是EIP寄存器所在的地方。
通常,计算机程序使用四种内存区域(也称为段或段):
text
部分:这包含程序代码。它在操作系统加载程序时被保留。该区域是固定的,在程序 运行ning 期间不会更改。这个最好叫做"code"节,但是这个名字有历史原因。data
部分:这包含程序的变量。它在程序加载并初始化为程序员定义的值时保留。程序在执行时可以更改这些值。- 堆栈:这是内存的动态区域。它用于存储函数调用的数据。它基本上通过 "pushing" 值入栈并从栈中弹出来工作。这也叫"LIFO":后进先出。这是函数的局部变量所在的位置。如果函数完成,数据将从堆栈中移除并丢失(基本上)。
- heap:这也是一个动态内存区域。编程语言中有特殊函数"allocate"(保留)根据程序的请求保留一块该区域。如果不再需要,另一个函数可用于 return 此区域到堆。由于数据是显式释放的,它可用于存储比函数调用(不同于堆栈)寿命更长的数据。
text
和 data
部分的数据存储在程序文件中(它们可以在 Linux 中找到,例如使用 objdump
(添加 .
到名称)。stack 和 heap 不存储在文件中的任何位置,因为它们是由程序本身。
通常情况下,程序加载后,内存区域扩充被视为一个大块,其中 stack 和 heap 位于。它们从该区域的另一端开始并向彼此生长。对于大多数体系结构,堆从低到高内存地址(升序)和堆栈向下(降序)增长。如果它们相交,则程序 运行 内存不足。由于这可能会在未被发现的情况下发生,因此堆栈可能会破坏(更改外部数据)堆,反之亦然。这可能会导致任何类型的错误,具体取决于 how/what 数据已更改。如果堆栈被破坏,这可能会导致程序失控(这实际上是特洛伊木马可能工作的一种方式)。然而,现代操作系统应该采取措施在这种情况变得严重之前检测到它。
这不仅适用于 x86,也适用于大多数其他 CPU 系列和操作系统,特别是:ARM、x86、MIPS、MSP430(微控制器)、AVR(微控制器),Linux , Windows, OS-X, iOS, Android(使用Linux OS), DOS。对于微控制器,通常没有堆(所有内存都在 运行-time 分配)并且堆栈的组织方式可能有点不同;基于 ARM 的 Cortex-M 微控制器也是如此。但无论如何,这是一个很特殊的课题。
免责声明:这是非常简化的,所以请不要评论 "how about bss, const, myspecialarea";-) 。 C 标准也没有要求这些领域,特别是使用堆或堆栈。事实上,有些实现也不使用。这些通常是带有小型(8 位或 16 位)MCU 或 DSP 的嵌入式系统。此外,现代体系结构使用 CPU 寄存器而不是堆栈来传递参数和保留局部变量。这些在目标平台的应用程序二进制接口中定义。
对于堆栈,你可能会阅读wikipedia article。请注意在典型(微)处理器中实现的数据结构 "stack" 和 "hardware stack" 之间的实现差异。