首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

深度解析函數調用棧:EIP、ESP、EBP 寄存器角色與棧幀變化

     編輯:程式設計疑難解答

問題核心

函數調用時,指令指針(EIP)指向函數地址,棧指針(ESP)和基址指針(EBP)如何變化?函數返回時如何恢復現場?EIP 是否等於 ESP?

x86 函數調用約定(cdecl)棧幀詳解

調用者端

  1. 將參數**從右向左**壓棧。
  2. 執行 CALL func:
    - 隱式將**下一條指令地址(返回地址)**壓棧(即當前 EIP)。
    - 跳轉到函數入口,EIP = func 地址。

被調用者端(函數序言)

push ebp        ; 保存調用者的 EBP
mov  ebp, esp   ; 建立新棧幀基址
sub  esp, N     ; 分配局部變量空間(N 字節)

此時:EBP 指向棧幀基址,ESP 指向棧頂(局部變量區),EIP 指向函數代碼區(不在棧中)。明顯 EIP ≠ ESP,二者分屬代碼段與棧段。

函數體執行

  • 通過 [ebp + offset] 訪問參數(正偏移)。
  • 通過 [ebp - offset] 訪問局部變量(負偏移)。

函數返回(函數結語)

mov  esp, ebp   ; 恢復 ESP(釋放局部變量空間)
pop  ebp        ; 恢復調用者 EBP
ret             ; 彈出返回地址到 EIP,跳轉回調用者下一條指令

關鍵點總結

階段EIPEBPESP棧內容(從高到低)
調用前調用點調用者基址棧頂參數N...參數1
CALL 後函數入口調用者基址返回地址返回地址、參數N...參數1
序言後函數代碼新棧幀基址局部變量區頂舊EBP、返回地址、參數...、局部變量
返回後調用點下一條調用者基址調用前棧頂參數N...參數1(由調用者清理)

常見誤區澄清

  • 函數代碼不在棧中:代碼段只讀,棧僅存參數、返回地址、幀指針、局部變量。
  • EIP 不等於 ESP:EIP 指向代碼,ESP 指向棧,屬不同內存區域。
  • 返回地址是「下一條指令」:CALL 壓入的是 CALL 指令之後那條指令的地址。

補充:調用約定差異

  • cdecl:調用者清理棧參數(C/C++ 默認)。
  • stdcall:被調用者清理棧參數(Windows API)。
  • fastcall:前幾個參數通過寄存器傳遞(ECX/EDX)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved