函数调用如何在x86 32中工作?
我正在从Jonathan Bartlett的“从头开始编程”一书中学习GNU程序集。
在讨论函数调用和堆栈的主题时,我无法理解它的工作原理。
以下是书中的内容:
在执行一个函数之前,一个程序按照它们记录的相反顺序将函数的所有参数压入堆栈。 然后程序发出一个呼叫指令,指示它希望启动哪个功能。 调用指令有两件事。 首先将下一条指令(返回地址)的地址推入堆栈。 然后它修改指令指针(%eip)以指向函数的开始。 所以,在函数启动的时候,堆栈看起来像这样(堆栈的“顶部”在这个例子的底部):
Parameter #N
...
Parameter 2
Parameter 1
Return Address <--- (%esp)
函数的每个参数都被压入堆栈,最后返回地址就在那里。 现在函数本身有一些工作要做。
它所做的第一件事是通过执行pushl%ebp来保存当前的基址指针寄存器%ebp。 基指针是用于访问函数参数和局部变量的特殊寄存器。 接下来,它通过执行movl%esp,%ebp将堆栈指针复制到%ebp。 这允许您可以从基指针访问函数参数作为固定索引。 你可能会认为你可以使用这个堆栈指针。 然而,在你的程序中,你可能会对堆栈做其他事情,比如将参数传递给其他函数。 将堆栈指针复制到函数开头的基址指针中,可以让您始终知道参数的位置(以及我们将看到的局部变量),即使您可能正在将堆栈中的东西打开或关闭。 %ebp将始终是堆栈指针位于函数开头的位置,因此它或多或少是对堆栈帧的常量引用(堆栈帧由函数中使用的所有堆栈变量组成,包括参数,本地变量和返回地址)。
在这一点上,堆栈看起来像这样:
Parameter #N <--- N*4+4(%ebp)
...
Parameter 2 <--- 12(%ebp)
Parameter 1 <--- 8(%ebp)
Return Address <--- 4(%ebp)
Old %ebp <--- (%esp) and (%ebp)
如您所见,可以使用%ebp寄存器使用基址指针寻址模式访问每个参数。
我可以在第二段后简要介绍作者想要告诉的内容。 我明显在%esp,%ebp寄存器和%ebp在这里工作之间混淆。 任何帮助,高度赞赏。
在第二个图中,有“旧的%ebp <---(%esp)和(%ebp)”。 这是什么意思 ?
这意味着保存的%ebp
值(函数保存/恢复的调用者的值)由%esp
和此时的新%ebp
指向。
您只需运行push %ebp
,即esp -= 4
并将%ebp
存储到(%esp)
。 这样可以节省调用者的%ebp
以便以后恢复。
然后,你跑mov %esp, %ebp
成立%ebp
作为帧指针。 所以%ebp = %esp
,他们都指着你推的最后一件事。
上一篇: How do function calls work in x86 32
下一篇: Behaviour of EBP and ESP in stack during 2 function calls