C語言彙編 函式呼叫棧

2021-08-28 06:39:50 字數 3927 閱讀 5220

函式呼叫大家都不陌生,呼叫者向被呼叫者傳遞一些引數,然後執行被呼叫者的**,最後被呼叫者向呼叫者返回結果,還有大家比較熟悉的一句話,就是函式呼叫是在棧上發生的,那麼在計算機內部到底是如何實現的呢?

對於程式,編譯器會對其分配一段記憶體,在邏輯上可以分為**段,資料段,堆,棧

**段:儲存程式文字,指令指標eip就是指向**段,可讀可執行不可寫

資料段:儲存初始化的全域性變數和靜態變數,可讀可寫不可執行

bss:未初始化的全域性變數和靜態變數

如圖所示

暫存器

eax:累加(accumulator)暫存器,常用於函式返回值

ebx:基址(base)暫存器,以它為基址訪問記憶體

ecx:計數器(counter)暫存器,常用作字串和迴圈操作中的計數器

edx:資料(data)暫存器,常用於乘除法和i/o指標

esi:源變址暫存器

dsi:目的變址暫存器

esp:堆疊(stack)指標暫存器,指向堆疊頂部

ebp:基址指標暫存器,指向當前堆疊底部

源**

int print_out(int begin, int end)

int add(int a, int b)

int pass(int a, int b, int c) ;

int sum = 0;

int ret;

ret = (int)(buffer+28);

//(*ret) += 0xa;

sum = a + b + c;

return sum;

}int main()

printf("%d\n", __sum);

system(「pause」);

}函式初始化

28: int main()

29: {

011c1540 push ebp //壓棧,儲存ebp,注意push操作隱含esp-4

011c1541 mov ebp,esp //把esp的值傳遞給ebp,設定當前ebp

011c1543 sub esp,0f0h //給函式開闢空間,範圍是(ebp, ebp-0xf0)

011c1549 push ebx

011c154a push esi

011c154b push edi

011c154c lea edi,[ebp-0f0h] //把edi賦值為ebp-0xf0

011c1552 mov ecx,3ch //函式空間的dword數目,0xf0>>2 = 0x3c

011c1557 mov eax,0cccccccch

011c155c rep stos dword ptr es:[edi]

//rep指令的目的是重複其上面的指令.ecx的值是重複的次數.

//stos指令的作用是將eax中的值拷貝到es:edi指向的位址,然後edi+4

一般所用函式的開頭都會有這段命令,完成了狀態暫存器的儲存,堆疊暫存器的儲存,函式記憶體空間的初始化

函式呼叫

30: print_out(0, 2);

013d155e push 2 //第二個實參壓棧

013d1560 push 0 //第乙個實參壓棧

013d1562 call print_out (13d10fah)//返回位址壓棧,本例中是013d1567,然後呼叫print_out函式

013d1567 add esp,8 //兩個實參出棧

除了vs可能增加一些安全性檢查外,print_out的初始化與main函式的初始化完全相同

被呼叫函式返回

013d141c mov eax,1 //返回值傳入eax中

013d1421 pop edi

013d1422 pop esi

013d1423 pop ebx //暫存器出棧

013d1424 add esp,0d0h //以下3條命令是呼叫vs的__rtc_checkesp,檢查棧溢位

013d142a cmp ebp,esp

013d142c call @ilt+315(__rtc_checkesp) (13d1140h)

013d1431 mov esp,ebp //ebp的值傳給esp,也就是恢復呼叫前esp的值

013d1433 pop ebp //彈出ebp,恢復ebp的值

013d1434 ret //把返回位址寫入eip中,相當於pop eip

call指令隱含操作push eip,ret指令隱含操作 pop eip,兩條指令完全對應起來 

寫到這裡我們就可以分析一下main函式呼叫print_out函式前後堆疊(stack)發生了什麼變化,下面用一系列圖說明

接下來是返回過程,從上面的013d1431 行**開始

print_out函式呼叫前後,main函式的棧幀完全一樣,perfect!

下面我們來看看print_out函式到底做了什麼事情

int *p;

p = (int*)(int(&begin) - 4);

if(begin <= end)

*p -= 5;

根據上面呼叫print_out函式後的示意圖,可以知道p實際上是指向了函式的返回位址addr,然後把addr-5,這又會發生什麼?

再回頭看一下反彙編的**,

013d1560 push 0 //第乙個實參壓棧

013d1562 call print_out (13d10fah)//返回位址壓棧,本例中是013d1567,然後呼叫print_out函式

013d1567 add esp,8 //兩個實參出棧

分析可知,返回位址addr的值是013d1567 ,addr-5為013d1562 ,把返回位址指向了call指令,結果是再次呼叫print_out函式,

從而print_out函式實現了列印從begin到end之間的所有數字,可以說是迴圈呼叫了print_out函式

對於add函式,主要是為了說明返回值存放於暫存器eax中。

另外,vs自身會提供一些安全檢查

checkstackvar安全檢查

通過ecx和edx傳遞引數, 區域性變數有陣列時使用

__security_check_cookie返回位址檢查, 陣列長度大於等於5時使用

__rtc_checkesp程式棧檢查,printf函式用使用

好文要頂

關注我收藏該文

小雨淅淅

關注 - 0

粉絲 - 15

+加關注 5 0

c++中float型別的儲存

kmp演算法

posted @ 2014-03-24 22:38

收藏

組合語言 函式呼叫棧

當發生函式呼叫的時候,棧空間中存放的資料是這樣的 1 呼叫者函式把被調函式所需要的引數按照與被調函式的形參順序相反的順序壓入棧中,即 從右向左依次把被調函式所需要的引數壓入棧 2 呼叫者函式使用call指令呼叫被調函式,並把call指令的下一條指令的位址當成返回位址壓入棧中 這個壓棧操作隱含在cal...

彙編呼叫c函式設定棧的原因

一.棧的整體作用 1 儲存現場 上下文 2 傳遞引數 彙編 呼叫c函式時,需傳遞引數 3 儲存臨時變數 包括函式的非靜態區域性變數以及編譯器自動生成的其他臨時變數。1.儲存現場 上下文 在函式呼叫之前,應該將這些暫存器等現場,暫時保持起來 入棧push 等呼叫函式執行完畢返回後 出棧pop 再恢復現...

C 從彙編角度詳解函式呼叫棧

先來看一段 int sum int a,int b intmain 有兩個問題 main函式呼叫sun,sum執行完之後怎麼知道回到哪個函式?sum函式執行完,回到main之後,怎麼知道從哪一行指令繼續執行?我們現在從彙編角度看這段 首先main還是會先開闢棧幀 mov dword ptr ebp ...