参考资料:
编写go语言test.go
package mainimport ( "fmt")func main(){ fmt.Println("Hello World")}
带调试的编译代码
go build -gcflags "-N -l" -o test test.go
使用gdb进行调试 输入info files 查看入口点,
对于同一个程序来说 每一次运行的入口点是一样的,表明这是一个将对位置,
通过对代码的修改也不能改变,这个入口点可能是编译后的程序入口,与其他代码无关
更换编译器,Entry point 发生了变化,和编译器有关。
gdb test(gdb) info filesSymbols 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 每个程序的入口点可能不一样
(gdb) b *0x44f4d0Breakpoint 1 at 0x44f4d0: file /usr/local/go/src/runtime/rt0_linux_amd64.s, line 8.
可以查看文件 /usr/local/go/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)
设置断点runtime.rt0_go
(gdb) b runtime.rt0_goBreakpoint 2 at 0x44be10: file /usr/local/go/src/runtime/asm_amd64.s, line 89.
查看/usr/local/go/src/runtime/asm_amd64.s 也就是函数真正的入口,汇编语言写的
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 nocpuinfonoavx: 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#endifneedtls:#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 // abortok: // 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 RETDATA runtime·mainPC+0(SB)/8,$runtime·main(SB)GLOBL runtime·mainPC(SB),RODATA,$8
设置断点runtime.main,接下来的代码是go语言写的了
(gdb) b runtime.mainBreakpoint 3 at 0x427700: file /usr/local/go/src/runtime/proc.go, line 109
查看/usr/local/go/src/runtime/proc.go文件
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 }}