匯編一個簡單的C程序引發(fā)的思考

如果不思考宝鼓,單純地做做試驗刑棵,此次實驗很簡單,但這幾句簡簡單單的代碼卻包括了底層的機制:如參數(shù)是如何傳遞的蛉签,堆棧是如何增長的胡陪,各個寄存器的作用又是怎樣的。

實驗截圖如下碍舍,不是很復雜柠座。

2514111487474604488-wm.png

2514111487425283904-wm.png

2514111487404404260-wm.png

刪除指導匯編器和鏈接器的命令(即是那些以.開頭的),得到真正對我們分析匯編代碼有用的這一部分。

匯編代碼:

g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $3, %eax
popl %ebp
ret

f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret

main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
leave
ret

C語言代碼:

int g(int x)
{
  return x + 3;
}

int f(int x)
{
  return g(x);
}

int main(void)
{
  return f(8) + 1;
}

這個程序中有3層調(diào)用,main函數(shù)調(diào)用f(),f()調(diào)用g(),
main函數(shù)運行授滓。
程序是這樣運行的,每個函數(shù)都有屬于自己的堆棧副编,這一段堆棧叫做棧幀,在32位機上由ebp(幀指針)和esp(棧指針)來劃定范圍。

main函數(shù)執(zhí)行的時候,先把自己ebp的值保存下來爆哑,然后,再將ebp賦值為esp了嚎,這樣便開始了一個新的棧幀泪漂,此時ebp和esp指向了同一處,而這個內(nèi)存單元里面保存的是ebp的值歪泳,esp便開始了對棧幀大小的修改。

為什么要保存ebp的值呢露筒,在函數(shù)運行的過程中呐伞,ebp一般是不會被修改的,當然慎式,除了下面這兩句:
pushl %ebp
movl %esp, %ebp

這兩句匯編代碼巧妙地只用了一個ebp寄存器就成功地區(qū)分開兩個函數(shù)的棧幀伶氢,因為它保存著上一個函數(shù)的esp的值,ebp的位置又固定不變瘪吏。

之前講過癣防,為了區(qū)分開每個函數(shù)的棧幀,需要保存ebp和esp掌眠,因為在每個函數(shù)的執(zhí)行過程中蕾盯,想獲取當前棧幀的數(shù)據(jù),而esp又是變化的蓝丙,無法把esp當作可靠的參考级遭,此時之前保存的ebp就派上了用場,因為ebp恒定指向此棧幀的最開始渺尘。

更為有意思的是以ebp作為參考這種機制在32位機中參數(shù)的傳遞有著重要的意義挫鸽,接下來在g()函數(shù)執(zhí)行的過程中,就可以看到這樣設計的妙處

接下來的

subl $4, %esp
movl $8, (%esp)

是為調(diào)用f()準備好參數(shù)鸥跟,參數(shù)的壓棧順序是從右向左壓棧丢郊,分別是參數(shù)n參數(shù)n-1,直到參數(shù)1。本次只有一個參數(shù)枫匾,故只保存了立即數(shù)8到棧中迅诬。

有一點是需要記住的,只有32位系統(tǒng)才有棧幀的概念婿牍,并且只能通過棧來傳遞參數(shù)侈贷,而64位機則是主要通過寄存器傳參,只有當寄存器不夠用才使用棧等脂,這樣帶來的好處就是效率更高俏蛮。

調(diào)用f()函數(shù)call f肯定會做一件事情,即將返回地址addl $1, %eax指令的地址壓棧上遥。f()函數(shù)運行的時候搏屑,同樣的道理,保存當前的ebp值粉楚,設定ebp的指向辣恋。
subl $4, %esp,為壓棧做準備模软。

movl 8(%ebp), %eax
movl %eax, (%esp)```
則是通過對ebp的帶偏移量的寄存器間接尋址(***基址+偏移量***)找到main函數(shù)保存的參數(shù)值伟骨。因為堆棧是向下增長,那么ebp+4,指向的是上個函數(shù)的ret地址燃异,ebp+8則指向的是我們保存的參數(shù)8携狭。`movl 8(%ebp), %eax`將8保存到自己的堆棧中去。然后調(diào)用g()函數(shù)回俐。

***不過我覺得如果使用GCC匯編的時候逛腿,優(yōu)化等級調(diào)高,那么仅颇,完全沒必要浪費這2條指令单默,因為本身f()函數(shù)什么都沒做,只是負責傳遞參數(shù)給g()而已忘瓦,那么直接利用g()的ebp便可尋址到傳入的參數(shù)搁廓。***
*其實,可能直接把g()函數(shù)給優(yōu)化掉了吧政冻?*

g()函數(shù)中的形成棧幀的前兩句不再多解釋枚抵,`movl 8(%ebp), %eax`是把傳入的參數(shù)8存儲到eax中,接下來的`addl $3, %eax`則把3加到eax中明场,`popl %ebp`則是恢復上個函數(shù)的ebp汽摹,為ret做準備。
`ret`又做了什么呢苦锨?`ret`相當于`pop %eip`逼泣,恢復eip的值趴泌,即調(diào)用者的下一條語句。在這個代碼中拉庶,eip被賦值為f()函數(shù)中l(wèi)eave指令的地址嗜憔,同時由eax返回結(jié)果11。

接下來到了f()中的`leave`,也就相當于

movl %ebp氏仗,%esp
popl %ebp

第一條把ebp的值賦值給esp吉捶,原因在于在當前的棧幀中ebp保存的是當前棧幀的esp值(還記得`movl %esp, %ebp`嗎?)皆尔,esp指向的地方實際保存著ebp呐舔,`popl %ebp`則把保存的ebp彈出來(`pushl %ebp`),然后ret指令彈出eip慷蠕,返回eax到main函數(shù)珊拼。
main函數(shù)中,把1加到eax上流炕,重復同樣的動作澎现,整個程序結(jié)束。
最后的結(jié)構(gòu)大概是這樣:


![Paste_Image.png](http://upload-images.jianshu.io/upload_images/4811496-1f8e875869ae3bbb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

至于說GCC堅持的x86編程指導防止每辟,也就是說一個函數(shù)使用的所有椊1瑁空間必須是16字節(jié)的整數(shù)倍(包括保存的%ebp值的4個字節(jié)和返回值的4個字節(jié)),在這個代碼中并未體現(xiàn)出來影兽,不知道是不是因為GCC比較新的緣故揭斧。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市峻堰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盅视,老刑警劉巖捐名,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闹击,居然都是意外死亡镶蹋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門赏半,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贺归,“玉大人,你說我怎么就攤上這事断箫》骱ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵仲义,是天一觀的道長婶熬。 經(jīng)常有香客問我剑勾,道長,這世上最難降的妖魔是什么赵颅? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任虽另,我火速辦了婚禮,結(jié)果婚禮上饺谬,老公的妹妹穿的比我還像新娘捂刺。我一直安慰自己,他們只是感情好募寨,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布族展。 她就那樣靜靜地躺著,像睡著了一般绪商。 火紅的嫁衣襯著肌膚如雪苛谷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天格郁,我揣著相機與錄音腹殿,去河邊找鬼。 笑死例书,一個胖子當著我的面吹牛锣尉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播决采,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼自沧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了树瞭?” 一聲冷哼從身側(cè)響起拇厢,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晒喷,沒想到半個月后孝偎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡凉敲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年衣盾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爷抓。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡势决,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蓝撇,到底是詐尸還是另有隱情果复,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布唉地,位于F島的核電站据悔,受9級特大地震影響传透,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜极颓,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一朱盐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧菠隆,春花似錦兵琳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至破衔,卻和暖如春清女,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晰筛。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工嫡丙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人读第。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓曙博,卻偏偏與公主長得像,于是被迫代替她去往敵國和親怜瞒。 傳聞我的和親對象是個殘疾皇子父泳,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容