棧幀與函式呼叫過程分析

2021-08-10 15:46:53 字數 2754 閱讀 2296

乙個c/c++編譯的程式占用的記憶體分為以下幾個部分:

1.棧區(stack):由編譯器自動分配和釋放,存放函式的引數值,區域性變數的值,返回資料,返回位址等。操作方式類似於資料結構中的棧。

2.堆區(heap):一般由程式設計師分配和釋放,若程式設計師不釋放,程式結束時可能由作業系統**。與資料結構中的堆是兩碼事,分配方式類似於鍊錶。

3.全域性區(靜態區)(static):存放全域性變數、靜態資料、常量。程式結束後由系統釋放。

4.文字常量區:常量字串放在此,程式結束後由系統釋放。

5.程式**區:存放函式體(類成員函式和全域性函式)的二進位制**。

記憶體區域分配如圖所示:

其中堆和棧相對而生。

堆和棧的申請方式:

棧由系統自動分配,速度較快,在windows下棧是向低位址擴充套件的資料結構,是一塊連續的記憶體區域,大小是2mb。

堆需要程式設計師自己申請,並指明大小,速度比較慢。在c中用malloc申請,c++中用new申請。另外,堆是向高位址擴充套件的資料結構,是不連續的記憶體區域,堆的大小受限於計算機的虛擬記憶體。因此堆空間獲取和使用比較靈活,可用空間較大。

接下來由乙個例項具體分析呼叫過程

#include 

#include

int add(int

x, int

y)int main()

在具體分析之前先了解3個重要暫存器:

ebp:基址暫存器,存放指向函式棧幀棧底的位址。

esp:棧頂暫存器,存放指向函式棧幀棧頂的位址。

eip/ip/pc:程式計數器,儲存的內容指向下一條指令。

main函式組合語言如下:
int main()

add函式組合語言如下:

pop edi //出棧

00e91402

pop esi //出棧

00e91403

pop ebx //出棧,使esp向下移動

00e91404

mov esp,ebp //將ebp賦值給esp

00e91406

pop ebp

00e91407

ret //ret指令會使得出棧一次,並將出棧的內容當作位址,將程式執行跳轉到該位址處。

//此位址將被壓入棧中

由此分析,x,y是連續存放的,所以我們可以通過修改x的位址進而修改y的值

如:

#include 

#include

int add(int

x, int

y)int main()

又如:
#include 

#include

void bug()

int add(int

x, int

y)int main()

#include 

#include

void *c = null;

void bug()

int add(int

x, int

y)int main()

程式還是會崩潰,因為呼叫add函式時使用的是call彙編語句,而呼叫bug函式是直接修改其ip位址。但兩個函式最後都有乙個ret結構,將棧頂的返回值彈出值eip中,所以經歷了兩次出棧,卻只有一次壓棧。需要平衡棧幀結構。

改進如下:

#include 

#include

void *c = null;

void bug()

int add(int

x, int

y)int main()

system("pause");

return

0;}

棧幀與函式呼叫過程

程式的位址空間圖中,從下向上,位址不斷增長,從0x00000000到0xffffffff。code 區,儲存 字元 串常量區 儲存不能被修改的常量 init,uninit 全域性區,分別為初始化全域性區,和未初始化全域性區,儲存全域性變數 heap 堆,使用malloc在堆申請空間,在堆上建立的變數...

函式呼叫過程及棧幀分析

linux核心程式boot head.s執行完基本初始化操作之後,就會跳轉去執行init main.c程式。那麼head.s程式是如何把執行控制轉交給init main.c程式的呢?即匯程式設計序是如何呼叫執行c語言程式的?這裡我們首先描述一下c函式的呼叫機制 控制權傳遞方式,然後說明head.s程...

函式呼叫過程(棧幀)

眾所周知,程式每呼叫乙個函式,系統都會為其開闢一塊空間,當它返回時,才收回這塊空間。程式崩潰有一部分原因就是因為無限制的呼叫函式,卻沒有及時返回,導致記憶體空間不夠。為了更好的維護這一塊空間 通常稱為棧空間 我們需要了解兩個暫存器,乙個為 esp 指向棧頂的指標 乙個為 ebp 指向棧底的指標 棧空...