Go 源码学习 引导(一)
参照雨痕的GO1.5源码剖析
env
$ go version
go version go1.18.3 linux/amd64
$ lsb_release -d
Description: CentOS Linux release 7.8.2003 (Core)
$ gdb --version
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
demo引导
demo
package main
import (
"fmt"
)
func main() {
fmt.Println("hello world")
}
build
建议使⽤ -gcflags “-N -l” 参数关闭编译器代码优化和函数内联,避免断点和单步执⾏⽆法准确对应源码⾏,避免⼩函数和局部变量被优化掉。
go build -gcflags "-N -l" -o helloworld helloworld.go
gdb
$ gdb helloworld
(gdb) info files
Symbols from "/home/go/test/helloworld/helloworld".
Local exec file:
`/home/go/test/helloworld/helloworld', file type elf64-x86-64.
Entry point: 0x45bfc0
0x0000000000401000 - 0x000000000047e0d7 is .text
0x000000000047f000 - 0x00000000004b4149 is .rodata
0x00000000004b42e0 - 0x00000000004b47a0 is .typelink
0x00000000004b47a0 - 0x00000000004b47f8 is .itablink
0x00000000004b47f8 - 0x00000000004b47f8 is .gosymtab
0x00000000004b4800 - 0x0000000000507fd0 is .gopclntab
0x0000000000508000 - 0x0000000000508130 is .go.buildinfo
0x0000000000508140 - 0x0000000000518720 is .noptrdata
0x0000000000518720 - 0x000000000051ff30 is .data
0x000000000051ff40 - 0x000000000054eea0 is .bss
0x000000000054eea0 - 0x0000000000554040 is .noptrbss
0x0000000000400f9c - 0x0000000000401000 is .note.go.buildid
在entry point打断点
(gdb) b *0x45bfc0
Breakpoint 1 at 0x45bfc0: file /usr/local/go/src/runtime/rt0_linux_amd64.s, line 8.
运行
(gdb) r
Starting program: /home/go/test/helloworld/helloworld
Breakpoint 1, _rt0_amd64_linux () at /usr/local/go/src/runtime/rt0_linux_amd64.s:8
8 JMP _rt0_amd64(SB)
(gdb) l
3 // license that can be found in the LICENSE file.
4
5 #include "textflag.h"
6
7 TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
8 JMP _rt0_amd64(SB)
9
10 TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0
11 JMP _rt0_amd64_lib(SB)
在 src/runtime 目录找到对应平台的入口文件rt0_linux_amd64.s 内容如下
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "textflag.h"
TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
JMP _rt0_amd64(SB)
TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0
JMP _rt0_amd64_lib(SB)
JMP -> _rt0_amd64
_rt0_amd64
TEXT _rt0_amd64(SB),NOSPLIT,$-8
MOVQ 0(SP), DI // argc
LEAQ 8(SP), SI // argv
JMP runtime·rt0_go(SB)
rt0_go 相对就要繁琐很多了
MOVQ $runtime·g0(SB), DI
LEAQ (-64*1024+104)(SP), BX
MOVQ BX, g_stackguard0(DI)
MOVQ BX, g_stackguard1(DI)
MOVQ BX, (g_stack+stack_lo)(DI)
MOVQ SP, (g_stack+stack_hi)(DI)
大致意思是说设置一个root goroutine. 具体参考 https://www.altoros.com/blog/golang-internals-part-5-the-runtime-bootstrap-process/
这一部分之后应该就是设置对应cpu型号
// find out information about the processor we're on
MOVL $0, AX
CPUID
CMPL AX, $0
JE nocpuinfo
CMPL BX, $0x756E6547 // "Genu"
JNE notintel
CMPL DX, $0x49656E69 // "ineI"
JNE notintel
CMPL CX, $0x6C65746E // "ntel"
JNE notintel
MOVB $1, runtime·isIntel(SB)
最后重要的部分check 及 一些初始化了
CALL runtime·check(SB)
MOVL 24(SP), AX // copy argc
MOVL AX, 0(SP)
MOVQ 32(SP), AX // copy argv
MOVQ AX, 8(SP)
CALL runtime·args(SB)
CALL runtime·osinit(SB)
CALL runtime·schedinit(SB)
// create a new goroutine to start program
MOVQ $runtime·mainPC(SB), AX // entry
PUSHQ AX
CALL runtime·newproc(SB)
POPQ AX
// start this M
CALL runtime·mstart(SB)
CALL runtime·abort(SB) // mstart should never return
RET
总结: begin -> _rt0_amd64_linux -> _rt0_amd64 -> runtime·rt0_go