x86_64彙編調用過程簡介

過程是軟件中一種很重要的抽象。

它提供了一種封裝代碼的方式,用一組指定的參數和一個可選的返回值實現了某種功能。
設計良好的軟件用過程作爲抽象機制,隱藏某個行爲的具體實現,同時又提供清晰簡潔的接口定義,說明要計算的是哪些值,過程會對程序狀態產生什麼樣的影響。
不同編程語言中,過程的形式多樣:函數,方法,子例程,處理函數等。
假設過程P調用過程Q,Q執行後返回到P。過程的實現需要使用下面三個機制。

  • 轉移控制。在進入過程Q的時候,程序計數器必須被設置爲Q的代碼的起始地址,然後在返回時,要把程序計數器設置爲P中調用Q後面那條指令的地址。
  • 傳遞數據。P必須能夠向Q提供一個或多個參數,Q必須能夠向P返回一個值。
  • 分配和釋放內存。在開始時,Q可能需要爲局部變量分配空間,而在返回前,又必須釋放這些存儲空間。
轉移控制:轉移控制是通過call指令以及ret指令來實現的。

call指令會把地址A壓入棧中,並將PC設置爲Q的起始地址。壓入棧中的地址A被稱爲返回地址,是緊跟在call指令後面的那條指令的地址。
ret指令會從棧中彈出地址A,並把PC設置爲A。

傳遞數據:數據可以通過寄存器和棧來傳遞。

x86_64中,可以通過寄存器最多傳遞6個整形參數。按照參數的順序分別爲寄存器%rdi,%rsi,%rdx,%rcx,%r8,%r9。
如果一個函數有大於6個整形參數,超出6個的部分就要通過棧來傳遞。通過棧傳遞參數時,所有的數據大小都向8的倍數對齊。

分配和釋放內存:局部存儲分爲棧上的局部存儲以及寄存器中的局部存儲。

棧上的局部存儲:
有些時候局部數據必須存放在內存中,常見的情況包括:

  • 寄存器不足夠存放所有的本地數據。
  • 對一個局部變量使用地址運算符&,因此必須能夠爲它產生一個地址。
  • 某些局部變量是數組或結構,因此必須能夠通過數組或結構引用被訪問到。

寄存器中的局部存儲:
寄存器組是唯一被所有過程共享的資源。雖然在給定時刻只有一個過程是活動的,我們仍然必須確保當一個過程(調用者)調用另一個過程(被調用)時,被調用者不會覆蓋調用者稍後會使用的寄存器值。
爲此,x86_64採用了一組統一的寄存器使用慣例,所有的過程都必須遵循。

根據慣例,寄存器%rbx,%rbp和%r12~%r15被劃分爲調用者保存寄存器
當過程P調用過程Q時,Q必須保存這些寄存器的值,保證它們的值在Q返回到P時與Q被調用時是一樣的。
過程Q保存一個寄存器的值不變,要麼就是根本不去改變它,要麼就是把原始值壓入棧中,改變寄存器的值,然後在返回前從棧中彈出舊值。壓入寄存器的值會在棧幀中創建標號爲保存的寄存器的一部分。

所有其他的寄存器,除了棧指針%rsp,都被分類爲調用者保存寄存器。這就意味着任何函數都能修改它們。可以這樣來理解調用者保存這個名字:過程P在某個此類寄存器中有局部數據,然後調用過程Q。因爲Q可以隨意修改這個寄存器,所有在調用之前首先保存好這個數據是P(調用者)的責任。

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