讀書筆記_CALL和RET指令
CALL指令是x86CPU中專門用作函式呼叫的指令,它的作用就是將當前的程式指標(EIP暫存器)值儲存到棧中(稱為linking information),然後轉移到(branch to)目標運算元所指定的函式(被呼叫過程)繼續執行。
根據被呼叫過程是否位於同一個程式碼段,CALL呼叫被分為近呼叫(Near Call)和遠呼叫(Far Call)兩種。在近呼叫中CPU的操作如下:
A. 將EIP暫存器的當前值壓入到棧中供返回時使用
B. 將被呼叫過程的偏移(相對於當前段)載入到EIP暫存器中
C. 開始執行被呼叫過程
對於遠呼叫,CPU執行的操作如下:
A. 將CS暫存器的當前值壓入到棧中供返回時使用
B. 將EIP暫存器的當前值壓入到棧中返回時使用
C. 將包含被呼叫過程的程式碼段的段選擇子載入到CS暫存器
D. 被呼叫過程的偏移載入到EIP暫存器
E. 開始執行被呼叫過程
可以看出近呼叫和遠呼叫的差異在於是否處理段暫存器,因為近呼叫發生在一個程式碼內的呼叫,因此不需要向棧中壓入和切換程式碼段,而遠呼叫因為發生在不同的程式碼段間,因此需要儲存和切換程式碼段。但對於NT系列的windows,因為使用了平坦記憶體,在同一程序內的程式碼都是在一個大的4GB段中,因此不必再考慮段的差異,幾乎所有時候使用的都是近呼叫。
下面來看RET指令,RET指令用於從被呼叫過程返回到發起呼叫的過程,RET指令可以有一個可選的引數n,用於指定ESP暫存器要遞增的位元組數,ESP遞增n個位元組相當於從棧中彈出n個位元組,經常用來釋放壓在棧上的引數。相對於近呼叫的返回稱為近返回(Near Return),相對於遠呼叫的被稱為遠返回(Far Return)。
對於近返回,CPU所執行的操作如下:
1. 將位於棧頂的資料彈出到EIP暫存器,這個值應該是發起近呼叫時CALL指令壓入的返回地址。
2. 如果RET指令包含引數n,那麼便將ESP暫存器的位元組數遞增n。
3. 繼續執行程式指標所指向的指令,通常就是父函式中呼叫指令的下一條指令。
而對於遠返回,在第一步和第二步之間,CPU會彈出執行遠呼叫時壓入的CS暫存器。所以,RET指令只是單純地返回到執行這條指令時棧頂所儲存的地址,如果棧暫存器(ESP)沒有指向合適的位置或棧上的地址被破壞了,那麼RET指令就返回到其他地方。