匯編 - 理解函數(shù)調(diào)用棧

首先介紹下面會用到的幾個寄存器:
rsp : 棧指針寄存器,指向棧頂
rbp : 椞椎伲基址寄存器歉秫,指向棧底
edi : 函數(shù)參數(shù)
rsi/esi : 函數(shù)參數(shù)
eax : 累加器或函數(shù)返回值用

int test2(int a, int b) {
    int v1 = a + 1;
    int v2 = b + 2;
    int c = v1 + v2 + 3;
    return  c + 4;
}

void test1() {
    int a = 1;
    int b = 2;
    int c = a + b + test2(a, b);
}

int main(int argc, const char * argv[]) {
    test1();
    return 0;
}

首先我們要知道然想,函數(shù)棧里面的內(nèi)存地址是從高到低的。
下面從main函數(shù)開始一句句匯編進行解讀:

image.png

0齿尽、初始時
rsp = 0x00007ffeefbff418
rbp = 0x00007ffeefbff428

image.png

1掠河、 pushq %rbp
將rbp的地址壓棧亮元,rsp繼續(xù)指向棧頂,所以我們可以看到
rsp = 0x00007ffeefbff418 - 0x8 = 0x00007ffeefbff410
此時棧頂?shù)刂反娣诺膬?nèi)容就是剛才壓棧的rbp的地址唠摹,即
*0x00007ffeefbff410 = 0x00007ffeefbff428(我這里就用*表示取地址的內(nèi)容爆捞,如果有不懂打印的為什么是反的,可以先去了解下大小端)勾拉。

2煮甥、 movq %rsp, %rbp
將棧頂rsp的值賦值給棧底rbp,即
rsp = rbp = 0x00007ffeefbff410

image.png

3藕赞、 subq $0x10, %rsp
棧頂往下移16個字節(jié)成肘,可以理解成給后面預(yù)留的16字節(jié)的空間。此時
rsp = 0x00007ffeefbff410 - 0x10 = 0x00007ffeefbff400

4斧蜕、 movl $0x0, -0x4(%rbp)
5双霍、 movl %edi, -0x8(%rbp)
6、 movq %rsi, -0x10(%rbp)
這三句可以理解成將寄存器edi和rsi之前的值先用第三步預(yù)留的內(nèi)存存儲下來批销,因為下面調(diào)用函數(shù)里面可能會修改這兩個寄存器里面的值洒闸。

image.png
image.png

7、 callq 0x100002f80
call表示調(diào)用函數(shù)均芽,同時call有一個作用:將call指令的下一條指令地址壓棧丘逸。所以此時棧頂
rsp = 0x00007ffeefbff400 - 0x8 = 0x00007ffeefbff3f8
rbp = 0x00007ffeefbff410
并且里面存放的內(nèi)容就是call下一條指令的地址,即
*0x00007ffeefbff3f8 = 0x100002fdb

從這里開始進入test1函數(shù)

8掀宋、 pushq %rbp
rbp壓棧鸣个,則
rsp = 0x00007ffeefbff3f8 - 0x8 = 0x00007ffeefbff3f0
*0x00007ffeefbff3f0 = 0x00007ffeefbff410

9、 movq %rsp, %rbp
將棧頂rsp的值賦值給棧底rbp布朦,即
rsp = rbp = 0x00007ffeefbff3f0

10囤萤、 subq $0x10, %rsp
棧頂往下移16個字節(jié),可以理解成給test1函數(shù)検桥浚空間分配16字節(jié)內(nèi)存涛舍。
rsp = 0x00007ffeefbff3f0 - 0x10 = 0x00007ffeefbff3e0

image.png

11、 movl $0x1, -0x4(%rbp)
將1放入內(nèi)存地址0x00007ffeefbff3f0 - 0x4中唆途,即
*0x00007ffeefbff3ec = 1
正好對應(yīng)a = 1富雅,所以可以猜測0x00007ffeefbff3ec就是a的地址

12掸驱、 movl $0x2, -0x8(%rbp)
同上,可知將2放入內(nèi)存地址0x00007ffeefbff3f0 - 0x8中没佑,即
*0x00007ffeefbff3e8 = 2
正好對應(yīng) b = 2毕贼,可猜測0x00007ffeefbff3e8就是b的地址

image.png

13、 movl -0x4(%rbp), %eax
14蛤奢、 addl -0x8(%rbp), %eax
15鬼癣、 movl %eax, -0x10(%rbp)
在文章最前面提過eax寄存器一般作為累加器,所以源碼里面的int c = a + b + test2(a, b);這里就很好理解啤贩,前面不是分配了16個字節(jié)的內(nèi)存么待秃,我們只用到了高8個字節(jié)的內(nèi)存分別存儲a、b痹屹,這里將a章郁、b的值通過累加器加起來,再用低8個字節(jié)的內(nèi)存 -0x10(%rbp)存儲a+b的和志衍,即
*0x00007ffeefbff3e0 = 3

image.png

16暖庄、 movl -0x4(%rbp), %edi
17、 movl -0x8(%rbp), %esi
前面提到過寄存器edi楼肪、esi一般用來做函數(shù)參數(shù)存儲培廓。所以這里就是將a的值用edi存儲,b的值用esi存儲淹辞,即
edi = 1
esi = 2
此時rsp = 0x00007ffeefbff3e0
rbp = 0x00007ffeefbff3f0

image.png
image.png

18、 callq 0x100002f50
同第7步俘侠,call的下一條匯編指令地址壓棧象缀,所以
rsp = 0x00007ffeefbff3e0 - 0x8 = 0x00007ffeefbff3d8
*0x00007ffeefbff3d8 = 0x100002faa

從這里開始進入test2函數(shù)

19、 pushq %rbp
rbp壓棧爷速,則
rsp = 0x00007ffeefbff3d8 - 0x8 = 0x00007ffeefbff3d0
*0x00007ffeefbff3d0 = 0x00007ffeefbff3f0

20央星、 movq %rsp, %rbp
將棧頂rsp的值賦值給棧底rbp,即
rsp = rbp = 0x00007ffeefbff3d0

image.png

21惫东、 movl %edi, -0x4(%rbp)
22莉给、 movl %esi, -0x8(%rbp)
把前面用edi、esi存儲的參數(shù)用test2的椓冢空間存儲颓遏,即
*0x00007ffeefbff3cc = 1
*0x00007ffeefbff3c8 = 2

image.png

23、 movl -0x4(%rbp), %eax
24滞时、 addl $0x1, %eax
25叁幢、 movl %eax, -0xc(%rbp)
將參數(shù)0x00007ffeefbff3cc里面存放的值(1)通過累加器eax,計算之后的值放入地址-0xc(%rbp)坪稽,即0x00007ffeefbff3c4 = 2曼玩,正好對應(yīng)源代碼int v1 = a + 1;鳞骤,所以這里v1的地址就是0x00007ffeefbff3c4

image.png

26、 movl -0x8(%rbp), %eax
27黍判、 addl $0x2, %eax
28豫尽、 movl %eax, -0x10(%rbp)
將參數(shù)0x00007ffeefbff3c8里面存放的值(2)通過累加器eax,計算之后的值放入地址-0x10(%rbp)顷帖,即0x00007ffeefbff3c0 = 2美旧,正好對應(yīng)源代碼int v2 = b + 2;;,所以這里v2的地址就是0x00007ffeefbff3c0

image.png

29窟她、 movl -0xc(%rbp), %eax
30陈症、 addl -0x10(%rbp), %eax
31、 addl $0x3, %eax
32震糖、 movl %eax, -0x14(%rbp)

這里就是通過累加器eax將-0xc(%rbp)和-0x10(%rbp)里面的值相加录肯,再加上3,放入地址-0x14(%rbp)中吊说,正好對應(yīng)int c = v1 + v2 + 3;论咏,所以這里c的地址就是-0x14(%rbp)即0x00007ffeefbff3bc,且*0x00007ffeefbff3bc = 9

image.png

33颁井、 movl -0x14(%rbp), %eax
34厅贪、 addl $0x4, %eax
這里將前面計算的c的值加4,將結(jié)果放入寄存器eax雅宾,前面提到eax也做函數(shù)返回养涮,所以此時eax = 13

image.png

35、 popq %rbp
這句指令表示出棧眉抬,同時將出棧的值放入寄存器rbp贯吓,所以有
rsp = 0x00007ffeefbff3d0 + 0x8 = 0x00007ffeefbff3d8
rbp = *0x00007ffeefbff3d0 = 0x00007ffeefbff3f0

image.png
從這里開始退出test2函數(shù)

36、 retq
這句表示退出test2函數(shù)蜀变,同時出棧悄谐,并且斷點跳到出棧值的地址,所以可以看到
rsp = 0x00007ffeefbff3d8 + 0x8 = 0x00007ffeefbff3e0库北,而之前
*0x00007ffeefbff3d8 = 0x100002faa爬舰,所以此時跳到0x100002faa
同時我們可以發(fā)現(xiàn)棧頂rsp = 0x00007ffeefbff3e0 棧底rbp = 0x00007ffeefbff3f0寒瓦,與進入test2函數(shù)之前的值保持一致情屹,說明函數(shù)在調(diào)用前后會保持棧平衡,即從哪里開始杂腰,最后又會回到哪里屁商。

從這里開始回到test1函數(shù)
image.png

37、 movl %eax, %ecx
前面提到test2的返回值存放在寄存器eax,這里先將返回值用寄存器ecx存儲蜡镶,即ecx = 13

image.png

38雾袱、 movl -0x10(%rbp), %eax
39、 addl %ecx, %eax
40官还、 movl %eax, -0xc(%rbp)
因為之前a+b的值存在地址-0x10(%rbp)中芹橡,這里這三句正好對應(yīng)int c = a + b + test2(a, b);,其中-0xc(%rbp)就是c的地址望伦,所以有
*0x7ffeefbff3e4 = 16

image.png

41林说、 addq $0x10, %rsp
這句正好對應(yīng)前面的subq $0x10, %rsp,此時
rsp = 0x00007ffeefbff3e0 + 0x10 = 0x00007ffeefbff3f0

image.png

42屯伞、 popq %rbp
這句指令表示出棧腿箩,同時將出棧的值放入寄存器rbp,所以有
rsp = 0x00007ffeefbff3f0 + 0x8 = 0x00007ffeefbff3f8
rbp = *0x00007ffeefbff3f0 = 0x00007ffeefbff410

image.png
從這里開始退出test1函數(shù)

43劣摇、 retq
這句表示退出test1函數(shù)珠移,同時出棧,并且斷點跳到出棧值的地址末融,所以可以看到
rsp = 0x00007ffeefbff3f8 + 0x8 = 0x00007ffeefbff400钧惧,而之前
*0x00007ffeefbff3f8 = 0x100002fdb,所以此時跳到0x100002fdb勾习。
同時我們可以發(fā)現(xiàn)棧頂rsp = 0x00007ffeefbff400 棧底rbp = 0x00007ffeefbff410浓瞪,與進入test1函數(shù)之前的值保持一致,再次驗證了前面的觀點巧婶。

44乾颁、 xorl %eax, %eax
因為函數(shù)test1無返回值,所以這里eax也沒實際用到

image.png

45艺栈、 addq $0x10, %rsp
這句正好對應(yīng)前面的subq $0x10, %rsp英岭,此時
rsp = 0x00007ffeefbff400 + 0x10 = 0x00007ffeefbff410

image.png

46、 popq %rbp
這句指令表示出棧眼滤,同時將出棧的值放入寄存器rbp巴席,所以有
rsp = 0x00007ffeefbff410 + 0x8 = 0x00007ffeefbff418
rbp = *0x00007ffeefbff410 = 0x00007ffeefbff428
此時也正好對應(yīng)初始時棧頂和棧底的值历涝。

總結(jié):函數(shù)調(diào)用會保持棧平衡
補充:函數(shù)遞歸沒有退出條件之所以會造成死循環(huán)诅需,就是因為rsp會一直往下減,直至減到棧區(qū)范圍外荧库,這樣就會造成棧溢出堰塌。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市分衫,隨后出現(xiàn)的幾起案子场刑,更是在濱河造成了極大的恐慌,老刑警劉巖蚪战,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牵现,死亡現(xiàn)場離奇詭異铐懊,居然都是意外死亡,警方通過查閱死者的電腦和手機瞎疼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門科乎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贼急,你說我怎么就攤上這事茅茂。” “怎么了太抓?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵空闲,是天一觀的道長。 經(jīng)常有香客問我走敌,道長碴倾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任悔常,我火速辦了婚禮影斑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘机打。我一直安慰自己矫户,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布残邀。 她就那樣靜靜地躺著皆辽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芥挣。 梳的紋絲不亂的頭發(fā)上驱闷,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音空免,去河邊找鬼空另。 笑死,一個胖子當(dāng)著我的面吹牛蹋砚,可吹牛的內(nèi)容都是我干的扼菠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼坝咐,長吁一口氣:“原來是場噩夢啊……” “哼循榆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起墨坚,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秧饮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盗尸,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡柑船,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泼各。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椎组。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖历恐,靈堂內(nèi)的尸體忽然破棺而出寸癌,到底是詐尸還是另有隱情,我是刑警寧澤弱贼,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布蒸苇,位于F島的核電站,受9級特大地震影響吮旅,放射性物質(zhì)發(fā)生泄漏溪烤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一庇勃、第九天 我趴在偏房一處隱蔽的房頂上張望檬嘀。 院中可真熱鬧,春花似錦责嚷、人聲如沸鸳兽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽揍异。三九已至,卻和暖如春爆班,著一層夾襖步出監(jiān)牢的瞬間衷掷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工柿菩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留戚嗅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓枢舶,卻偏偏與公主長得像懦胞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子祟辟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

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