x86寄存器使用的疑惑?

計算機愛好者一枚,非科班出身。最近在研究 x86 體系結構和函數調用約定,對 x86 的寄存器使用產生了一些疑惑。測試C代碼如下:(VS 2013)

反彙編後的部分main函數代碼:

sum函數的反彙編代碼:

我的問題是:

1、main函數的反彙編代碼中,為什麼要一開始就留出0xF0這麼大的空間,並將其中的0x3C大小的塊初始化為0xCCCCCCCC?感覺完全沒有必要啊。

2、main函數和sum函數都沒有使用EBX寄存器,為什麼都不約而同的在函數入口將其壓棧?其目的何在?

3、sum函數中使用了ECX寄存器,但是為什麼沒有將其壓棧?雖然我們知道sum函數的調用者(即main函數)沒有使用ECX寄存器存儲有意義的數據,但是sum函數是怎麼知道這一事實的呢?假設main函數使用ECX存儲了重要數據,那麼該如何保證調用完sum後ECX寄存器能夠恢復?是調用sum前main函數將ECX壓棧還是調用後sum函數在入口處壓棧?

4、假設在一個對效率要求極高的環境下,sum函數必須用彙編實現,也就是在main中嵌入彙編語言,那麼作為程序員,如何知道此時編譯器對寄存器做了什麼樣的分配能?也就是說,當我用彙編語言實現sum函數時,我如何得知哪些寄存器可用,不需壓棧保存,哪些寄存器已經被main使用,必須先壓棧保存能?

以上問題百思不得其解,望大家指教,謝謝。


1、main函數的反彙編代碼中,為什麼要一開始就留出0xF0這麼大的空間,並將其中的0x3C大小的塊初始化為0xCCCCCCCC?感覺完全沒有必要啊。

棧保護的東西,防止你把棧寫飛了,某些版本的VC里在函數結束的時候會調_RTC_CheckStackVars去檢查棧上數據是否正常,寫0xCC是因為這對應彙編指令是int 3,會觸發調試中斷,同時正常數值很少有這個值。

2、main函數和sum函數都沒有使用EBX寄存器,為什麼都不約而同的在函數入口將其壓棧?其目的何在?

ebx, esi, edi是被調用者保留的寄存器,具體寄存器使用可以參見這裡:X86彙編快速入門

至於為什麼把一個沒用到的寄存器也保存了,這是因為debug模式下,編譯器按照最差的方式進行編譯,不管你用不用寄存器,都入棧保存。

3、
sum函數中使用了ECX寄存器,但是為什麼沒有將其壓棧?雖然我們知道sum函數的調用者(即main函數)沒有使用ECX寄存器存儲有意義的數據,但
是sum函數是怎麼知道這一事實的呢?假設main函數使用ECX存儲了重要數據,那麼該如何保證調用完sum後ECX寄存器能夠恢復?是調用sum前
main函數將ECX壓棧還是調用後sum函數在入口處壓棧?

同上,是調用者還是被調用者保存,都是有規定的。

4、假設在一個對效率要求極高的環境下,sum函數必須用彙編實現,也就是在
main中嵌入彙編語言,那麼作為程序員,如何知道此時編譯器對寄存器做了什麼樣的分配能?也就是說,當我用彙編語言實現sum函數時,我如何得知哪些寄
存器可用,不需壓棧保存,哪些寄存器已經被main使用,必須先壓棧保存能?

同上。

問題1和問題2其實是優化的問題,你編譯成release模式,這些就都不存在了。

問題3和問題4是寄存器保存和恢復的問題,eax ecx edx是調用者保存,ebx esi edi是被調用者保存。如果你要嵌入彙編,最安全的辦法是在彙編入口保存你用到的所有的寄存器,因為編譯器本身可能做優化。


calling convention中規定了哪些寄存器是 caller saved,哪些是 callee saved,對於前者,如果caller認為某個寄存器的內容被callee修改無所謂,它可以選擇不入棧,相反callee必須保證自己負責的寄存器一定可以恢復,除非自己確認用不到那個寄存器。


推薦閱讀:

聯想為何要收購 IBM 的 X86 伺服器業務?

TAG:x86 | 編譯器 | 寄存器 |