从的上下文看这个函数调用那个函数。它执行的代码,以便看起来像:
caller+...: push argN
caller+...: ...
caller+...: push arg0
caller+...: call func
即参数被放入堆栈在这样的顺序是,在进入func()
,堆栈将具有以下布局:
[esp+(N*4)] : argN
... : arg(N-1)...arg2
[esp+4 ] : arg1
[esp ] : <return address to caller()+...>
然后您执行push %ebp; mov %esp, %ebp
序列,改变%esp
(由-4),以使您的布局现在是:
[ebp+4+(N*4)][esp+(N*4)] : argN
... : arg(N-1)...arg2
[ ebp+8 ][esp+8 ] : arg1
[ ebp+4 ][esp+4 ] : <return address to caller()+...>
[ ebp ][esp ] : <saved %ebp of caller>
代码然后继续推动堆栈上的更多寄存器 - 因为每次%esp
被更改为-4时向下增长。最终(你没有在你的反汇编中显示,但它会在那里),你会得到一条指令subl $..., %esp
。这就是为你的局部变量分配空间的原因。最终的堆栈布局是一样的东西:
[ebp+4+(N*4)][ ] : argN
... : arg(N-1)...arg2
[ ebp+8 ][ ] : arg1
[ ebp+4 ][ ] : <return address to caller()+...>
[ ebp ][ ] : <saved %ebp of caller>
[ ebp-4 ][ ] : <saved %ebx of caller>
[ ebp-8 ][ ] : ...
... : region for local variables
[ ebp-?? ][ esp ] : end of stack for func()
[esp ... ebp-4]
之间的任何地址内什么要求你的函数的栈帧,它包含无论是在的情况下保存代表调用(如ebx
的寄存器反汇编显示你)或局部变量。
因此,如果您在代码中看到对%ebp - XX
的任何访问权限,那么它位于本地变量空间中,如果您看到%ebp + YY
它位于包含函数参数的空间内。
他们这样做的原因是,参数总是可以被相同的ebp偏移量引用。您不能使用esp来达到此目的,因为此功能可能需要使用堆栈指针来执行临时推/拉指令。 – 2010-10-14 22:38:40