问题描述
我知道嵌套函数调用中的数据会进入堆栈.堆栈本身实现了一种分步方法,用于在函数被调用或返回时从堆栈中存储和检索数据.这些方法的名称最为人所知作为序言和结语.
I know data in nested function calls go to the Stack.The stack itself implements a step-by-step method for storing and retrieving data from the stack as the functions get called or returns.The name of these methods is most known as Prologue and Epilogue.
我尝试搜索有关此主题的材料,但没有成功.你们知道关于函数序言和结语在 C 中通常如何工作的任何资源(站点、视频、文章)吗?或者如果你能解释一下会更好.
I tried with no success to search material on this topic. Do you guys know any resource ( site,video, article ) about how function prologue and epilogue works generally in C ? Or if you can explain would be even better.
P.S : 我只是想要一些大致的看法,不要太详细.
P.S : I just want some general view, not too detailed.
推荐答案
有很多资源可以解释这一点:
There are lots of resources out there that explain this:
- 函数序言(维基百科)
- x86 反汇编/调用约定(维基教科书)
- 编写 Prolog 的注意事项/结语代码 (MSDN)
仅举几例.
基本上,正如您所描述的,堆栈"是指在程序的执行中有几个目的:
Basically, as you somewhat described, "the stack" serves several purposes in the execution of a program:
- 在调用函数时跟踪返回的位置
- 在函数调用的上下文中存储局部变量
- 将参数从调用函数传递给被调用者.
序言是在函数开始时发生的事情.它的职责是设置被调用函数的堆栈框架.Epilog 正好相反:它是函数中最后发生的事情,其目的是恢复调用(父)函数的堆栈帧.
The prolouge is what happens at the beginning of a function. Its responsibility is to set up the stack frame of the called function. The epilog is the exact opposite: it is what happens last in a function, and its purpose is to restore the stack frame of the calling (parent) function.
在 IA-32 (x86) cdecl 中,语言使用 ebp
寄存器来跟踪函数的堆栈帧.esp
寄存器被处理器用来指向堆栈中最近添加的值(顶部值).(在优化的代码中,使用 ebp
作为帧指针是可选的;其他方法可以为异常展开堆栈,因此实际上不需要花费指令来设置它.)
In IA-32 (x86) cdecl, the ebp
register is used by the language to keep track of the function's stack frame. The esp
register is used by the processor to point to the most recent addition (the top value) on the stack. (In optimized code, using ebp
as a frame pointer is optional; other ways of unwinding the stack for exceptions are possible, so there's no actual requirement to spend instructions setting it up.)
call
指令做了两件事:首先将返回地址压入堆栈,然后跳转到被调用的函数.在 call
之后,esp
立即指向堆栈上的返回地址.(所以在函数入口处,事情被设置,以便 ret
可以执行以将该返回地址弹出回 EIP.序言将 ESP 指向其他地方,这就是为什么我们需要一个结尾的部分原因.)
The call
instruction does two things: First it pushes the return address onto the stack, then it jumps to the function being called. Immediately after the call
, esp
points to the return address on the stack. (So on function entry, things are set up so a ret
could execute to pop that return address back into EIP. The prologue points ESP somewhere else, which is part of why we need an epilogue.)
然后执行序言:
push ebp ; Save the stack-frame base pointer (of the calling function).
mov ebp, esp ; Set the stack-frame base pointer to be the current
; location on the stack.
sub esp, N ; Grow the stack by N bytes to reserve space for local variables
此时,我们有:
...
ebp + 4: Return address
ebp + 0: Calling function's old ebp value
ebp - 4: (local variables)
...
结语:
mov esp, ebp ; Put the stack pointer back where it was when this function
; was called.
pop ebp ; Restore the calling function's stack frame.
ret ; Return to the calling function.
这篇关于C 中的函数 Prologue 和 Epilogue的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!