Spa*_*rky 138
每个例程都使用堆栈的一部分,我们将其称为堆栈帧.尽管汇编程序员不必遵循以下风格,但强烈建议将其作为良好实践.
每个例程的堆栈帧分为三个部分:函数参数,指向前一个堆栈帧的后向指针和局部变量.
第1部分:功能参数
例程的堆栈帧的这一部分由调用者设置.使用'push'指令,调用者将参数压入堆栈.不同的语言可能会以不同的顺序推动参数.C,如果我没记错的话,将它们从右向左推.也就是说,如果你打电话......
foo (a, b, c);
Run Code Online (Sandbox Code Playgroud)
调用者将此转换为...
push c
push b
push a
call foo
Run Code Online (Sandbox Code Playgroud)
当每个项目被推入堆栈时,堆栈会向下扩展.也就是说,堆栈指针寄存器递减四(4)个字节(在32位模式下),并且该项被复制到堆栈指针寄存器指向的存储器位置.请注意,'call'指令将隐式地推送堆栈上的返回地址.第5部分将讨论参数的清理.
第2部分:Stackframe后退指针
此时,已发出'call'指令,我们现在处于被调用例程的开始处.如果我们想访问我们的参数,我们可以访问它们,如...
[esp + 0] - return address
[esp + 4] - parameter 'a'
[esp + 8] - parameter 'b'
[esp + 12] - parameter 'c'
Run Code Online (Sandbox Code Playgroud)
However, this can get clumsy after we carve out space for local variables and stuff. So, we use a stackbase-pointer register in addition to the stack-pointer register. However, we want the stackbase-pointer register to be set to our current frame, and not the previous function. Thus, we save the old one on the stack (which modifies the offsets of the parameters on the stack) and then copy the current stack-pointer register to the stackbase-pointer register.
push ebp ; save previous stackbase-pointer register
mov ebp, esp ; ebp = esp
Run Code Online (Sandbox Code Playgroud)
Sometimes you may see this done using only the 'ENTER' instruction.
Part 3: Carving space for local variables
Local variables get stored on the stack. Since the stack grows down, we subtract some # of bytes (enough to store our local variables):
sub esp, n_bytes ; n_bytes = number of bytes required for local variables
Part 4: Putting it all together. Parameters are accessed using the stackbase-pointer register ...
[ebp + 16] - parameter 'c'
[ebp + 12] - parameter 'b'
[ebp + 8] - parameter 'a'
[ebp + 4] - return address
[ebp + 0] - saved stackbase-pointer register
Run Code Online (Sandbox Code Playgroud)
使用堆栈指针寄存器访问局部变量...
[esp + (# - 4)] - top of local variables section
[esp + 0] - bottom of local variables section
Run Code Online (Sandbox Code Playgroud)
第5部分:Stackframe清理
当我们离开例程时,必须清理堆栈帧.
mov esp, ebp ; undo the carving of space for the local variables
pop ebp ; restore the previous stackbase-pointer register
Run Code Online (Sandbox Code Playgroud)
有时您可能会看到"LEAVE"指令替换这两条指令.
根据您使用的语言,您可能会看到"RET"指令的两种形式之一.
ret
ret <some #>
Run Code Online (Sandbox Code Playgroud)
Whichever is chosen will depend upon the choice of language (or style you wish to follow if writing in assembler). The first case indicates that the caller is responsible for removing the parameters from the stack (with the foo(a,b,c) example it will do so via ... add esp, 12) and it is the way 'C' does it. The second case indicates that the return instruction will pop # words (or # bytes, I can't remember which) off the stack when it returns, thus removing the parameters from the stack. If I remember correctly, this is style used by Pascal.
It's long, but I hope this helps you better understand stackframes.
Aby*_*byx 15
通过执行创建x86-32堆栈帧
function_start:
push ebp
mov ebp, esp
Run Code Online (Sandbox Code Playgroud)
所以它可以通过ebp访问,看起来像
ebp+00 (current_frame) : prev_frame
ebp+04 : return_address
....
prev_frame : prev_prev_frame
prev_frame+04 : prev_return_address
Run Code Online (Sandbox Code Playgroud)
通过汇编指令设计将堆栈帧用于ebp有一些优点,因此通常使用ebp寄存器访问参数和本地.
| 归档时间: |
|
| 查看次数: |
30267 次 |
| 最近记录: |