6

我知道这个话题已经在这里和互联网上的其他地方被报道过令人作呕- 但希望这个问题是一个简单的问题,因为我试图让我的头脑围绕组装......

因此,如果我理解正确,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

4

3 回答 3

4

这实际上不仅取决于硬件架构和编译器,还取决于调用约定,这只是一种商定的方式,函数与堆栈一起调用彼此。换句话说,根据您的编译器设置(以及特殊#pragma选项等),函数可以将事物压入堆栈的顺序不同。

看起来您正在谈论cdeclx86 架构上的调用约定。在这种情况下,调用者ebp通常会在返回地址之后立即被压入堆栈。因此,在您的示例的 N-2 中,位置 25 将包含一个指向调用函数 N-3 的指针(即,它将包含紧接在call您进入 N-2 之后的指令地址),位置 24 将包含旧的ebp,并且您的 esp 将在调用后立即 = 23,在任何本地人被推入堆栈之前。(除了一些编译器会在调用后立即在堆栈上腾出空间,因此 ESP 将是 20 而不是在函数 N-2 内上下移动。)

但是请注意,在 x86 上,编译器有时可以进行一种特殊的优化,称为帧指针省略ebp,它可以避免在某些条件下将旧的完全压入堆栈。

于 2010-04-28T01:54:31.000 回答
3
  1. 在调用N-3, ebpis 28espis之后25
  2. ebp的被推送,然后ebp被设置为 的当前值esp。现在两者espebp都是24
  3. 最后,esp调整为局部变量腾出空间。esp现在很可能20,这取决于调用时函数的行为方式 N-2

了解这一点的最佳方法是阅读函数序言,并熟悉 x86 实现。它也有助于接受这一点esp,并ebp用于在每个函数中本地化堆栈的使用,编译器、体系结构和平台之间存在一些差异(并且几乎与任何高于或等于 C 的语言的用户无关)。

于 2010-04-28T01:55:02.207 回答
1

这取决于平台,但这通常是事情的运作方式。

在我最熟悉的架构中,“调用”(也称为返回)地址位于 $ra 寄存器中,而堆栈位于调用者留下的任何位置。所以发生的事情是返回地址被压入堆栈,就像你的(调用者的)基指针一样,然后基指针被更新到堆栈所在的位置并且堆栈不断向上爬。当我不记得时,事物被推送的确切顺序和设置的内容,但通常由被调用者来保存将被破坏的寄存器。这样,如果被调用的函数只使用一个或两个寄存器,调用函数就不需要保存所有内容。(实际上,返回地址寄存器是一样的——如果函数没有调用其他任何东西,它就不会被压入堆栈。)

如果您反汇编程序并查看函数序言和结语,这实际上很容易理解。它们都遵循非常常见的模式,即在顶部“存储所有内容”并在底部“恢复所有内容”。(请注意,有时有些“特殊”寄存器永远不会存储或恢复,编译器知道如果没有函数调用,它只能指望值是一致的。在 MIPS 上,我认为它们是 S 寄存器, PPC称他们为t?)

于 2010-04-28T01:39:03.427 回答