函數(shù)調(diào)用本質(zhì)

文章來(lái)源:CoderHong 的博客

從反匯編角度窺探平時(shí)開發(fā)調(diào)用的函數(shù)或者方法的本質(zhì)产镐。平時(shí)我們編寫的高級(jí)語(yǔ)言最終通過(guò)編譯器鹅龄、鏈接生成機(jī)CPU執(zhí)行的機(jī)器指令驴娃。 不同的CPU對(duì)應(yīng)著不同著機(jī)器指令花竞,并且每一條機(jī)器指令對(duì)應(yīng)著一條匯編署浩。

先看一個(gè)最簡(jiǎn)單的C語(yǔ)言函數(shù)揉燃,這里主要通過(guò)C++來(lái)反編譯分析匯編指令。

1.png

可以通過(guò)反匯編看到調(diào)用func函數(shù)的匯編指令筋栋,當(dāng)前環(huán)境是8086匯編炊汤。

2.png

通過(guò)最終的匯編指令可以看出,在執(zhí)行調(diào)用一個(gè)函數(shù):本質(zhì)就是通過(guò)call指令調(diào)用函數(shù)在代碼段的地址進(jìn)行直接調(diào)用弊攘。

注意:在上面的匯編指令可以看到當(dāng)函數(shù)執(zhí)行完畢抢腐,執(zhí)行ret匯編指令退出函數(shù)。其實(shí)一個(gè)完整的函數(shù)調(diào)用必定包含callret指令襟交。

那么只有了解了callret才能徹底從最根本了解函數(shù)的調(diào)用過(guò)程迈倍。

call 標(biāo)號(hào)
1.將下一條指令的偏移地址入棧
2.轉(zhuǎn)到標(biāo)號(hào)出執(zhí)行指令
ret
將棧頂?shù)闹党鰲#x值給IP

下面通過(guò)匯編代碼調(diào)用 printf 函數(shù)標(biāo)號(hào)打印 HelloWorld 執(zhí)行驗(yàn)證上面的結(jié)論捣域。

3.png

在即將執(zhí)行執(zhí)行 printf 函數(shù)之前棧頂指針SP指向內(nèi)存單元的數(shù)據(jù)啼染。

4.png

上面說(shuō)到執(zhí)行函數(shù)前會(huì)將下一條指令的偏移地址入棧醋界,上圖可以看出的下一條CPU執(zhí)行的指令偏移地址IP為:000D。開始執(zhí)行提完,看下棧頂指針SP的指向和指向內(nèi)存單元的數(shù)據(jù)

5.png

函數(shù) printf 執(zhí)行完畢后,執(zhí)行 ret 指令丘侠,棧頂偏移地址出棧賦值給 IP 中徒欣,棧頂指針向上移動(dòng)兩個(gè)字節(jié)。

6.png

不管什么開發(fā)語(yǔ)言最終都會(huì)轉(zhuǎn)成二進(jìn)制匯編指令蜗字,對(duì)應(yīng)著相應(yīng)的匯編指令打肝,本質(zhì)都是一致的。這里是通過(guò)C++反匯編窺探函數(shù)調(diào)用本質(zhì)挪捕。

上述介紹只是最簡(jiǎn)單函數(shù)調(diào)用粗梭,一說(shuō)到函數(shù)首先就會(huì)想到函數(shù)的三要素,函數(shù)的返回值级零、函數(shù)的參數(shù)断医、局部變量**。

返回值

如果調(diào)用函數(shù)想拿到函數(shù)返回值奏纪,就得有容器來(lái)存放返回值鉴嗤,我們可以想到用棧、數(shù)據(jù)區(qū)序调、寄存器來(lái)保存醉锅。

首先棧段不可以的,如下圖发绢,函數(shù)內(nèi)部push返回值硬耍,棧頂存儲(chǔ)的是CPU函數(shù)執(zhí)行完畢后的IP的偏移地址。

7.png

可以考慮將返回值放入數(shù)據(jù)段边酒,這個(gè)需要與調(diào)用者約好協(xié)議经柴,比如約定好將返回值放在ds:[0]

8.png

這樣側(cè)面證明了數(shù)據(jù)段里的數(shù)據(jù)是全局,全局區(qū)的數(shù)據(jù)是作用域是全局的甚纲。上面的實(shí)例代碼好比下面的C++代碼口锭。

9.png

在實(shí)際中,大多數(shù)平臺(tái)介杆,windows鹃操、linux、Android等通常的做法是將方法返回值放在寄存器ax春哨。其實(shí)這樣的效率比上面返回值放在全局區(qū)效率高荆隘,CPU從寄存器中讀取數(shù)據(jù)要快,放在全局區(qū)需要從內(nèi)存先讀取到寄存器赴背。

10.png

下面在X86環(huán)境下寫一段代碼看下匯編指令

11.png

參數(shù)

同樣我們先考慮將參數(shù)放入數(shù)據(jù)段來(lái)實(shí)現(xiàn)一個(gè)求和的函數(shù)椰拒。

12.png

放在數(shù)據(jù)段是可以的晶渠,在我們概念中形參的作用于是數(shù)據(jù)函數(shù)內(nèi)部,函數(shù)執(zhí)行完畢形參所占用的內(nèi)存空間會(huì)被回收燃观。這樣就很明顯了褒脯,通常,形參是放在棧中的缆毁。

13.png

注意:在函數(shù)調(diào)用完畢后番川,一定要保證棧平衡,否者會(huì)導(dǎo)致棧的空間會(huì)被用完脊框,通常保持棧平衡有兩種方式:內(nèi)平棧和外平棧颁督。

上面的案例是使用了外平棧方式,也就是在函數(shù)調(diào)用完畢后浇雹,對(duì)棧頂指針進(jìn)行回復(fù)到函數(shù)調(diào)用前的位置沉御。

14.png

對(duì)于函數(shù)的封裝性跟人覺的棧內(nèi)平衡的方式會(huì)好一些,讓函數(shù)調(diào)用者不用關(guān)心內(nèi)部細(xì)節(jié)昭灵。函數(shù)的形參本質(zhì)了解后吠裆,接下來(lái)窺探最后一個(gè)函數(shù)的局部變量本質(zhì),這個(gè)相對(duì)復(fù)雜一些烂完。

局部變量

函數(shù)的內(nèi)部需要定義局部變量硫痰,C語(yǔ)言特別簡(jiǎn)單,那么在匯編中怎么分配內(nèi)存空間給局部變量呢窜护,局部變量的作用域只是當(dāng)前函數(shù)效斑,函數(shù)執(zhí)行完畢后局部所棧中的空間被回收,因此局部變量空間分配還是通過(guò)棧來(lái)實(shí)現(xiàn)柱徙。

15.png

上面開始沒有問(wèn)題缓屠,唯一缺陷是在函數(shù)內(nèi)部調(diào)用函數(shù)時(shí),由于我們沒有對(duì)bp進(jìn)行恢復(fù)护侮,一旦對(duì)函數(shù)內(nèi)部在調(diào)用函數(shù)就會(huì)存存在問(wèn)題敌完, 因此需要對(duì)bp進(jìn)行記錄和恢復(fù)。

16.png
17.png
18.png

函數(shù)的調(diào)用流程總結(jié)

1  push參數(shù)羊初,參數(shù)入棧
2  將函數(shù)的返回地址(下一條指令的地址)入棧
3  保護(hù)sp滨溉,將sp賦值給bp
4  分配一定的空間給函數(shù)的局部變量使用(讓sp減去該空間大小)长赞,為了安全晦攒,用CC填充(int 3h)
5  保護(hù)寄存器, 因?yàn)樵诤瘮?shù)執(zhí)行過(guò)程中會(huì)修改寄存器的值,所以在修改之前保存一下之前的值得哆,后面再還原
6  具體的業(yè)務(wù)代碼
7  恢復(fù)寄存器的值脯颜,跟第5步相反
8  將bp賦值給sp,恢復(fù)bp
9  返回(ret)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贩据,一起剝皮案震驚了整個(gè)濱河市栋操,隨后出現(xiàn)的幾起案子闸餐,更是在濱河造成了極大的恐慌,老刑警劉巖矾芙,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舍沙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡剔宪,警方通過(guò)查閱死者的電腦和手機(jī)场勤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)歼跟,“玉大人,你說(shuō)我怎么就攤上這事格遭」郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵拒迅,是天一觀的道長(zhǎng)骚秦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)璧微,這世上最難降的妖魔是什么作箍? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮前硫,結(jié)果婚禮上胞得,老公的妹妹穿的比我還像新娘。我一直安慰自己屹电,他們只是感情好阶剑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著危号,像睡著了一般牧愁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上外莲,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天猪半,我揣著相機(jī)與錄音,去河邊找鬼偷线。 笑死磨确,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的声邦。 我是一名探鬼主播俐填,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼翔忽!你這毒婦竟也來(lái)了英融?” 一聲冷哼從身側(cè)響起盏檐,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驶悟,沒想到半個(gè)月后胡野,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡痕鳍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年硫豆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笼呆。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡熊响,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诗赌,到底是詐尸還是另有隱情汗茄,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布铭若,位于F島的核電站洪碳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叼屠。R本人自食惡果不足惜瞳腌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望镜雨。 院中可真熱鬧嫂侍,春花似錦、人聲如沸荚坞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)西剥。三九已至痹栖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瞭空,已是汗流浹背揪阿。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咆畏,地道東北人南捂。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像旧找,于是被迫代替她去往敵國(guó)和親溺健。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345