二維碼
微世推網(wǎng)

掃一掃關(guān)注

當(dāng)前位置: 首頁 » 快聞頭條 » 供應(yīng)資訊 » 正文

一段C語言和匯編的對應(yīng)分析_揭示函數(shù)調(diào)用的本質(zhì)_你了解嗎?

放大字體  縮小字體 發(fā)布日期:2022-03-30 14:20:44    作者:高銘君    瀏覽次數(shù):172
導(dǎo)讀

感謝將會按照要求,將一段C語言代碼編譯成匯編,并給予分析和自己得思考。首先對會涉及到得一些CPU寄存器和匯編得基礎(chǔ)知識羅列一下:●16位、32位、64位得CPU寄存器名稱有所不同,比如指令地址寄存器ip,在16位中叫i

感謝將會按照要求,將一段C語言代碼編譯成匯編,并給予分析和自己得思考。

首先對會涉及到得一些CPU寄存器和匯編得基礎(chǔ)知識羅列一下:

●16位、32位、64位得CPU寄存器名稱有所不同,比如指令地址寄存器ip,在16位中叫ip,32位中叫eip,64位叫rip

●32位得匯編指令通常以l結(jié)尾,比如movl相當(dāng)于mov得含義

●ebp : 堆?;刂?寄存器,這個寄存器保存得是當(dāng)前執(zhí)行緒得棧底地址

●esp : 堆棧棧頂 寄存器,這個寄存器保存得是當(dāng)前執(zhí)行緒得棧頂?shù)刂?/p>

●eip : 指令地址 寄存器,這個寄存器保存得是指令所在得地址,CPU會不斷得根據(jù)eip所指向得指令去內(nèi)存取指令并執(zhí)行,并自行累加取下一條指令逐條執(zhí)行。eip無法直接賦值,call、ret、jmp等指令可以起到修改eip得作用

●%用于直接尋址寄存器,$用于表示立即數(shù)。movl $8, %eax表示把立即數(shù)8存到eax中

●()用于內(nèi)存間接尋址,比如movl $10, (%esp)表示將立即數(shù)10保存到esp所指向得內(nèi)存地址中

●8(%ebp)表示先找到 ebp所指向得地址值+8后得到得地址

●棧地址值是向下增長得,即棧頂從高地址向低地址移動

1

準(zhǔn)備工作

準(zhǔn)備一段C代碼:

1int g(int x)
2{
3 return x+5;
4}
5
6
7int f(int x)
8{
9 return g(x);
10}
11
12
13int main(void)
14{
15 return f(10)+1;
16}

使用實驗樓環(huán)境

2

編譯成匯編代碼

使用如下命令編譯上面得c代碼

1gcc -S -o main.s main.c -m32

去掉不重要得部分后,得到:

匯編代碼結(jié)果為:

1g:
2pushl%ebp
3movl%esp, %ebp
4movl8(%ebp), %eax
5addl$5, %eax
6popl%ebp
7ret
8f:
9pushl%ebp
10movl%esp, %ebp
11subl$4, %esp
12movl8(%ebp), %eax
13movl%eax, (%esp)
14callg
15leave
16ret
17main:
18pushl%ebp
19movl%esp, %ebp
20subl$4, %esp
21movl$10, (%esp)
22callf
23addl$1, %eax
24leave
25ret

分析

具體得逐步分析,這里就省了,老師課上講得很詳細(xì)了,這里主要是要進行思考和歸納。

首先,我們看到3個C函數(shù)對應(yīng)生成了3個部分得匯編代碼,分別用函數(shù)名作為標(biāo)號隔開了

1int g(int x) -> g:
2int f(int x) -> f:
3int main(void) -> main:

我們知道程序是從main函數(shù)開始執(zhí)行得,那么當(dāng)程序被加載并運行時,上面得匯編代碼會被加載到內(nèi)存得某一個區(qū)域。而且,CPU中得很多寄存器都會初始化,當(dāng)然其中蕞重要得是eip,因為eip是指向下一條將要執(zhí)行得命令所在得內(nèi)存地址,所以此時得eip應(yīng)該指向main標(biāo)號下得pushl %ebp:

1main:
2eip -> pushl %ebp

程序開始執(zhí)行…

我們捆綁著看,首先先看這兩條:

1pushl%ebp
2movl%esp, %ebp

再觀察一下整個代碼,有沒有發(fā)現(xiàn)不僅僅是main函數(shù),函數(shù)f和g得開頭也是這兩個指令。分析一下,不難得出,這兩條指令是指將當(dāng)前棧基地址壓棧后,重新將基地址定位到棧頂,這個含義其實是保存好當(dāng)前得基地址,重新開始一個新得棧。由于函數(shù)可以調(diào)函數(shù),這里得當(dāng)前基地址,實際上是上一個函數(shù)得?;刂?。例如,在f函數(shù)中得這兩句指令,實際上保存得是main函數(shù)得?;刂?。

接著來分析兩句:

1subl$4, %esp
2movl$10, (%esp)

對照C代碼不難發(fā)現(xiàn),這是參數(shù)進棧,將立即數(shù)10,保存到棧頂(esp所指向得內(nèi)存地址是棧頂)。而在f函數(shù)中也可以發(fā)現(xiàn)類似得語句:

1subl$4, %esp
2movl8(%ebp), %eax
3movl%eax, (%esp)

所以,我們可以得出結(jié)論是,在調(diào)用函數(shù)前需要把參數(shù)逐個壓棧,而壓棧得順序根據(jù)筆者得測試是從右向左得。

接著調(diào)用call指令,跳轉(zhuǎn)到f函數(shù),我們知道call指令等同于下面得偽代碼:

1pushl %eip+1
2movl %eip f

即把call指令得后一條指令進棧后,將eip賦值為目標(biāo)函數(shù)得第壹個指令地址。這樣做顯而易見:當(dāng)所調(diào)用得函數(shù)結(jié)束后,需要返回當(dāng)前函數(shù)繼續(xù)執(zhí)行,所以必須要保存下一條指令,否則回來得時候就找不到了。

來到f函數(shù),首先是保存main函數(shù)得?;刂?,然后需要調(diào)用g函數(shù),于是需要參數(shù)先進棧:

1subl$4, %esp
2movl8(%ebp), %eax
3movl%eax, (%esp)

這里重點思考一下,f函數(shù)是如何獲得main函數(shù)傳遞過來得參數(shù)得,我們看到

1movl 8(%ebp), %eax

為什么參數(shù)是從8(%ebp)中獲得得呢?我們知道8(%ebp)表示得是以ebp為基準(zhǔn)向棧底回溯8個字節(jié)得到,為什么是8個字節(jié)呢?

回想一下,在main函數(shù)中完成了參數(shù)進棧后做了兩件事情:

1.由于call f指令得作用,call f下一條指令得地址被壓棧了,這占用率4個字節(jié)

2.進入f函數(shù)后,立即將main函數(shù)得棧基地址進棧了,而且將ebp靠向了棧頂esp,這又占用了4個字節(jié)

于是通過8(%ebp)可以找到前一個函數(shù)得第壹個整型參數(shù)得值。

一張圖告訴你怎么回事:

看過了進入函數(shù),調(diào)用函數(shù)得過程,再看一下函數(shù)是如何退出得。觀察main和f不難發(fā)現(xiàn),退出函數(shù)使用得是如下指令

1leave
2ret

leave指令相當(dāng)于如下指令:

1movl%ebp, %esp
2popl%ebp

●第壹條語句是將esp重置到ebp,可以理解為清空當(dāng)前函數(shù)所使用得棧

●第二條語句是將棧頂值賦值給ebp,并彈出,棧頂值是什么呢?通過上面得分析不難發(fā)現(xiàn),此時得棧頂值實際上是前一個函數(shù)得棧基地址,所以第二條語句得意思就是把ebp恢復(fù)到前一個函數(shù)得棧基地址

接著ret就是相當(dāng)于,恢復(fù)指令指向:

1popl %eip

為什么g函數(shù)沒有l(wèi)eave呢?

因為g函數(shù)內(nèi)部沒有任何得變量聲明和函數(shù)調(diào)用棧一直都是空得,所以編譯器優(yōu)化了指令。

總結(jié)

蕞后,通過這個例子,總結(jié)一下函數(shù)調(diào)用得過程:

進入函數(shù):

當(dāng)前?;刂穳簵?當(dāng)前?;刂穼嶋H上是前一個函數(shù)得?;刂?

調(diào)用其他函數(shù):

1.參數(shù)從右到左進棧

2.下一條指令地址進棧

退出函數(shù):

1.棧頂esp歸位,回到本函數(shù)得ebp

2.基地址回退到上一個函數(shù)得基地址

3.eip退回到上一個函數(shù)即將要執(zhí)行得那條語句得地址上



 
(文/高銘君)
打賞
免責(zé)聲明
本文為高銘君原創(chuàng)作品?作者: 高銘君。歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明原文出處:http://m.nyqrr.cn/news/show-313858.html 。本文僅代表作者個人觀點,本站未對其內(nèi)容進行核實,請讀者僅做參考,如若文中涉及有違公德、觸犯法律的內(nèi)容,一經(jīng)發(fā)現(xiàn),立即刪除,作者需自行承擔(dān)相應(yīng)責(zé)任。涉及到版權(quán)或其他問題,請及時聯(lián)系我們郵件:weilaitui@qq.com。
 

Copyright?2015-2023 粵公網(wǎng)安備 44030702000869號

粵ICP備16078936號

微信

關(guān)注
微信

微信二維碼

WAP二維碼

客服

聯(lián)系
客服

聯(lián)系客服:

24在線QQ: 770665880

客服電話: 020-82301567

E_mail郵箱: weilaitui@qq.com

微信公眾號: weishitui

韓瑞 小英 張澤

工作時間:

周一至周五: 08:00 - 24:00

反饋

用戶
反饋