函數(shù)調(diào)用是通過棧來實(shí)現(xiàn)的,而且知道在棧中存放著該函數(shù)的局部變量。但是,對(duì)于棧的實(shí)現(xiàn)細(xì)節(jié)可能不一定清楚。本文將介紹一下在Linux平臺(tái)下函數(shù)棧是如何實(shí)現(xiàn)的。
棧幀的結(jié)構(gòu)
函數(shù)在調(diào)用的時(shí)候都是在棧空間上開辟一段空間以供函數(shù)使用,棧是由高地址向地地址的方向生長(zhǎng)的,而且棧有其棧頂和棧底,入棧出棧的地方就叫做棧頂。
在x86系統(tǒng)的CPU中,rsp是棧指針寄存器,這個(gè)寄存器中存儲(chǔ)著棧頂?shù)牡刂贰bp中存儲(chǔ)著棧底的地址。函數(shù)棧空間主要是由這兩個(gè)寄存器來確定的。
當(dāng)程序運(yùn)行時(shí),棧指針rsp可以移動(dòng),棧指針和幀指針rbp一次只能存儲(chǔ)一個(gè)地址,所以,任何時(shí)候,這一對(duì)指針指向的是同一個(gè)函數(shù)的棧幀結(jié)構(gòu)。
而幀指針rbp是不移動(dòng)的,訪問棧中的元素可以用-4(%rbp)或者8(%rbp)訪問%rbp指針下面或者上面的元素。
測(cè)試代碼如下:
#include int sum (int a,int b) { int c = a + b; return c; } int main() { int x = 5,y = 10,z = 0; z = sum(x,y); printf("%drn",z); return 0; }
0000000000000000 : 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 89 7d ec mov %edi,-0x14(%rbp) # 參數(shù)傳遞 7: 89 75 e8 mov %esi,-0x18(%rbp) # 參數(shù)傳遞 a: 8b 55 ec mov -0x14(%rbp),%edx d: 8b 45 e8 mov -0x18(%rbp),%eax 10: 01 d0 add %edx,%eax 12: 89 45 fc mov %eax,-0x4(%rbp) # 局部變量 15: 8b 45 fc mov -0x4(%rbp),%eax # 存儲(chǔ)結(jié)果 18: 5d pop %rbp 19: c3 retq 000000000000001a : 1a: 55 push %rbp # 保存%rbp。rbp,棧底的地址 1b: 48 89 e5 mov %rsp,%rbp # 設(shè)置新的棧指針。rsp 棧指針,指向棧頂?shù)牡刂? 1e: 48 83 ec 10 sub $0x10,%rsp # 分配 16字節(jié)棧空間。%rsp = %rsp-16 22: c7 45 f4 05 00 00 00 movl $0x5,-0xc(%rbp) # 賦值 29: c7 45 f8 0a 00 00 00 movl $0xa,-0x8(%rbp) # 賦值 30: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp) # 賦值 37: 8b 55 f8 mov -0x8(%rbp),%edx 3a: 8b 45 f4 mov -0xc(%rbp),%eax 3d: 89 d6 mov %edx,%esi # 參數(shù)傳遞 ,從右向左 3f: 89 c7 mov %eax,%edi # 參數(shù)傳遞 41: e8 00 00 00 00 callq 46 # 調(diào)用sum 46: 89 45 fc mov %eax,-0x4(%rbp) 49: 8b 45 fc mov -0x4(%rbp),%eax # 存儲(chǔ)計(jì)算結(jié)果 4c: 89 c6 mov %eax,%esi 4e: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 55 55: b8 00 00 00 00 mov $0x0,%eax 5a: e8 00 00 00 00 callq 5f 5f: b8 00 00 00 00 mov $0x0,%eax 64: c9 leaveq 65: c3 retq +0x45>+0x3b>+0x2c>
在函數(shù)被調(diào)用之前,調(diào)用者會(huì)為調(diào)用函數(shù)做準(zhǔn)備。首先,函數(shù)棧上開辟了16字節(jié)的空間,存儲(chǔ)定義的3個(gè)int型變量,建立了main函數(shù)的棧。
CALL指令內(nèi)部其實(shí)還暗含了一個(gè)將返回地址(即CALL指令下一條指令的地址)壓棧的動(dòng)作(由硬件完成)。
具體來說,call指令執(zhí)行時(shí),先把下一條指令的地址入棧,再跳轉(zhuǎn)到對(duì)應(yīng)函數(shù)執(zhí)行的起始處。
審核編輯:湯梓紅
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3614瀏覽量
129636 -
Linux
+關(guān)注
關(guān)注
87文章
11336瀏覽量
210097 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4344瀏覽量
62864
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論