前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go源码分析(一) 通过调试看go程序初始化过程

go源码分析(一) 通过调试看go程序初始化过程

作者头像
杜争斌
发布2022-04-27 19:19:56
2100
发布2022-04-27 19:19:56
举报
文章被收录于专栏:我的博文

参考资料:Go 1.5 源码剖析 (书签版).pdf

编写go语言test.go

代码语言:javascript
复制
package main
import (
	"fmt"
)
func main(){
	fmt.Println("Hello World")
}

 带调试的编译代码

代码语言:javascript
复制
go build -gcflags "-N -l" -o test test.go

 使用gdb进行调试 输入info files 查看入口点,

 对于同一个程序来说 每一次运行的入口点是一样的,表明这是一个将对位置,

通过对代码的修改也不能改变,这个入口点可能是编译后的程序入口,与其他代码无关

更换编译器,Entry point 发生了变化,和编译器有关。

代码语言:javascript
复制
 gdb test
(gdb) info files
Symbols from "/root/test/test".
Local exec file:
	`/root/test/test', file type elf64-x86-64.
	Entry point: 0x44f4d0
	0x0000000000401000 - 0x0000000000482178 is .text
	0x0000000000483000 - 0x00000000004c4a5a is .rodata
	0x00000000004c4b80 - 0x00000000004c56c8 is .typelink
	0x00000000004c56c8 - 0x00000000004c5708 is .itablink
	0x00000000004c5708 - 0x00000000004c5708 is .gosymtab
	0x00000000004c5720 - 0x000000000051343f is .gopclntab
	0x0000000000514000 - 0x0000000000520bdc is .noptrdata
	0x0000000000520be0 - 0x00000000005276f0 is .data
	0x0000000000527700 - 0x0000000000543d88 is .bss
	0x0000000000543da0 - 0x0000000000546438 is .noptrbss
	0x0000000000400f9c - 0x0000000000401000 is .note.go.buildid

 设置断点 b *0x44f4d0 每个程序的入口点可能不一样

代码语言:javascript
复制
(gdb) b *0x44f4d0
Breakpoint 1 at 0x44f4d0: file /usr/local/go/src/runtime/rt0_linux_amd64.s, line 8.

 可以查看文件 /usr/local/go/src/runtime/rt0_linux_amd64.s

代码语言:javascript
复制
// 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)

 设置断点runtime.rt0_go

代码语言:javascript
复制
(gdb) b runtime.rt0_go
Breakpoint 2 at 0x44be10: file /usr/local/go/src/runtime/asm_amd64.s, line 89.

 查看/usr/local/go/src/runtime/asm_amd64.s 也就是函数真正的入口,汇编语言写的

代码语言:javascript
复制
TEXT runtime·rt0_go(SB),NOSPLIT,$0
        // copy arguments forward on an even stack
        MOVQ    DI, AX          // argc
        MOVQ    SI, BX          // argv
        SUBQ    $(4*8+7), SP            // 2args 2auto
        ANDQ    $~15, SP
        MOVQ    AX, 16(SP)
        MOVQ    BX, 24(SP)

        // create istack out of the given (operating system) stack.
        // _cgo_init may update stackguard.
        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)

        // find out information about the processor we're on
        MOVL    $0, AX
        CPUID
        MOVL    AX, SI
        CMPL    AX, $0
        JE      nocpuinfo

        // Figure out how to serialize RDTSC.
        // On Intel processors LFENCE is enough. AMD requires MFENCE.
        // Don't know about the rest, so let's do MFENCE.
        CMPL    BX, $0x756E6547  // "Genu"
        JNE     notintel
        CMPL    DX, $0x49656E69  // "ineI"
        JNE     notintel
        CMPL    CX, $0x6C65746E  // "ntel"
        JNE     notintel
        MOVB    $1, runtime·isIntel(SB)
        MOVB    $1, runtime·lfenceBeforeRdtsc(SB)
notintel:

        // Load EAX=1 cpuid flags
        MOVL    $1, AX
        CPUID
        MOVL    AX, runtime·processorVersionInfo(SB)

        TESTL   $(1<<26), DX // SSE2
        SETNE   runtime·support_sse2(SB)

        TESTL   $(1<<9), CX // SSSE3
        SETNE   runtime·support_ssse3(SB)

        TESTL   $(1<<19), CX // SSE4.1
        SETNE   runtime·support_sse41(SB)

        TESTL   $(1<<20), CX // SSE4.2
        SETNE   runtime·support_sse42(SB)

        TESTL   $(1<<23), CX // POPCNT
        SETNE   runtime·support_popcnt(SB)

        TESTL   $(1<<25), CX // AES
        SETNE   runtime·support_aes(SB)

        TESTL   $(1<<27), CX // OSXSAVE
        SETNE   runtime·support_osxsave(SB)

        // If OS support for XMM and YMM is not present
        // support_avx will be set back to false later.
        TESTL   $(1<<28), CX // AVX
        SETNE   runtime·support_avx(SB)

eax7:
        // Load EAX=7/ECX=0 cpuid flags
        CMPL    SI, $7
        JLT     osavx
        MOVL    $7, AX
        MOVL    $0, CX
        CPUID

        TESTL   $(1<<3), BX // BMI1
        SETNE   runtime·support_bmi1(SB)

        // If OS support for XMM and YMM is not present
        // support_avx2 will be set back to false later.
        TESTL   $(1<<5), BX
        SETNE   runtime·support_avx2(SB)

        TESTL   $(1<<8), BX // BMI2
        SETNE   runtime·support_bmi2(SB)

        TESTL   $(1<<9), BX // ERMS
        SETNE   runtime·support_erms(SB)

osavx:
        CMPB    runtime·support_osxsave(SB), $1
        JNE     noavx
        MOVL    $0, CX
        // For XGETBV, OSXSAVE bit is required and sufficient
        XGETBV
        ANDL    $6, AX
        CMPL    AX, $6 // Check for OS support of XMM and YMM registers.
        JE nocpuinfo
noavx:
        MOVB $0, runtime·support_avx(SB)
        MOVB $0, runtime·support_avx2(SB)

nocpuinfo:
        // if there is an _cgo_init, call it.
        MOVQ    _cgo_init(SB), AX
        TESTQ   AX, AX
        JZ      needtls
        // g0 already in DI
        MOVQ    DI, CX  // Win64 uses CX for first parameter
        MOVQ    $setg_gcc<>(SB), SI
        CALL    AX

        // update stackguard after _cgo_init
        MOVQ    $runtime·g0(SB), CX
        MOVQ    (g_stack+stack_lo)(CX), AX
        ADDQ    $const__StackGuard, AX
        MOVQ    AX, g_stackguard0(CX)
        MOVQ    AX, g_stackguard1(CX)

#ifndef GOOS_windows
        JMP ok
#endif
needtls:
#ifdef GOOS_plan9
        // skip TLS setup on Plan 9
        JMP ok
#endif
#ifdef GOOS_solaris
        // skip TLS setup on Solaris
        JMP ok
#endif

        LEAQ    runtime·m0+m_tls(SB), DI
        CALL    runtime·settls(SB)

        // store through it, to make sure it works
        get_tls(BX)
        MOVQ    $0x123, g(BX)
        MOVQ    runtime·m0+m_tls(SB), AX
        CMPQ    AX, $0x123
        JEQ 2(PC)
        MOVL    AX, 0   // abort
ok:
        // set the per-goroutine and per-mach "registers"
        get_tls(BX)
        LEAQ    runtime·g0(SB), CX
        MOVQ    CX, g(BX)
        LEAQ    runtime·m0(SB), AX

        // save m->g0 = g0
        MOVQ    CX, m_g0(AX)
        // save m0 to g0->m
        MOVQ    AX, g_m(CX)

        CLD                             // convention is D is always left cleared
        CALL    runtime·check(SB)

        MOVL    16(SP), AX              // copy argc
        MOVL    AX, 0(SP)
        MOVQ    24(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
        PUSHQ   $0                      // arg size
        CALL    runtime·newproc(SB)
        POPQ    AX
        POPQ    AX

        // start this M
        CALL    runtime·mstart(SB)

        MOVL    $0xf1, 0xf1  // crash
        RET

DATA    runtime·mainPC+0(SB)/8,$runtime·main(SB)
GLOBL   runtime·mainPC(SB),RODATA,$8

 设置断点runtime.main,接下来的代码是go语言写的了

代码语言:javascript
复制
(gdb) b runtime.main
Breakpoint 3 at 0x427700: file /usr/local/go/src/runtime/proc.go, line 109

 查看/usr/local/go/src/runtime/proc.go文件

代码语言:javascript
复制
func main() {
        g := getg()

        // Racectx of m0->g0 is used only as the parent of the main goroutine.
        // It must not be used for anything else.
        g.m.g0.racectx = 0

        // Max stack size is 1 GB on 64-bit, 250 MB on 32-bit.
        // Using decimal instead of binary GB and MB because
        // they look nicer in the stack overflow failure message.
        if sys.PtrSize == 8 {//判断机器是32位还是64位,可以通过指针的长度进行判断,64位为8
                maxstacksize = 1000000000
        } else {
                maxstacksize = 250000000
        }

        // Allow newproc to start new Ms.
        mainStarted = true

        systemstack(func() {
                newm(sysmon, nil)
        })

        // Lock the main goroutine onto this, the main OS thread,
        // during initialization. Most programs won't care, but a few
        // do require certain calls to be made by the main thread.
        // Those can arrange for main.main to run in the main thread
        // by calling runtime.LockOSThread during initialization
        // to preserve the lock.
        lockOSThread()

        if g.m != &m0 {
                throw("runtime.main not on m0")
        }

        runtime_init() // must be before defer
        if nanotime() == 0 {
                throw("nanotime returning zero")
        }

        // Defer unlock so that runtime.Goexit during init does the unlock too.
        needUnlock := true
        defer func() {
                if needUnlock {
                        unlockOSThread()
                }
        }()

        // Record when the world started. Must be after runtime_init
        // because nanotime on some platforms depends on startNano.
        runtimeInitTime = nanotime()

        gcenable()

        main_init_done = make(chan bool)
        if iscgo {
                if _cgo_thread_start == nil {
                        throw("_cgo_thread_start missing")
                }
                if GOOS != "windows" {
                        if _cgo_setenv == nil {
                                throw("_cgo_setenv missing")
                        }
                        if _cgo_unsetenv == nil {
                                throw("_cgo_unsetenv missing")
                        }
                }
                if _cgo_notify_runtime_init_done == nil {
                        throw("_cgo_notify_runtime_init_done missing")
                }
                // Start the template thread in case we enter Go from
                // a C-created thread and need to create a new thread.
                startTemplateThread()
                cgocall(_cgo_notify_runtime_init_done, nil)
        }

        fn := main_init // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
        fn()
        close(main_init_done)

        needUnlock = false
        unlockOSThread()

        if isarchive || islibrary {
                // A program compiled with -buildmode=c-archive or c-shared
                // has a main, but it is not executed.
                return
        }
        fn = main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
        fn()
        if raceenabled {
                racefini()
        }

        // Make racy client program work: if panicking on
        // another goroutine at the same time as main returns,
        // let the other goroutine finish printing the panic trace.
        // Once it does, it will exit. See issues 3934 and 20018.
        if atomic.Load(&runningPanicDefers) != 0 {
                // Running deferred functions should not take long.
                for c := 0; c < 1000; c++ {
                        if atomic.Load(&runningPanicDefers) == 0 {
                                break
                        }
                        Gosched()
                }
        }
        if atomic.Load(&panicking) != 0 {
                gopark(nil, nil, "panicwait", traceEvGoStop, 1)
        }

        exit(0)
        for {
                var x *int32
                *x = 0
        }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档