進程棧調用

進程的虛擬內存中的標準內存段佈局
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,而是直接跳轉
控制棧的增長,且減少壓棧,程序運行的效率也可能更高

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章