操作系統之彙編語言(2)

現在,讓我們看看程序調用過程中彙編操作,一個過程調用包括將數據(以過程參數和返回值的形式), 和控制從代碼的一部分傳遞到令一部分。另外,它還必須在進入是爲過程的局部變量分配空間,並在退出時釋放這些空間。

棧幀結構
IA32程序用程序棧來支持過程調用。機器用棧來傳遞過程參數,存儲返回信息,保存寄存器用於以後恢復,以及本地存儲,爲整個過程分配的那部分棧稱爲棧幀(stack frame)。最頂端的棧幀以兩個指針界定,寄存器%ebp爲幀指針,而寄存器%esp爲棧指針。當程序執行時,棧指針是可以移動的,因此絕大多數信息的訪問都是相對於幀指針的。

這裏寫圖片描述

轉移控制
call指令:其效果是將返回值地址入棧,並跳轉到被調用過程的起始處。返回地址是在程序中緊跟在call後面的那條指令的地址。這樣當被調用函數返回時,執行會從此處繼續。ret指令從棧中彈出地址,並跳轉到這個位置。例如下面代碼:

int accum = 0;  
int sum(int x,int y);  
int main()  
{  
    return sum(1.3);  
}  
int sum(int x,int y)  
{  
    int t = x + y;  
    accum += t;  
    return t;  
}  
經過反彙編後,節選處call部分的代碼如下圖所示:

這裏寫圖片描述

在main函數中我們可以看到,在main函數中,地址爲0x080483dc的call指令調用函數sum,指明瞭棧指針%esp和程序計數器%eip的值。我們可以看到在main函數中,地址0x080483dc的下一個執行地址是是0x080483e1,這一點很重要。call指令的效果是將返回地址0x080483e1壓入棧中,並跳到函數sum的第一條指令,地址爲0x8048394。函數sum繼續執行,直到遇到地址爲0x080483a4的ret指令。這條指令從棧中彈出值0x080483e1,然後跳轉到這個地址,就在調用sum的call函數之後,繼續main函數的執行。

寄存器使用示例

int swap_add(int* xp,int* yp);  
int caller()  
{  
    int arg1 = 534;  
    int arg2 = 1057;  
    int sum = swap_add(&arg1 , &arg2);  
    int diff = arg1 - arg2;  

    retur sum * diff;  
}  
int swap_add(int* xp,int* yp)  
{  
    int x = * xp;  
    int y = * yp;  
    *xp = y;  
    *yp = x;  
    return x + y;  
}  

我們先來看看caller函數調用swap_add正在運行時的棧幀結構。有些指令訪問的棧位置是相對於棧指針%esp的,而另一些的訪問的棧位置是相對於基地址指針%ebp的。

這裏寫圖片描述

caller:
      push1 %ebp
      movl %esp,%ebp
      sub1 $24, %esp
      movl $534,-4(%ebp)
      movl $1057,-8(%ebp)
      leal -8(%ebp),%eax   //compute &arg2
      movl %eax,4(%esp)
      leal -4(%ebp),%eax   //compute &arg1
      movl %eax,(%esp)
      call swap_add

我們可以看到,在調用swap_add之前,我們用push1指令把%ebp的數據壓入棧,然後爲這個棧分配24字節。caller的幀棧包括局部變量arg1和arg2的存儲,其位置相對於幀指針是-4和-8。這些變量必須存在棧中,因爲我們必須爲他們生成地址。
在這裏我們可以看到分配給棧幀的24個字節中,8個用於局部變量,8個用於向swap_add傳遞參數,還有8個未使用。因爲GCC堅持一個x86編程指導方針,也就是一個函數使用的所有棧空間必須是16字節的整數倍,包括保存%ebp值的4個字節和返回值的4個字節,caller一共使用了32個字節,採用這個規則是爲了保證訪問數據的嚴格對齊。

發佈了82 篇原創文章 · 獲贊 11 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章