BP_scratch那段代碼

CALL做了什麼

那麼一條CALL指令做了什麼事情呢?它做的就是對CPU執行指令所需要的充要條件相關因素進行處理,從而保證下一條指令能夠正確執行。CALL指令執行需要知道下一步調用的函數的地址(最簡單跳轉指令JMP需要知道的東東),而在它將CPU執行點給下一步需要執行的函數之前,需要先保存現有執行點的一些信息,最簡單的就是CS、EIP和ESP寄存器(自然的,也就是遵守調用約定的,函數調用ESP由調用函數自動計算,可以不存儲)。

diyblPic

圖 2 執行CALL之前的寄存器和內存使用情況

假定CPU現有代碼執行至004014A0時各寄存器的值如下:ESP(008B0010),EIP(004014A0),CS(0000)。如圖2所示。爲了方便描述,這裏假定各條指令的長度都是4字節(遠調用爲8字節)。

那麼執行CALL 004032A0後的各寄存器的值如下:ESP(008B000C),EIP(004032A0),CS(0000)。如圖3所示。執行一次近調用後CALL指令會將EIP入棧,並同時更新ESP和EIP的值,由於是近調用,CS寄存器的值不變。

 diyblPic

圖 3 執行near CALL之後的寄存器和內存使用情況

同理,執行完CALL 0300: 004032A0後的各寄存器值如下:ESP(008B0008),EIP(004032A0),CS(0300)。如圖4所示。執行一次遠調用後CALL指令會將CS和EIP依次入棧,並同時更新ESP、EIP和CS的值,下一條指令的執行地址由最新的EIP和CS計算出來。

 diyblPic

圖 4 執行FAR CALL之後的寄存器和內存使用情況

4       RET幹了些啥?

diyblPic

圖 5 執行RET之後的寄存器和內存使用情況

與CALL指令相對的,RET指令主要進行出棧操作,並更新相應的寄存器。出棧指令RET有四種形式。

(1)       RET。可能是近返回也可能是遠返回。

(2)       RETN。顯式指定近返回。

(3)       RETF。顯式指定遠返回。

(4)       RET N。同(1),不過ESP另外減去N字節。

圖5中給出了最簡單的一種調用RET且爲近返回調用後寄存器和內存的使用情況。


  /*
   * Calculate the delta between where we were compiled to run
   * at and where we were actually loaded at. This can only be done
   * with a short local call on x86. Nothing else will tell us what
   * address we are running at. The reserved chunk of the real-mode
   * data at 0x1e4 (defined as a scratch field) are used as the stack
   * for this calculation. Only 4 bytes are needed.
   */
    //計算編譯時的連接地址與實際加載地址之間的差值。。。。計算時使用實模式數據段偏移在0x1e4
    //處的地址作爲棧,這次計算只需要4個字節的棧就可以了!這裏很是費解,把boot_params->scratch
    //作爲堆棧使用。
    //將前邊操作數的偏移地址賦值給esp,前邊參數的地址爲ds:(  (BP_scratch+4)(%esi) ),那他的偏移就是
    //(BP_scratch+4)(%esi),%esi裏放的是boot_params。
    leal (BP_scratch+4)(%esi), %esp
    call 1f //根據前面對call的解釋,知道,這裏call先將1處的加載地址eip+4放入堆棧
1 : popl %ebp //ebp就是1:的實際地址
    subl $1b, %ebp //這裏$1b是1處的連接地址,因爲$1b是個標號,標號表示的都是連接地址

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