c語言--棧幀結構分析
這是一篇在自己寫了很久的文章了,因為要清理電腦,放在部落格上當備份。
寫在前邊:
棧幀結構對於初學C語言著,可能有些陌生。但是不能否認的是棧幀在C語言中扮演著很重要的角色,下邊就棧幀分析下記憶體。如果真對C語言感興趣的請耐心的看下邊的敘述。
棧幀說的簡單點就是呼叫函式的過程中,為這個函式開闢一個棧空間,用來儲存這個函式中的臨時變數。而這個儲存函式臨時變數的空間就被稱為棧空間。而這個棧空間就被稱為棧幀。
*******************************************************
索引:
在分析棧幀前還需要簡述幾個簡單的知識點。(在棧幀的分析中我們會用到的)
EIP:EIP又稱為
esp:棧頂暫存器,存放了指向函式棧幀棧頂的地址。其記憶體放著一個指標,該指標永遠指向系統棧最上面一個棧幀的棧頂。
Ebp:棧地暫存器(又可稱基址暫存器),存放了指向棧幀棧底的地址。
在這塊就需要簡單說一下函式的記憶體圖了:
(&&對這就個圖的介紹):記憶體中地址由小到大,先可以簡單的化成上邊的這樣一個區域,此次主要研究的是堆和棧區,下邊的記憶體暫不仔細去研究(下邊記憶體並不全面,只是取了幾個代表)。
棧區和堆區是相對而生的,堆區是向上生長的,棧區是向下生長的。堆區和地址空間的大小排列一樣,而這快容易出問題的應該算棧區了。而我們下邊會仔細去敘述一下堆區的。
而這裡需要強調的一點是堆區和棧區之間有一段空白,堆區和棧區不是相連的區域。
上邊說了這麼多的話語了,現在簡單粗暴一點,直接上程式碼。
我們執行的程式碼在
定義一個main函式,定義兩個引數a,b。在main函式中呼叫一個函式使得兩個數相加,暫且將這個函式稱為Add,最終返回到main函式中。打印出結果。
函式如下
暫時定義a和b為這樣,為了等會更直觀的展現出來。程式碼很簡單現在進行分析。
程式執行進入組合語言過程中。(組合語言的指令不詳細介紹了)
棧向下生長,因此ebp棧底反而在地址大的地方,esp棧頂在地址小的地方。
這張圖片所描述的是棧區的放大空間。
棧是向下生長的,因此這塊需要注重的一個細節是ebp棧底指標反而指向地址大的地方,esp棧頂指標指向在地址小的地方。
調用出彙編程式碼,函式從main開始,前邊的預處理暫時不仔細去研究,簡單來描述,預處理給main分配了一個空間,並全部初始化了。如果真對這段彙編程式碼感興趣,可以查閱相關的組合語言的書籍。
當代碼進行到add時時在棧空間中已經進去了a,b,以及定義的ret。在這塊需要注意的是mov指令將a移動到【ebp-4】即ebp指標下移指向a,將a的地址放進去。b移動到【ebp-8】中即ebp指標指向了b的地址。
這塊需要說的和之前一樣棧中的生長方式是向下生長的,因此地址由高到低,一次存放。即實際上就是給a先開闢空間,再給b開闢空間。
此處的push即壓棧的過程,而eax是cup裡的通用暫存器,此時的eax裡放的是esp的地址。即棧幀的壓棧過程是往棧頂壓棧,通用暫存器裡邊放的是esp的地址。此時就是最關鍵的地方了。在理論上的棧先進後出,就在這用直觀的程式碼顯示出來了。
(esx,ebx,ecx,edx都是通用暫存器,在CPU中起一個儲存臨時變數的作用)
eax儲存的是【ebp-8】,即b的地址,即從組合語言來看就是將,b的地址壓棧進,esp頂部。而a也是這個原理根據彙編程式碼可以分析出,a在b的下邊。即根據彙編程式碼可以分析畫出下邊的圖。
此時的esp指向了壓棧a的地址空間。
這個過程返回到編輯器,呼叫一下暫存器,與記憶體就可以清晰的展現在面前。
此時的呼叫esp棧頂的地址,a,b的地址展現的很清楚了。而這塊還需要注意的是棧是由上向下生長的。
這塊需要知道一個組合語言的命令。
Call命令:(兩個作用)
1、將當前執行指令的下一條指令的下一條地址壓入棧中。
2、隨機call就會跳轉(jmp)至指定函式程式碼塊去執行。
此時的介面是呼叫玩call指令之後的圖,即現在的棧頂將add這個地址壓棧進去。可看見esp的最上端是add的地址。
上圖就是呼叫到add函式的程式碼。跟main有異曲同工之處。
此時的eip開始呼叫add()函式中的值。
即在空間圖上可以分析為在空間上在分配一個空間用來存放add函式的臨時變數。
Add函式就是跟上邊的main函式一樣的存放在屬於它自己的棧幀空間裡的。具體的就不詳細描述了。可以跟著上邊的main對比來看一下。
而在這塊 add函式返回的這塊的仔細的剖析一下。
在函式add中,a,b的和賦值給z,而將兩個數所加之和賦值給,通用暫存器eax中,暫時存放起來。
在函式的結束部分,有這樣一段彙編程式碼。這個過程就是棧的釋放過程。
將ebp的地址賦給了esp.然後釋放了ebp的空間,即main:ebp被釋放。Ebp返回到main函式中的ebp地址中去。而原來的main:ebp空間是空的所以向上自動跳一格。
而最後的ret這塊需要強調一下。
Ret命令的兩個作用:
1、彈出pop棧頂
2、彈出棧頂的地址,並且將返回值放入EIP中。
則此時的ebp,esp又返回到之前的地址。到現在,棧的形成與銷燬就完成了。
得出一個函式的棧空間都是自己開闢的,使用完後銷燬釋放空間。