56

我发现 ESP 寄存器是当前堆栈指针,而 EBP 是当前堆栈帧的基指针。但是,我不理解这些定义(我刚刚开始学习如何在汇编程序中编码)。

我的理解是 ESP 指向堆栈本身,而 EBP 指向堆栈顶部的任何内容1。但这些只是我的猜测,它们很可能是不正确的。否则,像下面这样的陈述是什么意思?

MOV EBP, ESP    

脚注 1:编者注:是的,这是不正确的。在标准术语中,“堆栈顶部”是 ESP 指向的位置,即使它是堆栈帧中的最低地址。类似于向上增长的堆栈数据结构,即使 x86 上的调用堆栈(与大多数 ISA 一样)向下增长。

4

4 回答 4

69

esp 是堆栈指针,ebp 是/是堆栈帧,因此当您输入函数时,ebp 可以在该点获取 esp 的副本,在此之前堆栈上的所有内容,返回地址,传入的参数等对于该函数是全局的(局部变量)现在将在函数执行期间与堆栈帧指针保持静态距离。esp 现在可以根据编译器的需要自由漫游,并且可以在嵌套到其他函数时使用(每个函数都需要自然地保留 ebp)。

这是管理堆栈的一种懒惰方式。使编译器调试更容易,使理解编译器生成的代码更容易,但会烧毁一个本来可以通用的寄存器。

于 2014-02-12T04:49:47.667 回答
20

通常EBP是用来备份ESP的,所以如果ESP被函数中的代码改变了,恢复ESP只需要mov ESP, EBP。此外,由于函数中的代码通常保持 EBP 不变,因此它可用于访问传递的参数或局部变量,而无需调整偏移量。

对于“堆栈帧”用法,EBP 在任何函数开始时被压入堆栈,因此压入堆栈的 EBP 的值是调用当前函数的函数的 EBP 值。这使得代码或调试器可以通过 EBP 被推入堆栈的所有实例“回溯”,并且堆栈上 EBP 值的每个实例都可以被认为是堆栈帧的基指针.

请注意,某些编译器具有“省略帧指针”选项,在这种情况下,EBP 不用于保存 ESP 或作为堆栈帧指针。相反,编译器会跟踪 ESP,并且所有本地偏移量都是相对于 ESP 的当前值的偏移量。

于 2014-02-12T04:16:43.997 回答
15

EBP 和 ESP 是那个时代的遗留物,在那个时代,编译器没有例如静态分析来检测函数调用中需要多少堆栈字节。此外,堆栈应该在函数执行期间动态增长和收缩,中断将允许从 0 到 SP 的所有堆栈丢弃,而意大利面条代码是事实上的标准。实际上中断(并且仅通过寄存器传递参数)是调用内核函数的设计方法。

在这些环境中,需要有一个固定的堆栈点,其中始终可以找到调用者的返回地址、局部变量和函数的参数。因此,bp登记是合理的。在这个架构bp中,允许被索引([bp - 300h]),但sp不是。那些可以被解释为mov ax, [sp + 1111h]被重新用于其他目的的操作码/指令编码。

在 386+ 中,通过引入“E”,ESP 获得了偏移属性。此时EBP已从唯一的目的中解脱出来,因esp​​为能够同时处理这两项任务。

请注意,即使现​​在通过堆栈EBP(SS)指向内存,就像. 其他寻址模式(没有 ESP/EBP 作为基础)默认为 DS 段。(16 位模式下的绝对值、DI、SI 和/或 BX,以及 32 位寻址模式下的任何寄存器都可以作为寻址模式下的基址)。ESP

于 2014-02-12T04:44:06.887 回答
5

ESP 寄存器是系统堆栈的堆栈指针。它很少由程序直接更改,但在将数据压入堆栈或从堆栈中弹出时会更改。堆栈的一种用途是在过程调用中。过程调用指令之后的指令地址存储在堆栈中。EBP 寄存器指向基址。通常,在堆栈中访问的唯一数据项是位于堆栈顶部的数据项。虽然 EBP 寄存器通常用于标记堆栈中除栈顶以外的固定点,例如此类数据是参数。它们在返回地址之后从基指针的堆栈 EBP 顶部偏移。所以你会看到类似 EBP+0x8, EBP+0xC,这分别是 1 和 2 中的参数。

在此处输入图像描述

理解堆栈在汇编语言编程中非常重要,因为这会影响您将使用的调用约定,无论类型如何。例如,即使 cdecl 或 __stdcall 也依赖于 ESP 和 EBP 寄存器,而其他的也以某种方式依赖于一些寄存器和堆栈。

于 2020-03-20T11:33:37.817 回答