linux核心程式boot/head.s執行完基本初始化操作之後,就會跳轉去執行init/main.c程式。那麼head.s程式是如何把執行控制轉交給init/main.c程式的呢?即匯程式設計序是如何呼叫執行c語言程式的?這裡我們首先描述一下c函式的呼叫機制、控制權傳遞方式,然後說明head.s程式跳轉到c程式的方法。
函式呼叫操作包括從一塊**到另一塊**之間的雙向資料傳遞和執行控制轉移。資料傳遞通過函式引數和返回值來進行。另外,我們還需要在進入函式時為函式的區域性變數分配儲存空間,並且在退出函式時收回這部分空間。intel 80x86 cpu為控制傳遞提供了簡單的指令,而資料的傳遞和區域性變數儲存空間的分配與**則通過棧操作來實現。
1.棧幀結構和控制轉移權方式
大多數cpu上的程式實現使用棧來支援函式呼叫操作。棧被用來傳遞函式引數、儲存返回資訊、臨時儲存暫存器原有值以備恢復以及用來儲存區域性資料。單個函式呼叫操作所使用的棧部分被稱為棧幀(stack frame)結構,其一般結構如圖3-4所示。棧幀結構的兩端由兩個指標來指定。暫存器ebp通常用做幀指標(frame pointer),而esp則用作棧指標(stack pointer)。在函式執行過程中,棧指標esp會隨著資料的入棧和出棧而移動,因此函式中對大部分資料的訪問都基於幀指標ebp進行。即esp負責資料的儲存與丟棄,而ebp負責資料的讀取。
於函式a呼叫函式b的情況,傳遞給b的引數包含在a的棧幀中。當a呼叫b時,函式a的返回位址(呼叫返回後繼續執行的指令位址)被壓入棧中,棧中該位置也明確指明了a棧幀的結束處,因為隨後pc指標跳轉到函式b內執行。而b的棧幀則從隨後的棧部分開始,即圖中儲存幀指標(ebp)的地方開始,儲存的ebp為上一函式(即函式a)的棧幀起始位址。再隨後則用於存放任何儲存的暫存器值以及函式的臨時值。
因此b函式棧幀中的內容可以理解為:1. ebp的值(儲存的是a函式的棧幀)。2. b函式儲存的暫存器、區域性變數、臨時值。3.呼叫其他函式時,傳給其他函式的引數。4. b函式的返回位址。
棧是往低(小)位址方向擴充套件的,而esp指向當前棧頂處的元素。通過使用push和pop指令我們可以把資料壓入棧中或從棧中彈出。對於沒有指定初始值的資料所需要的儲存空間,我們可以通過把棧指標遞減適當的值來做到。類似地,通過增加棧指標值我們可以**棧中已分配的空間。
指令call和ret用於處理函式呼叫和返回操作。呼叫指令call的作用是把返回位址壓入棧中並且跳轉到被呼叫函式開始處執行。返回位址是程式中緊隨呼叫指令call後面一條指令的位址。因此當被調函式返回時就會從該位置繼續執行。返回指令ret用於彈出棧頂處的位址並跳轉到該位址處。在使用該指令之前,應該先正確處理棧中內容,使得當前棧指標所指位置內容正是先前call指令儲存的返回位址。可以發現,call和ret指令均完成兩項工作,從棧中把返回位址儲存或恢復,跳轉pc到指定位置,可以說,兩者的操作是相反的。若返回值是乙個整數或乙個指標,那麼暫存器eax將被預設用來傳遞返回值。
儘管某一時刻只有乙個函式在執行,但我們還是需要確定在乙個函式(呼叫者)呼叫其他函式(被呼叫者)時,被呼叫者不會修改或覆蓋呼叫者今後要用到的暫存器內容。因此intel cpu 採用了所有函式必須遵守的暫存器用法統一慣例。該慣例指明,暫存器eax、edx和ecx的內容必須由呼叫者自己負責儲存。當函式b被a呼叫時,函式b可以在不用儲存這些暫存器內容的情況下任意使用它們而不會毀壞函式a所需要的任何資料。另外,暫存器ebx、esi和edi的內容則必須由被呼叫者b來保護。當被呼叫者需要使用這些暫存器中的任意乙個時,必須首先在棧中儲存其內容,並在退出時恢復這些暫存器的內容。因為呼叫者a(或者一些更高層的函式)並不負責儲存這些暫存器內容,但可能在以後的操作中還需要用到原先的值。還有暫存器ebp和esp也必須遵守第二個慣例用法。
函式呼叫過程原理及函式棧幀分析
在x86的計算機系統中,記憶體空間中的棧主要用於儲存函式的引數,返回值,返回位址,本地變數等。一切的函式呼叫都要將不同的資料 位址壓入或者彈出棧。因此,為了更好地理解函式的呼叫,我們需要先來看看棧是怎麼工作的。簡單來說,棧是一種lifo形式的資料結構,所有的資料都是後進先出。這種形式的資料結構正好滿...
棧幀與函式呼叫過程分析
乙個c c 編譯的程式占用的記憶體分為以下幾個部分 1.棧區 stack 由編譯器自動分配和釋放,存放函式的引數值,區域性變數的值,返回資料,返回位址等。操作方式類似於資料結構中的棧。2.堆區 heap 一般由程式設計師分配和釋放,若程式設計師不釋放,程式結束時可能由作業系統 與資料結構中的堆是兩碼...
函式呼叫過程(棧幀)
眾所周知,程式每呼叫乙個函式,系統都會為其開闢一塊空間,當它返回時,才收回這塊空間。程式崩潰有一部分原因就是因為無限制的呼叫函式,卻沒有及時返回,導致記憶體空間不夠。為了更好的維護這一塊空間 通常稱為棧空間 我們需要了解兩個暫存器,乙個為 esp 指向棧頂的指標 乙個為 ebp 指向棧底的指標 棧空...