GO语解惑:从源码分析GO程序的入口(续:_rt0_go)

书接上回。现在我们知道_rt0_go这个汇编过程是GO语言的真正入口并做了一些初始化工作,本文来大略的过一下这个过程和它调用的几个runtime里的函数。

Windwos Live Writer写的文章,编码有问题,对不住各位:我不想改。

 

参数

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)

 

测试CPUID(386、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指令提高速度。

 

初始化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的错误信息也就不难理解了。

本文来自:开源中国博客

感谢作者:sz_Promi

查看原文:GO语解惑:从源码分析GO程序的入口(续:_rt0_go)

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。