Assembly 以下代码中EBP的用途是什么?
我有两个关于EBP注册的问题 我了解ESP和EIP。然而,我真的不明白为什么会使用EBP 在下面的代码中,我将EBP寄存器(实际上是0000000)推送到堆栈中。然后,我将堆栈的内存地址移动到EBP,以便ESP和EBP具有相同的数据。这是序言。有一些代码以syscall结束。然后我做相反的操作(epilog),因为“leave”表示我将EBP移动到ESP(由于prolog,这些值是相同的),然后将堆栈的最后一个值(EBP,00000000)弹出到EBP。这将为EBP提供与序言之前相同的值 为什么会有人这样做?重点是什么?请用简单的方式回答!请记住,我没有掌握EBP(帧指针)的实际功能 编辑:或者这是一种在函数中有效备份堆栈(ESP)的方法?换句话说:程序可以做它对堆栈所做的事情,“原始堆栈”将始终存在于EBP中。然后,当程序完成时,EBP将恢复到以前的状态。这是正确的吗?如果是这样,尾声只是一个整理程序 此外,在AIUI中,我可以使用“回车”替换“推ebp/mov ebp,esp”。然而,当我尝试在nasm中编译时,我得到了“错误:操作码和操作数的组合无效”“离开”“工作正常;”“输入”不适用。正确的语法是什么 谢谢Assembly 以下代码中EBP的用途是什么?,assembly,Assembly,我有两个关于EBP注册的问题 我了解ESP和EIP。然而,我真的不明白为什么会使用EBP 在下面的代码中,我将EBP寄存器(实际上是0000000)推送到堆栈中。然后,我将堆栈的内存地址移动到EBP,以便ESP和EBP具有相同的数据。这是序言。有一些代码以syscall结束。然后我做相反的操作(epilog),因为“leave”表示我将EBP移动到ESP(由于prolog,这些值是相同的),然后将堆栈的最后一个值(EBP,00000000)弹出到EBP。这将为EBP提供与序言之前相同的值 为什么
Example:
push ebp
mov, ebp, esp
[some code here]
int 0x80
leave
ret
enter
还需要一个数字,该数字是要分配的空间量,这是问题的关键:这些说明用于为函数的局部变量设置空间
局部变量通过EBP寄存器引用。让我给你举个例子:
import core.stdc.stdio;
void main() {
int a = 8;
a += 8;
printf("%d\n", 8);
}
(这是D代码,但实际上并不相关)
这是函数prolog,用于设置ebp。子esp,0x4
将堆栈推开4个字节-这为本地int a
变量腾出了空间,该变量的长度为4个字节
很少使用enter
指令,但我相信enter4,0
也会做同样的事情-输入一个具有4字节局部变量空间的函数。编辑:另一个0是嵌套级别,我从未见过使用它。。。enter通常比编译器在这里自己执行步骤慢/编辑
6: b8 08 00 00 00 mov eax,0x8
b: 89 45 fc mov DWORD PTR [ebp-0x4],eax
这是a=8
行-第二行将值存储在局部变量的内存中
e: 01 45 fc add DWORD PTR [ebp-0x4],eax
然后,我们在a+=8
中添加它(编译器在这里重用了eax,因为它认识到
号码相同……)
之后,它通过将参数推送到堆栈来调用printf,然后将eax寄存器归零(xor eax,eax
),这就是D从函数返回0的方式
11: 50 push eax
12: b9 00 00 00 00 mov ecx,"%d\n"
17: 51 push ecx
18: e8 fc ff ff ff call printf
1d: 31 c0 xor eax,eax
1f: 83 c4 08 add esp,0x8
请注意,addesp,0x8
这里是printf调用的一部分:调用方负责在调用函数后清除参数。这是必需的,因为只有调用者知道它实际发送了多少个参数——这就是启用printf变量参数的原因
总之,最后,我们清理局部变量并从函数返回:
22: c9 leave
23: c3 ret
编辑:leave
btw扩展到mov-esp,ebp;pop-ebp代码>-这与设置指令完全相反,正如Aki Suikkonen在另一个答案中所说,这里的一件好事是堆栈恢复到函数入口的状态,不管函数内部发生了什么(除非函数完全破坏了堆栈的内容,否则程序很可能很快就会崩溃)。/edit
因此,归根结底,ebp都是关于局部变量的。它使用esp启动,因此它有一个很好的内存空间,不会占用其他函数(它在调用堆栈上),而是将其移动到ebp,以便在整个函数中,局部变量保持一致的偏移量-变量a
始终是[ebp-4]在这个函数中,即使在堆栈被操纵的时候
通过反汇编您用C或其他语言编写的函数(如我们在这里所做的),最容易看到实际操作。我使用的是linux命令objdump-d-M intel somefile.o
(然后我手动修复了一些小问题,使其更具可读性。如果反汇编.o文件,还没有解决所有库调用,因此看起来有点奇怪,但这不会影响局部变量!)EBP
形成堆栈中变量的固定参考点:主要是函数的所有参数,函数的所有局部参数,最后是返回地址。有了这个固定点,函数可以几乎随机地增长/改变堆栈,从任何地方跳到函数尾声,并将堆栈指针恢复到起始位置最后位置
这个概念几乎是强制性的,因为最初的8086代码不允许堆栈指针与位移一起使用,如mov ax,[sp+10]
,而只能与push
和pop
一起使用。除了顶部元素之外,其他任何内容都需要使用mov xx,[bp+10]
EBP的思想实际上是形成一个固定的参考点。通常,您可能会摆弄堆栈指针(例如,在将参数推到堆栈上准备调用时)找出某个数据段相对于堆栈指针的位置是一件非常痛苦的事情。但是相对于基指针,它总是一样的。现代编译器很容易解决这个问题,但是如果你想(手工)编写一大块汇编代码使用堆栈进行推送和弹出时,您会发现相对于不改变的寄存器(EBP)引用本地变量更容易。调用您缺少的内容。enter
的语法是enter 0,0
。第一个参数是为本地变量保留的字节数(子esp,?)。第二个参数是“lex level”-您不想知道,只需将其设为零即可。可能重复的Never useenter
。
11: 50 push eax
12: b9 00 00 00 00 mov ecx,"%d\n"
17: 51 push ecx
18: e8 fc ff ff ff call printf
1d: 31 c0 xor eax,eax
1f: 83 c4 08 add esp,0x8
22: c9 leave
23: c3 ret