《組合語言》王爽,學習筆記(子程式呼叫)

weixin_33912445發表於2016-04-01

在棧的那裡已經簡單提到了子程式的呼叫的實質,就是修改CS:IP,在這裡就詳細介紹一下子程式呼叫的過程。

1.call,ret,retf指令

call,ret和retf是彙編為子程式呼叫而專門設計的彙編指令,CPU在執行ret和retf指令時,利用的棧中的資料,ret只修改IP的值,實現近轉移(段內),而retf修改CS和IP的值(預設CS存在棧中的高位地址),實現遠轉移(段間)。

CPU在執行ret指令時,進行下面操作,用匯編語法解釋就是:

pop IP                          ;將棧頂元素的值賦值給IP,把棧頂向下移動兩個位元組(刪除原棧頂)

而CPU在執行retf指令時,進行下面操作,用匯編語法解釋就是:

pop IP                          ;把棧頂元素值賦值給IP,把棧頂向下移動兩位元組

pop CS                         ;把新棧頂元素複製給CS,再次將棧頂向下移動兩位元組

CPU在執行call指令時,進行兩步操作,(1)將當前的IP或CS與IP壓入棧中,(2)jmp標號處

call實現轉移的方法和jmp原理相同,call分為兩種(不含短轉移,因為call操作需要用到棧,而對棧的操作是預設16位的),近轉移,遠轉移。

call  標號                       ;相當於執行push  IP,jmp near prt 標號

call far prt 標號             ;相當於執行push CS,push IP,jmp far prt 標號

體會一下call和ret指令利用棧的理由,在思考一下C語言中的遞迴演算法,是不是能深刻了解遞迴中函式的返回過程了?不斷call函式,然後就把IP不斷壓入棧中,最後返回時,不斷popIP,就完成了逐級返回。

2.暫存器衝突

在這之前,已經提到過暫存器衝突的問題了(CX的迴圈問題),現在介紹的是一種標準的呼叫函式的過程。

因為我們CPU中的暫存器是有限的,所以你的子函式也難免會利用到你父函式中使用的暫存器,那麼為了解決這類問題,組合語言在編寫子函式一般使用以下框架:

(1)子程式開始:將父程式的所有暫存器入棧。

(2)子程式內容

(3)子程式結束:將暫存器出棧恢復

(4)ret,retf返回

因為利用到了棧,所以子程式中改變的暫存器的值並不會影響到父程式(除非你在子程式中直接修改棧中內容),這也就更加深刻的解釋了C語言中傳參的問題,為什麼實參無法影響到形參。

日後如果碰到彙編程式一開頭就在不斷push一些暫存器,那麼這接下來的程式就很可能是子函式了。

相關文章