c++棧調用的彙編研究

<!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } -->

先寫一段程序,用的編輯環境爲 Netbeans 6.9 gcc gdb

int main(int argc, char** argv) {

int i=1;

i++;

return (EXIT_SUCCESS);

}

 

編譯爲彙編指令爲

 

!int main(int argc, char** argv) {

main+0: push %ebp

main+1: mov %esp,%ebp

main+3: sub $0x10,%esp

! int i=1;

main()

main+6: movl $0x1,-0x4(%ebp)

! i++;

main+13: addl $0x1,-0x4(%ebp)

! return (EXIT_SUCCESS);

main+17: mov $0x0,%eax

!}

main+22: leave

main+23: ret

 

!應該爲註釋,標記正在進行到代碼的哪一個步驟

 

main+0: push %ebp

ebp是基址指針寄存器。這裏應該是說將原來的 ebp壓入棧,這裏的 ebp應該是作爲一個棧的基地址而從棧裏面取地址就要用 ebp做基準,所以這裏存入上一級棧的 ebp

 

看: main+1: mov %esp,%ebp

這裏把 esp的值傳給 ebp,也就是說 ebp現在可以當作目前這一級棧的基地址,而因爲 esp經常改變,所以要用 ebp來做基址。

 

main+3: sub $0x10,%esp

這裏因爲有一個局部變量 i,所以要把 esp向上調,不知道 esp是不是就是指棧頂的意思?這裏貌似還要 RoundUp變到 16 的倍數,所以總是 0xn0。如果有 5個局部變量,就會移動 x020。(這裏還是有問題,見下一個例子)

 

main()

 

main+6: movl $0x1,-0x4(%ebp)

這裏可以看到將 1付給 i。這裏用到了偏址 4,得到 i的指,這裏很奇怪爲什麼 i在那個位置。注意這裏只有當運行到這一個語句的時候纔會將 i的值壓棧,而 esp會預留全部的局部變量的空間。

 

main+13: addl $0x1,-0x4(%ebp)

這裏將 i的值加上 1

 

main+17: mov $0x0,%eax

這裏將 0付給 eax寄存器,可見這裏 eax是存儲返回值的寄存器。

 

看: main+22: leave

main+23: ret

這裏應該是函數返回。

 

如果有一個語句爲 j=i+j

則對應於彙編爲

main+24: mov -0x4(%ebp),%eax// i的值付給 eax

main+27: add %eax,-0x8(%ebp) // eax付給 j。可見這裏 eax也是可以作爲臨時寄存器。

 

 

繼續編一段程序:

int add(int i,int p){

int j=i+p;

return j;

}

int main(int argc, char** argv) {

int i=1;

 

i=add(i,8);

 

return (i);

}

 

 

main+0: push %ebp

main+1: mov %esp,%ebp

main+3: sub $0x18,%esp

! int i=1;

main+6: movl $0x1,-0x4(%ebp)

!

! i=add(i,8);

main+13: movl $0x8,0x4(%esp)

main+21: mov -0x4(%ebp),%eax

main+24: mov %eax,(%esp)

main+27: call 0x80483b4 <add>

main()

main+32: mov %eax,-0x4(%ebp)

!

! return (i);

main+35: mov -0x4(%ebp),%eax

!}

main+38: leave

main+39: ret

 

!int add(int i,int p){

add()

add+0: push %ebp

add+1: mov %esp,%ebp

add+3: sub $0x10,%esp

! int j=i+p;

add+6: mov 0xc(%ebp),%eax

add+9: mov 0x8(%ebp),%edx

add+12: lea (%edx,%eax,1),%eax

add+15: mov %eax,-0x4(%ebp)

! return j;

add+18: mov -0x4(%ebp),%eax

!}

add+21: leave

add+22: ret

 

main+13: movl $0x8,0x4(%esp)

main+21: mov -0x4(%ebp),%eax

main+24: mov %eax,(%esp)

這裏其實就是把參數壓棧,把 8 i壓進去。

 

main+27: call 0x80483b4 <add>

這裏調用 add函數。

add+0: push %ebp

add+1: mov %esp,%ebp

add+3: sub $0x10,%esp

還是和 main函數開頭一樣的操作。存儲舊的 ebp值壓棧。這裏剛開頭的時候其實 esp的值已經變掉了從 0x560變到了 0x558,這裏應該是 call指令完成的。

 

add+6: mov 0xc(%ebp),%eax

add+9: mov 0x8(%ebp),%edx

add+12: lea (%edx,%eax,1),%eax

add+15: mov %eax,-0x4(%ebp)

這裏計算那個表達式的值。

最後返回。

 

這裏我把棧的圖畫一下。

 

esp 0x548

 

 

0xbf899554 j

ebp 0xbf899558 0xbf899578(上一級的 ebp )

0xbf89955c 0x8048431(這裏己有可能是調用着執行 call時的下一條語句)

esp 0xbf899560 i

0xbf899564 8

 

 

 

0xbf899574 i

esp 0xbf899578 上一級的 ebp

 

但是如果我把 add函數寫成這樣

int add(int i,int p){

int j=1;

printf("i:%p\n",&i);

printf("p:%p\n",&p);

printf("j:%p\n",&j);

return j;

}

 

結果是:

i:0xbffff550

p:0xbffff554

j:0xbffff53c

 

發現局部變量和函數參數之間有四個空即 16個字節。發現其實 j並不是壓在 ebp上一格的,而是 (ebp+12),這個很詭異。所以會出現在這種情況,我原來還以爲是局部變量與參數間空 16個字節放各種 pointer以及返回值這些東西呢,這個實在是太詭異了。

 

宗其所述:正常情況下,局部變量與參數間空了 8個字節(linux下)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章