什么是ESP和EBP寄存器?

Luc*_*nis 33 x86 assembly callstack stack-frame

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

据我所知,ESP指向堆栈本身,EBP指向堆栈顶部的任何东西.但这些只是我的猜测而且很可能是不正确的.否则,如下所述的声明是什么意思?

MOV EBP, ESP    
Run Code Online (Sandbox Code Playgroud)

编辑:我认为上面的陈述是我书的错字.我认为它应该是EBX而不是EBP

old*_*mer 35

esp是堆栈指针,ebp是/是堆栈帧的,所以当你输入一个函数时,ebp可以获得esp的副本,在那之前堆栈上的所有内容,返回地址,传入参数等等对于该函数的全局函数(局部变量)现在将在函数持续时间内远离堆栈帧指针.esp现在可以像编译器一样随意漫游,并且可以在嵌套到其他函数时使用(每个函数都需要自然保留ebp).

这是一种管理堆栈的懒惰方式.使编译器调试变得更加容易,使得理解编译器生成的代码更容易,但是烧掉了一个可能是通用的寄存器.

  • 谢谢。对于所有仍在尝试理解的人,对我来说,关键是告诉自己:将 ebp 推入堆栈以进行 ebp 备份。然后将 esp 移动到 ebp。现在我们可以“玩”了。在函数返回之前,将 ebp 移回 esp 以恢复 esp 在将 esp 移至 ebp 之前的状态。然后pop ebp从栈顶恢复ebp。 (12认同)
  • 这一切都是为了将​​堆栈和寄存器恢复到函数调用之前的状态。 (5认同)

rcg*_*ldr 15

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

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

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


Aki*_*nen 8

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

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

在386+中,通过引入'E',ESP获得了抵消的属性.此时EBP脱离了唯一的目的,因为esp能够处理这两项任务.

注意,即使现​​在EBP指向通过堆栈段的内存,就像ESP.EBX和BX使用DS.

  • 历史上,IA 有段寄存器;CS 代表代码,DS/ES 代表数据,SS 代表堆栈。每个段一次只能访问 64kb 的内存。386 具有相同的架构,添加了 FS 和 GS,但现在每个段都可以配置为访问 1 字节到 4GB 内存之间的任何位置。每个指令或寻址模式都有一个隐式段寄存器,通过它访问内存。甚至后来“扁平”内存模型成为事实上的标准,其中每个段寄存器都可以看到所有内容(但内核,它保留了例如 GS 寄存器)。 (2认同)

Pet*_*ter 5

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

在此处输入图片说明

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