ARM二進制程序的函數調用過程棧的變化詳解


概要

本篇博客主要包括兩個方面的內容:

  1. 整理棧涉及到的一些基本概念、ARM架構下棧相關的操作指令;
  2. 分析一個函數調用實例。

* 棧的基本知識

棧的概念


  • 首先,棧是一種先進后出(FILO)的數據結構,棧底是第一個進棧數據所在的位置,棧頂是最后一個進棧數據所在的位置。
    其次,棧也是內存中的一段特殊空間,用於存放函數參數、函數上下文(寄存器)、函數返回地址、局部變量等。

Ps. 返回值一般是在R0寄存器中返回,不使用棧。

  • 棧幀
    系統在運行過程中,會為每個進程分配一個棧空間(互不干擾),而進程中的每個函數在被調用時會分配棧上的一塊連續的空間區域,這個空間區域就是棧幀,函數在執行過程中可以隨意使用屬於自己棧幀上的空間,當函數返回時,棧幀空間將被收回,后續可能會分配給其他函數使用。每個函數的棧幀使用棧頂指針和棧底指針來界定。如下圖所示:

  • 棧的相關寄存器和指針

寄存器 名稱 功能
R11 frame pointer,FP寄存器 用於保存FP指針,即棧底指針
R12 IP寄存器 用於暫存SP,即棧頂
R13 stack pointer,SP寄存器 用於保存SP指針,即棧頂指針
R14 link register,LR寄存器 用於保存函數的返回地址
R15 PC寄存器 用於保存程序計數器的值

棧的分類

  • 滿/空棧【Full/Empty】
    根據SP指針指向的位置,棧可分為滿棧和空棧,如下圖:

    SP指向最后入棧的數據時為滿棧(左圖);SP指向下一個將要放入數據的空位置時為空棧(右圖)。形象的理解為,SP指針指向最后一個數據,這個棧沒有空位置,那就是滿的;而當SP指針指向一個空位置,那么這個棧就不滿,就是空的。

  • 升/降棧【Ascending/Descending】
    根據SP指針的移動方向,棧可以分為升棧和降棧,如下圖:

    當數據入棧時,SP指針從低地址向高地址移動(即棧的生長方向為Low->High),此為升棧(左圖);相反的,SP指針從高地址向低地址移動(即棧的生長方向為High->Low),此為降棧(右圖)。

  • 棧的四種情況
    綜上,兩兩結合可以得到四種類型的棧,即:

滿降棧FD 滿升棧FA 空降棧ED 空升棧EA

Ps. ARM為滿降棧。

棧相關指令(存取指令)

##以32位ARM為例,ARM64中棧的指令有所改變##

首先需要明確的一點是,存取指令有很多,但實際的使用需要結合棧的種類。如果是滿棧的話,肯定是需要先改變SP指針再壓棧;出棧則相反,需要先出棧,再改變SP指針。而如果是空棧,壓棧時先壓入數據,再改變SP指針;出棧時,先移動SP指針,再彈出數據。

批量存取指令為:LDM(取)和STM(存),即load multiple & store multiple。

結合四種類型的棧,那么就會存在以下四對存取指令:

😃 滿降棧 滿升棧 空降棧 空升棧
批量存 STMFD STMFA STMED STMEA
批量取 LDMFD LDMFA LDMED LDMEA

ARM是滿降棧,因此使用STMFD和LDMFD指令。
STMFD指令即向棧中壓入多個數據,采用事先遞減方式(DB,before decrease),先將SP指針減小,再壓入數據;
LDMFD指令即彈出棧中的多個數據,采用事后遞增方式(IA,after increase),先彈出數據,再將SP指針增大。
其他指令類似,在這里不做過多討論。重點是要理解不同類型棧的壓棧和出棧規律。

Ps. 此外還有事后遞減(DA)和事先遞增(IB)方式。

* 函數調用實例分析棧的變化

下面看一個實例,如下圖,main函數調用test_b函數,在test_b函數中首先是函數序言(Prologue,一般用於在棧中保存一些環境變量等),然后是test_b函數的函數體(完成相應功能),最后是函數結語(Epilogue,一般用於恢復棧中保存的變量,准備好返回main函數的環境)。

函數序言
執行STMFD指令,向棧中壓入R11和LR,即main函數的FP指針和返回地址,確保test_b函數執行完畢后可以正確返回main函數中的相應位置繼續執行后續指令以及可以正確恢復main函數的棧幀。隨着數據的壓入,SP = SP - 8。
執行ADD指令,更新當前的棧底指針(R11寄存器,FP指針),指向test_b函數棧幀的棧底。
執行SUB指令,抬高棧頂,為test_b函數創建棧幀,后續代碼的執行可以用到這塊棧空間。

以上過程示意圖如下:

函數結語
執行SUB指令,更新SP = FP - 4,即指向保存的R11值。
執行LDMFD指令,彈出之前保存的main函數棧底指針,以及返回地址賦值給PC寄存器,這樣的話main函數的執行流和棧幀全部恢復了,然后SP = SP + 8。

以上過程示意圖如下:

參考鏈接

  1. ARM匯編之棧與函數 函數調用棧示例分析
  2. ARM-棧 棧的基本概念和作用
  3. ARM下C語言棧幀機制
  4. [arm-匯編stmdb、ldmia、stmfd、ldmfd]


免責聲明!

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



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