汇编语言中的堆栈/基址指针

7

我知道这个话题已经被过度讨论了,无论是在这里还是在其他互联网地方,但希望问题很简单,因为我试图理解汇编语言......

所以如果我理解正确,ebp(基指针)将指向栈顶,esp(栈指针)将指向底部--因为栈向下增长。esp因此指向“当前位置”。 因此,在函数调用时,一旦您已将ebp保存在堆栈上,您就会插入一个新的堆栈帧-用于该函数。因此,在下面的图像中,如果您从N-3开始,将使用函数调用转到N-2。但是当您在N-2时,您的ebp是否等于25而esp等于24(至少在放置任何数据在堆栈上之前)?

这是正确的吗,还是我离题了?

谢谢!

http://upload.wikimedia.org/wikipedia/en/a/a7/ProgramCallStack2.png
(来源:wikimedia.org)


我会打上C标签,因为你似乎想了解C编译器生成的代码。如果不对,请随意删除该标签。 - Earlz
3个回答

4
这实际上不仅取决于硬件架构和编译器,还取决于调用约定,即函数如何使用堆栈相互调用的约定方式。换句话说,根据编译器设置(以及特殊的#pragma选项等等),函数可以按不同的顺序将东西推入堆栈。
看起来你正在谈论x86架构上的调用约定。在这种情况下,调用者的ebp通常会在返回地址之后立即被推入堆栈。因此,在您的示例中,N-2中的位置25将包含指向调用函数N-3的指针(即,它将包含进入N-2之后紧接着call指令的地址),位置24将包含旧的ebp,并且在任何局部变量被推入堆栈之前,您的esp将在调用后立即=23。(除了一些编译器会在调用后立即在堆栈上留出空间,因此ESP将是20而不是在函数N-2内移动。)

然而,需要注意的是,在x86上编译器有一种特殊的优化叫做frame pointer omission,在特定条件下可以避免将旧的ebp推入栈中。


3
  1. 调用 N-3 后,ebp 的值为 28esp 的值为 25
  2. 先将旧的 ebp 压入堆栈,然后将 ebp 设置为当前 esp 的值。现在,espebp 的值都是 24
  3. 最后,调整 esp 的值以为本地变量腾出空间。这时,esp 的值可能是 20,具体取决于调用 N-2 时函数的行为。

理解此过程的最佳方法是阅读有关函数序言的内容,并熟悉x86实现。还要知道,在每个函数中,espebp 被用于本地化堆栈的使用,不同编译器、架构和平台之间会有一些差异(对于任何高于或等于C语言的高级语言的用户来说,这几乎是无关紧要的)。


1
很遗憾我不能接受两个答案 - 感谢您的指引! - malangi

1

这取决于平台,但一般来说,事情是这样的。

在我最熟悉的架构中,“调用”(又称返回)地址在$ra寄存器中,堆栈则是在调用者离开它的地方。因此,返回地址和您(调用者)的基指针被推到堆栈上,然后基指针被更新为指向堆栈所在的位置,并且堆栈继续向上爬。我不记得推送哪些内容的确切顺序以及设置什么,但通常由被调用者保存将被破坏的寄存器。这样,如果被调用的函数仅使用一个或两个寄存器,那么调用函数就无需保存所有内容。 (实际上,返回地址寄存器是相同的-如果函数不调用其他任何东西,它将不会被推入堆栈。)

如果您反汇编程序并查看函数序言和尾声,那么这实际上非常容易理解。它们都遵循相当普遍的模式,即在顶部“存储所有内容”,在底部“恢复所有内容”。 (请注意,有时会有“特殊”寄存器不会被存储或恢复,并且编译器知道只有在没有函数调用的情况下才能依赖值是一致的。在MIPS上,我认为它们是S寄存器,而PPC称其为t?)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接