2014-02-12 82 views
3

我发现ESP寄存器是当前堆栈指针,EBP是当前堆栈帧的基址指针。但是,我不明白这些定义(我刚开始学习如何在汇编中编写代码)。什么是ESP和EBP寄存器?

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

MOV EBP, ESP  

编辑:我认为上面的声明是我的书的错字。我认为它应该是EBX而不是EBP

+0

EBX和EBP是不同的寄存器。 –

回答

6

esp是堆栈指针,ebp是/为一个堆栈帧,所以当你输入一个函数时,ebp可以得到一个esp的副本,在这之前堆栈上的所有东西都会发生,返回地址,传入参数等等,对于该函数来说全局的东西(局部变量)现在将在函数的持续时间内远离堆栈帧指针的静态距离。 esp现在可以随意编译,并且可以在嵌套到其他函数时使用(每个函数都需要自然保留ebp)。

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

2

正常情况下,EBP用于备份ESP,因此如果通过函数中的代码更改ESP,则恢复ESP所需的全部内容为mov ESP,EBP。另外,由于EBP通常不受函数中的代码的影响,因此可用于访问传递的参数或局部变量,而无需调整偏移量。

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

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

2

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

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

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

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

+0

我发现你的回答非常丰富,但我不确定你最后一句话的含义:“EBX和BX使用DS。”? –

+1

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

相关问题