匯編語言中的函數調用


C語言從原則上來說,只能在函數內執行代碼。
所以任何 text 段都對應有自己的幀棧。
本文主要談一下 call leave ret 三條與函數調用緊密相關的指令。

call 指令

call 的不同形式

call Label 所謂直接跳轉
call *operand 所謂間接跳轉

080483f7 <caller>: 804840c: e8 dc ff ff ff call 80483ed <callee>

上邊代碼段中 caller 中 call 80483ed <callee> 就是直接跳轉

call 之前的准備

080483f7 <caller>: 80483fa: 83 ec 08 sub $0x8,%esp 80483fd: c7 44 24 04 1c a0 04 movl $0x804a01c,0x4(%esp) 8048404: 08 8048405: c7 04 24 01 00 00 00 movl $0x1,(%esp)

gcc ABI約定被調函數的參數保存在調用者的棧幀(frame)上,所以 caller 需要將 callee 的參數放在自己的棧幀上。這個過程分兩步完成。

  • 開棧。
    將棧指針向下(棧由高位向下擴展)移動 8 bytes。這是因為兩個參數一個是指針類型,一個是整數類型,均需要 4 bytes 來存儲。事實上由於對齊的要求,即使參數類型小於 4 bytes 編譯器還是會為其分配 4 bytes 的棧空間,
  • 反向保存參數。
    gcc ABI規定,反向保存參數,故棧頂保存最后一個參數。如果參數類型大於 4 bytes,IA32 需要用兩條 movl 指令來傳遞參數。
    值得注意的是,ABI只規定了參數在棧上存儲的空間順序,並沒有規定參數壓入棧中的時間順序

call 干了什么

存儲返回地址。
call 指令將 (%eip) 對應指令之后的那條指令的起始地址放在棧上,也就是把 %eip + n 放在 (%esp),其中 n 為 (%eip) 中指令的長度。然后跳轉到 call 的操作數所指的地址。

call之后發生了什么

080483ed <callee>: 80483ed: 55 push %ebp // sub $0x4,%esp // mov %ebp,(%esp) 80483ee: 89 e5 mov %esp,%ebp 80483f0: 83 ec 2c sub $0x2c,%esp 8048405: c7 45 e8 01 00 00 00 movl $0x1,-0x18(%ebp) 804840c: c7 45 ec 02 00 00 00 movl $0x2,-0x14(%ebp) 8048413: c7 45 f0 03 00 00 00 movl $0x3,-0x10(%ebp) 804841a: c7 45 f4 04 00 00 00 movl $0x4,-0xc(%ebp) 8048421: c7 45 f8 05 00 00 00 movl $0x5,-0x8(%ebp) 8048428: c7 45 fc 06 00 00 00 movl $0x6,-0x4(%ebp)
  • 切換棧幀。
    被調函數首先將舊的棧底指針 %ebp 壓到自己的棧幀上,然后以其地址(而非內容)作為自己的棧底指針的內容,此時新的棧幀已經形成了,由於 %esp == %ebp,故新的棧幀暫時沒有使用棧內存。
  • 開棧。
    當局部變量數量太大時,編譯器會選擇將局部變量放在棧幀上。gcc的ABI約定,函數棧幀的大小必須 16 bytes 對齊,所以sub指令所減去的16進制數以c結尾(棧幀上已經有上一幀 %ebp ) 。
  • 初始化局部變量。
    這里對局部變量的初始化是以棧底指針為基准的,此處值得注意的是 (%ebp) 中存儲的是上一幀的 %ebp

leave 指令

8048411: c9 leave

leave 所做的工作是還原上一幀的棧底指針與棧頂指針,等效於

mov  %ebp,%esp // 把棧頂指針置為本幀的棧底(同時也是存儲上一幀棧底指針內容的地址),
popl %ebp      // 還原上一幀的棧底指針,此時 %esp 指向返回地址

ret 指令

8048412: c3 ret

ret 所做的工作是彈出棧頂的返回地址,並跳轉到此地址。此時 %esp 指向調用函數所存儲的被調函數的最后一個參數。

雜記

一個完整的棧幀上會有什么?
從底到頂依次是:

1. 上一幀的 `%ebp` 1. ABI 約定被調用者保存(如果有)的調用者的三個寄存器的內容 `%ebx` `%esi` `%edi` 2. 局部變量 2. 對齊空白 3. ABI 約定調用者保存(如果有)的自己的三個寄存器的內容 `%eax` `%edx` `%ecx` 3. 所調用的函數的參數 3. 返回地址(本幀的 %esp所指,下一幀的 0x4(%ebp))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM