這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
說明
電腦中的棧一個很大的應用場合使用在函數調用中。我們這裡簡單說說golang的協程棧布局,學過電腦的應該都不會陌生。
程式案例
package mainfunc f(a, b int) int { sum := 0 sum = a + b for i := 0; i < 1000; i++ { println("sum is:", sum) } return sum}func main() { f(1, 2)}
彙編代碼
(gdb) disasDump of assembler code for function main.main:0x00000000004010b0 <main.main+0>: mov %fs:0xfffffffffffffff8,%rcx0x00000000004010b9 <main.main+9>: cmp 0x10(%rcx),%rsp0x00000000004010bd <main.main+13>: jbe 0x4010de <main.main+46>0x00000000004010bf <main.main+15>: sub $0x18,%rsp0x00000000004010c3 <main.main+19>: movq $0x1,(%rsp)0x00000000004010cb <main.main+27>: movq $0x2,0x8(%rsp)0x00000000004010d4 <main.main+36>: callq 0x401000 <main.f>0x00000000004010d9 <main.main+41>: add $0x18,%rsp0x00000000004010dd <main.main+45>: retq0x00000000004010de <main.main+46>: callq 0x44abd0 <runtime.morestack_noctxt>0x00000000004010e3 <main.main+51>: jmp 0x4010b0 <main.main>0x00000000004010e5 <main.main+53>: add %al,(%rax)0x00000000004010e7 <main.main+55>: add %al,(%rax)0x00000000004010e9 <main.main+57>: add %al,(%rax)0x00000000004010eb <main.main+59>: add %al,(%rax)0x00000000004010ed <main.main+61>: add %al,(%rax)0x00000000004010ef <main.main+63>: add %ah,-0x75(%rax,%rcx,2)End of assembler dump.(gdb) disasDump of assembler code for function main.f:0x0000000000401000 <main.f+0>: mov %fs:0xfffffffffffffff8,%rcx0x0000000000401009 <main.f+9>: cmp 0x10(%rcx),%rsp0x000000000040100d <main.f+13>: jbe 0x401097 <main.f+151>0x0000000000401013 <main.f+19>: sub $0x20,%rsp0x0000000000401017 <main.f+23>: mov 0x28(%rsp),%rbx0x000000000040101c <main.f+28>: mov 0x30(%rsp),%rbp0x0000000000401021 <main.f+33>: add %rbp,%rbx0x0000000000401024 <main.f+36>: mov %rbx,0x10(%rsp)0x0000000000401029 <main.f+41>: xor %eax,%eax0x000000000040102b <main.f+43>: mov %rax,0x18(%rsp)0x0000000000401030 <main.f+48>: cmp $0x3e8,%rax0x0000000000401036 <main.f+54>: jge 0x401088 <main.f+136>......0x0000000000401088 <main.f+136>: mov 0x10(%rsp),%rbx0x000000000040108d <main.f+141>: mov %rbx,0x38(%rsp)0x0000000000401092 <main.f+146>: add $0x20,%rsp
執行過程中stack變化情況
在main調用f()時,協程堆棧情況:
注意:這裡的返回地址是由call指令自動push至esp所指向記憶體,而參數內容則是由調用者main函數設定的,如下代碼:
// we have 2 argument and 1 return value// so must reserve 24 bytes in amd64(0x18)0x00000000004010bf <main.main+15>: sub $0x18,%rsp0x00000000004010c3 <main.main+19>: movq $0x1,(%rsp)0x00000000004010cb <main.main+27>: movq $0x2,0x8(%rsp)0x00000000004010d4 <main.main+36>: callq 0x401000 <main.f>
在f函數內部執行時,會擴充當前stack,為了臨時儲存一些本地變數,如sum等,則f執行時堆棧情況如下:
可以看到為本地變數sum和i自動在棧上分配了儲存空間,計算sum,然後將sum的值儲存到f()傳回值該去的地方((esp) + 0x38)
可以簡單看看main.f()的主要彙編代碼
// sub esp to allocate space for local variable0x0000000000401013 <main.f+19>: sub $0x20,%rsp// get parameters, compute and store sum 0x0000000000401017 <main.f+23>: mov 0x28(%rsp),%rbx0x000000000040101c <main.f+28>: mov 0x30(%rsp),%rbp0x0000000000401021 <main.f+33>: add %rbp,%rbx// store sum in (esp) + 0x100x0000000000401024 <main.f+36>: mov %rbx,0x10(%rsp)// for loop assemble code0x0000000000401029 <main.f+41>: xor %eax,%eax0x000000000040102b <main.f+43>: mov %rax,0x18(%rsp)0x0000000000401030 <main.f+48>: cmp $0x3e8,%rax0x0000000000401036 <main.f+54>: jge 0x401088 <main.f+136>......// store sum into return value address(esp + 0x38)// and shrink stack((%esp) + 0x20) and return to main 0x0000000000401088 <main.f+136>: mov 0x10(%rsp),%rbx0x000000000040108d <main.f+141>: mov %rbx,0x38(%rsp)0x0000000000401092 <main.f+146>: add $0x20,%rsp
參考資料
http://www.cs.nyu.edu/courses/fall04/V22.0201-003/ia32_chap_03.pdf