GO語解惑:從源碼分析GO程序的入口(續:_rt0_go)

書接上回。如今咱們知道_rt0_go這個彙編過程是GO語言的真正入口並作了一些初始化工做,本文來大略的過一下這個過程和它調用的幾個runtime裏的函數。linux

Windwos Live Writer寫的文章,編碼有問題,對不住各位:我不想改。windows

 函數

參數

MOVL    argc+0(FP), AX  
MOVL    argv+4(FP), BX    
SUBL    $128, SP        // plenty of scratch    
ANDL    $~15, SP    
MOVL    AX, 120(SP)        // save argc, argv away    
MOVL    BX, 124(SP)    
這一段主要是處理操做系統傳來的參數,並把他們保存到棧上測試

 優化

初始化棧

MOVL    $runtime路g0(SB), BP  
LEAL    (-64*1024+104)(SP), BX    
MOVL    BX, g_stackguard(BP)    
MOVL    BX, g_stackguard0(BP)    
MOVL    SP, g_stackbase(BP)ui

 this

測試CPUID(38六、AMD64)

// find out information about the processor we're on  
MOVL    $0, AX    
CPUID    
CMPL    AX, $0    
JE    nocpuinfo    
MOVL    $1, AX    
CPUID    
MOVL    CX, runtime·cpuid_ecx(SB)    
MOVL    DX, runtime·cpuid_edx(SB)編碼

測試CPUID的目的在於充分利用硬件的能力,在GO源碼中的哈希部分用到了它,在哈希的時候用aes&sse指令提升速度。操作系統

 orm

初始化CGO

    // if there is an _cgo_init, call it to let it  
    // initialize and to set up GS.  if not,    
    // we set up GS ourselves.    
    MOVL    _cgo_init(SB), AX    
    TESTL    AX, AX    
    JZ    needtls    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; $setmg_gcc<>(SB), BX    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; BX, 4(SP)    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; BP, 0(SP)    
&#160;&#160;&#160; CALL&#160;&#160;&#160; AX    
&#160;&#160;&#160; // update stackguard after _cgo_init    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; $runtime&middot;g0(SB), CX    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; g_stackguard0(CX), AX    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; AX, g_stackguard(CX)    
&#160;&#160;&#160; // skip runtime&middot;ldt0setup(SB) and tls test after _cgo_init for non-windows    
&#160;&#160;&#160; CMPL runtime&middot;iswindows(SB), $0    
&#160;&#160;&#160; JEQ ok    
needtls:    
&#160;&#160;&#160; // skip runtime&middot;ldt0setup(SB) and tls test on Plan 9 in all cases    
&#160;&#160;&#160; CMPL&#160;&#160;&#160; runtime&middot;isplan9(SB), $1    
&#160;&#160;&#160; JEQ&#160;&#160;&#160; ok

&#160;&#160;&#160; // set up %gs  
&#160;&#160;&#160; CALL&#160;&#160;&#160; runtime&middot;ldt0setup(SB)

&#160;&#160;&#160; // store through it, to make sure it works  
&#160;&#160;&#160; get_tls(BX)    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; $0x123, g(BX)    
&#160;&#160;&#160; MOVL&#160;&#160;&#160; runtime&middot;tls0(SB), AX    
&#160;&#160;&#160; CMPL&#160;&#160;&#160; AX, $0x123    
&#160;&#160;&#160; JEQ&#160;&#160;&#160; ok

&#160;

運行時檢測

// set up m and g "registers"  
get_tls(BX)    
LEAL&#160;&#160;&#160; runtime&middot;g0(SB), CX    
MOVL&#160;&#160;&#160; CX, g(BX)    
LEAL&#160;&#160;&#160; runtime&middot;m0(SB), AX    
MOVL&#160;&#160;&#160; AX, m(BX)

// save m->g0 = g0  
MOVL&#160;&#160;&#160; CX, m_g0(AX)

CALL&#160;&#160;&#160; runtime&middot;emptyfunc(SB)&#160;&#160;&#160; // fault if stack check is wrong

// convention is D is always cleared  
CLD

CALL&#160;&#160;&#160; runtime&middot;check(SB)  
這段檢查一下運行時函數調用,emptyfunc只有一條ret指令,會當即返回。

&#160;

重頭戲:調用初始化C函數並最終進入C環境

// saved argc, argv  
MOVL&#160;&#160;&#160; 120(SP), AX    
MOVL&#160;&#160;&#160; AX, 0(SP)    
MOVL&#160;&#160;&#160; 124(SP), AX    
MOVL&#160;&#160;&#160; AX, 4(SP)    
CALL&#160;&#160;&#160; runtime&middot;args(SB)    
CALL&#160;&#160;&#160; runtime&middot;osinit(SB)    
CALL&#160;&#160;&#160; runtime&middot;hashinit(SB)    
CALL&#160;&#160;&#160; runtime&middot;schedinit(SB)

// create a new goroutine to start program  
PUSHL&#160;&#160;&#160; $runtime&middot;main&middot;f(SB)&#160;&#160;&#160; // entry    
PUSHL&#160;&#160;&#160; $0&#160;&#160;&#160; // arg size    
ARGSIZE(8)    
CALL&#160;&#160;&#160; runtime&middot;newproc(SB)    
ARGSIZE(-1)    
POPL&#160;&#160;&#160; AX    
POPL&#160;&#160;&#160; AX

// start this M  
CALL&#160;&#160;&#160; runtime&middot;mstart(SB)

INT $3  
RET    

DATA&#160;&#160;&#160; runtime&middot;main&middot;f+0(SB)/4,$runtime&middot;main(SB)  
GLOBL&#160;&#160;&#160; runtime&middot;main&middot;f(SB),RODATA,$4

1,args在windows下不執行,目前只有在linux下執行

2,osinit會加載一些dll,並設置控制檯和獲取系統信息

3,hashinit在檢測到CPU支持ase和sse指令集時,會優化執行速度。

&#160;

proc.c

_rt0_go最後調用的幾個C函數都在proc.c裏面,到這裏就容易理解了。追蹤newproc/newproc1能夠發現它先推入goexit後推入main,這說明goexit是在main退出後執行的。在棧裏的表現就好像是goexit調用了main,其實這樣作只是爲了讓main退出後能彈出goexit的地址。因此上文所指的runtime.caller的錯誤信息也就不難理解了。

相關文章
相關標籤/搜索