我发现ESP寄存器是当前的堆栈指针,而EBP是当前堆栈帧的基本指针。但是,我不明白这些定义(我刚开始学习如何在汇编程序中编写代码)。
据我所知,ESP指向堆栈本身,EBP指向堆栈顶部的任何东西。但这些只是我的猜测而且很可能是不正确的。否则,如下所述的声明是什么意思?
MOV EBP, ESP
编辑:我认为上面的陈述是我书中的错字。我认为它应该是EBX而不是EBP
答案 0 :(得分:25)
esp是堆栈指针,ebp是/是用于堆栈帧,因此当你输入一个函数时,ebp可以获得esp的副本,在此之前堆栈上的所有内容,返回地址,传入参数,等等,对于该函数而言是全局的(局部变量)现在将在函数持续时间内远离堆栈帧指针。 esp现在可以像编译器一样随意漫游,并且可以在嵌套到其他函数时使用(每个函数都需要自然地保留ebp)。
这是一种管理堆栈的懒惰方式。使编译器调试变得更加容易,使编译器生成的代码更容易理解,但是烧掉了一个可能是通用目的的寄存器。
答案 1 :(得分:12)
正常情况下,EBP用于备份ESP,因此如果ESP由函数中的代码更改,则恢复ESP所需的全部是mov ESP,EBP。此外,由于EBP通常由函数中的代码保持不变,因此可以用它来访问传递的参数或局部变量,而无需调整偏移量。
对于“堆栈帧”用法,EBP在任何函数的开头被压入堆栈,因此压入堆栈的EBP值是调用当前函数的函数的EBP值。这使得代码或调试器可以“回溯”通过EBP被推送到堆栈的所有实例,并且堆栈上的每个EBP值实例都可以被认为是堆栈帧的基指针
请注意,某些编译器具有“省略帧指针”选项,在这种情况下,EBP不用于保存ESP或作为堆栈帧指针。相反,编译器会跟踪ESP,并且所有本地偏移都是与当前ESP的偏移量。
答案 2 :(得分:6)
EBP和ESP是这个时代的遗留物,编译器没有例如进行静态分析以检测函数调用中需要多少字节的堆栈。此外,堆栈应该在执行函数期间动态增长和缩小,中断将允许将所有堆栈从0丢弃到SP,而意大利面条代码是事实上的标准。实际上,中断(以及仅通过寄存器传递参数)是调用内核函数的设计方法。
在这些环境中,一个需要来拥有堆栈的固定点,其中始终找到调用者的返回地址,局部变量和函数的参数。因此bp
寄存器是合理的。在此架构中,bp
被允许编入索引([bp - 300h]),但sp
不是。那些可能被解释为mov ax, [sp + 1111h]
的操作码/指令编码被重用于其他目的。
在386+中,通过引入'E',ESP获得了抵消的属性。此时EBP
被释放的唯一目的,因为esp
能够处理这两项任务。
注意,即使现在EBP
通过堆栈段指向内存,就像ESP
一样。 EBX和BX使用DS。
答案 3 :(得分:3)
ESP寄存器是系统堆栈的堆栈指针。它很少由程序直接更改,但已更改 当数据被推入堆栈或从堆栈弹出时。堆栈的一种用途是过程调用。过程调用指令之后的指令地址存储在堆栈中。 EBP寄存器指向基址的指针。 通常,堆栈中访问的唯一数据项是堆栈顶部的数据项。尽管EBP寄存器通常用于标记堆栈中除堆栈顶部以外的固定点,例如,此类数据是参数。它们在返回地址之后从基本指针的堆栈EBP顶部偏移。这样您会看到类似EBP + 0x8,EBP + 0xC的东西,分别是1和2中的参数。
了解堆栈对于使用汇编语言进行编程非常重要,因为这可能会影响您将使用的调用约定,而与类型无关。例如,即使cdecl或__stdcall也依赖于ESP和EBP寄存器,而其他一些也以某种方式依赖于某些寄存器和堆栈。