当我将 %esp 移动到 %ebp 时会发生什么?

What happens when I move %esp to %ebp?

我知道这是一个非常基本的问题,但问题是:

我开始学习汇编程序,我正在尝试了解堆栈的工作原理。

起初,当我将值传递给汇编程序函数时,我会像这样访问它:

movl 4(%esp),%eax  # first parameter
movl 8(%esp),%ebx  # second parameter

但后来我被告知最好这样做:

push %ebp
movl %esp,%ebp
# and then I'd access the values on %ebp:
movl 8(%ebp),%eax
movl 12(%ebp),%eax
pop %ebp

好的,这里有什么区别?当我直接从 %esp 访问这些值时,它们不是已经在堆栈上了吗?再用push做有什么好处?

有没有人提供关于如何学习堆栈如何工作的好学习工具(针对傻瓜类型)的提示,以便可以学习这些堆栈指针、return 地址等等?我还没有找到任何关于它如何工作的良好视觉演示。 谢谢!

区别和原因都与堆栈帧有关。下面的link总结得还不错(如果我自己这么说的话)。

What is stack frame in assembly?

在大多数情况下使用 %ebp 有历史原因:在 16 位程序中,x86 CPU 不支持像 "movw 2(%sp), %ax" 这样的指令,因为 x86 CPU 只支持 %bx、%si、%di 和 %bp 寄存器用于内存寻址。所以在 16 位程序中你不能使用 %sp 所以你使用 %bp 代替。

使用优化良好的现代编译器时,您将不会再在 32 位代码中看到 "push" 和 "mov %esp, %ebp" 指令,但代码看起来像您的第一个示例 ("4( %esp)" 而不是 "8(%ebp)")。

此类编译器有时会出于不同目的使用 %ebp 寄存器。

有一个用例仍然需要 %ebp:alloca() 函数:此函数通过执行 "sub %eax, %esp"(或类似)指令在堆栈上保留内存。在这条指令之后,你不再知道你的参数在哪里。

然而,在具有 "flat" 内存布局 (%ds=%ss) 的 32 位代码中,您也可以使用任何其他寄存器代替 %ebp。