進程的虛擬內存中的標準內存段佈局
4G 0xffff ffff kernel space, user code write or read segmentation fault
3G 0xbfff ffff random stack offset
rlimit_stack, 8M
random mmap offset
memory mapping segment, libc
heap
bss
data
text
0 reserved
Random stack offset和Random mmap offset等隨機值意在防止惡意程序
Linux通過對棧、內存映射段、堆的起始地址加上隨機偏移量來打亂佈局,以免惡意程序通過計算訪問棧、庫函數等地址
堆和棧都是匿名頁,堆向上,棧向下
函數調用,通過彙編查看堆棧過程
源碼:
int bar(int c, int d)
{
int e = c + d;
return e;
}
int foo(int a, int b)
{
return bar(a, b);
}
int main(void)
{
foo(2, 5);
return 0;
}
彙編:objdump -Sd
0000000000400506 <main>:
int main(void)
{ main 被 _start調用
400506: 55 push %rbp 棧底地址入棧,棧開始
400507: 48 89 e5 mov %rsp,%rbp 建立main函數的棧幀,幀開始bp爲_start的sp
foo(2, 5);
40050a: be 05 00 00 00 mov $0x5,%esi si是存儲第二個變量寄存器
40050f: bf 02 00 00 00 mov $0x2,%edi di是存儲第一個變量寄存器
400514: e8 ce ff ff ff callq 4004e7 <foo> call函數foo,相當於
pushq %rip ip=400519入棧,sp-8
jmpq addr ip執行4004e7
return 0;
400519: b8 00 00 00 00 mov $0x0,%eax
}
40051e: 5d pop %rbp 棧開始地址回到_start
40051f: c3 retq popq %rip,ip出棧執行400519
00000000004004e7 <foo>:
int foo(int a, int b)
{
4004e7: 55 push %rbp
4004e8: 48 89 e5 mov %rsp,%rbp
4004eb: 48 83 ec 08 sub $0x8,%rsp 建立foo的棧幀,向下擴展sp
4004ef: 89 7d fc mov %edi,-0x4(%rbp) 傳參1入棧
4004f2: 89 75 f8 mov %esi,-0x8(%rbp) 傳參2入棧
return bar(a, b);
4004f5: 8b 55 f8 mov -0x8(%rbp),%edx 參數1此處無計算,直接存儲
4004f8: 8b 45 fc mov -0x4(%rbp),%eax 參數2此處無計算,直接存儲
4004fb: 89 d6 mov %edx,%esi si是存儲第二個變量寄存器
4004fd: 89 c7 mov %eax,%edi di是存儲第一個變量寄存器
4004ff: e8 c9 ff ff ff callq 4004cd <bar> pushq %rip ip 400504入棧,sp-8
jmpq addr ip執行4004cd
}
400504: c9 leaveq 與函數進入對應,恢復上級的棧地址
movq %rbp, %rsp 棧開始爲上級結束
popq %rbp 出棧爲上級開始
400505: c3 retq popq %rip,ip出棧執行
int bar(int c, int d)
{
4004cd: 55 push %rbp
4004ce: 48 89 e5 mov %rsp,%rbp 不擴建棧幀,最後一個函數不必要保存局部變量
4004d1: 89 7d ec mov %edi,-0x14(%rbp) 直接獲取上層的入參,這裏變量e是棧前4字節
4004d4: 89 75 e8 mov %esi,-0x18(%rbp)
int e = c + d;
4004d7: 8b 45 e8 mov -0x18(%rbp),%eax
4004da: 8b 55 ec mov -0x14(%rbp),%edx
4004dd: 01 d0 add %edx,%eax
4004df: 89 45 fc mov %eax,-0x4(%rbp)
return e;
4004e2: 8b 45 fc mov -0x4(%rbp),%eax
}
4004e5: 5d pop %rbp
4004e6: c3 retq
(gdb) info registers rbp rsp
rbp 0x7fffffffe478 0x7fffffffe478
rsp 0x7fffffffe478 0x7fffffffe478
(gdb) x/16x
0x7fffffffe478: 0xffffe490 0x00007fff 0x00400504 0x00000000
0x7fffffffe488: 0x00000005 0x00000002 0xffffe4a0 0x00007fff
0x7fffffffe498: 0x00400519 0x00000000 0x00000000 0x00000000
0x7fffffffe4a8: 0xf7a30445 0x00007fff 0x00000000 0x00000000
當遞歸調用是整個函數體中最後執行的語句且它的返回值不屬於表達式的一部分時,這個遞歸調用就是尾遞歸
當編譯器檢測到一個函數調用是尾遞歸的時候,它就覆蓋當前的活動記錄而不是在棧中去創建一個新的
尾遞歸,彙編將沒有call,而是直接跳轉
控制棧的增長,且減少壓棧,程序運行的效率也可能更高