【问题标题】:Behaviour of EBP and ESP in stack during 2 function calls2 次函数调用期间堆栈中 EBP 和 ESP 的行为
【发布时间】:2016-12-17 23:30:48
【问题描述】:

我正在了解函数调用在 x86 平台中的工作方式。据我了解 调用函数时发生的步骤:

  1. 函数的参数和返回地址被压入堆栈。

  2. 然后将当前 EBP 的值压入堆栈。

  3. 现在 ESP 已更改(由于步骤 2),EBP 已替换为 ESP,现在它们是 指向(堆栈?)的相同地址。

  4. 然后局部变量被推送,函数开始工作。

  5. 最后这个函数的局部变量和寄存器都被弹出了。在这个 进程 ESP 也在移动,对吧?

  6. 最后,EBP 被当前 ESP 中的值替换

  7. 现在 ESP 和 EBP 指向堆栈的相同地址。

所以我的问题是如果以上所有点都正确,否则请纠正我,将如何 该系统在 2 个函数调用的情况下工作。让我解释一下

  1. 对于第一个函数调用,ebp 被压入堆栈,并且 esp 和 ebp 相同。

  2. 现在增加 esp 以创建第一个函数调用的本地堆栈,例如 foo()。

  3. 现在假设有来自 foo() 的第二个函数调用,比如 goo()。

  4. 现在将再次发生相同的过程....当前的 ebp 将被推送到堆栈

  5. esp 和 ebp 将变得相同,并且 esp 将递增(从技术上讲, decremented ) 用于 goo() 函数的局部变量。

现在让我们关注函数何时返回。

  1. 从 goo() 返回,尤其是随着本地寄存器从堆栈中弹出并在 最后一个 esp 将被分配位置 ebp 持有的值,而不是当前的值 ebp,对吧?

  2. 然后会弹出保存 ebp 的位置。

所以我的问题是,如果现在是这种情况,我们已经失去了 函数 foo() 因为 ebp 和 esp 指向 foo() 的 ebp,它将被弹出 foo() 返回...

由于实际情况并非如此,因此我一定遗漏了一些东西。请帮帮我...

谢谢。

【问题讨论】:

标签: x86 return cpu-registers calling-convention


【解决方案1】:

我假设您说的是 cdecl (https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl) 调用约定(c 中经常使用的约定)。

%ebp 是基指针寄存器,它是被调用者保存的,这意味着调用者可以假设被调用者不会修改其值,因此调用者可以假设 %ebp 将始终引用其堆栈的基数。

%esp 是堆栈指针寄存器,它也是被调用者保存的,但是,由于调用者将被调用者的参数压入堆栈,%esp 是递减的,所以当被调用者返回时需要递增。我认为这就是你的问题潜伏的地方,%esp 的值只会增加到足以释放分配的参数,而不是返回到堆栈的底部。

而 %ebp 和 %esp 有效地指代堆栈的底部和“顶部”。没问题

【讨论】:

  • 谢谢你的回答,那么你能告诉我什么时候 ebp 和 esp 相等并且 ebp 被弹出。我的意思是当执行返回到 main() 函数时,它肯定会发生,对吧?但如果不涉及其他功能,如何区分这两种情况?
  • %ebp 和 %esp 可以在从子程序返回后的函数中相等,如果您在调用程序中做的第一件事是调用子程序,否则不会发生。例如,如果您在调用子程序之前声明了另一个局部变量,那么即使从该子程序返回后 %esp 也不会等于 %ebp。
  • ... 当我说它不会发生时,我的意思是只要例程还没有完成它必须做的事情,当它完成时,它将 %esp 恢复到它的值通过弹出堆栈或增加 %esp 的值以释放一些局部变量进入例程时。
  • 不,我们不会松开它,因为它是被调用者保存的,%esp 在调用子程序之前和之后的值是相同的。子例程可以根据需要修改 %esp,但它必须在返回之前恢复它。
  • @paramvir:另请参阅x86 tag wiki 中的 ABI/调用约定链接。
猜你喜欢
  • 2014-01-08
  • 2012-12-20
  • 2011-08-23
  • 2015-02-03
  • 2016-06-05
  • 2017-04-24
  • 2014-05-15
  • 1970-01-01
相关资源
最近更新 更多