栈帧
-
什么是栈帧
- 栈帧是在程序的运行时栈中分配的内存块,专门用于特定的函数调用。
-
栈与函数调用
- 系统栈在函数调用时的变化:main->A->B->A->main
- 在main函数调用func_A的时候,首先在自己的栈帧中压入函数返回地址,然后func_A创建新栈帧并压入系统栈。
- 在func_A调用func_B时,同样先在自己的栈帧中压入函数返回地址,然后为func_B创建新栈帧并压入系统栈。
- 在func_B返回时,func_B的栈帧被弹出系统栈,func_A的返回地址被露在栈顶,此时处理器按照这个返回地址重新跳到 func_A代码区。
- 在func_A返回时,func_A的栈帧弹出系统栈,main的返回地址被露出栈顶,此时处理器按这个地址重新返回main函数。
- 系统栈在函数调用时的变化:main->A->B->A->main
-
函数调用的详细操作步骤
- 调用方将被调用函数所需的任何参数放入到该函数所采用的调用约定指定的位置。如果参数被放到运行时栈上,该操作可能导致程序的栈帧发生改变。
- 调用方将控制权转交给被调用的函数,这个过程常又x86CALL或MIPSJAL等指令执行。然后,返回地址被保存到程序栈或CPU寄存器中。
- 如有必要,被调用的函数会配置一个栈指针,并保存调用方希望保持不变的任何寄存器值。
- 被调用的函数为它可能需要的任何局部变量分配空间。一般,通过调整程序栈指针在运行时保留空间来完成这一任务。
- 被调用的函数执行其操作,可能产生一个结果。在执行操作的过程中,被调用的函数可能会访问调用函数传递给它的参数。如果参数返回一个结果,此结果通常被放置到一个特定的寄存器中,或者放置到函数返回后调用方可立即访问的寄存器。
- 函数完成其操作后,任何为局部变量保留的栈空间将被释放。通常,逆向执行第4步中的操作,即可完成这个任务。
- 如果某个寄存器的值还为调用方保存(第三步)着,那么将其恢复到原始值。这包括恢复调用方的帧指针寄存器。
- 被调用的函数将控制权返还给调用方。实现这一操作主要指令包括x86RET和MIPS JR。根据所使用的调用约定,这一操作可能还会从程序栈中清楚一个或多个参数。
- 调用方一旦重新获得控制权,它可能需要删除程序栈中的参数。这时可能需要对栈进行调整,以将程序栈指针恢复到第1步以前的值。