從的上下文看這個函數調用那個函數。它執行的代碼,以便看起來像:
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