go源码分析(一) 通过调试看go程序初始化过程
编写go语言test.go
package main
import (
"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 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 每个程序的入口点可能不一样
(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
// 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_go
Breakpoint 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 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语言写的了
(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文件
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
}
}
go源码分析(一) 通过调试看go程序初始化过程的更多相关文章
- 鸿蒙内核源码分析(信号生产篇) | 信号安装和发送过程是怎样的? | 百篇博客分析OpenHarmony源码 | v48.03
百篇博客系列篇.本篇为: v48.xx 鸿蒙内核源码分析(信号生产篇) | 年过半百,依然活力十足 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管 ...
- Orchard源码分析(5.1):Host初始化(DefaultOrchardHost.Initialize方法)
概述 Orchard作为一个可扩展的CMS系统,是由一系列的模块(Modules)或主题(Themes)组成,这些模块或主题统称为扩展(Extensions).在初始化或运行时需要对扩展进行安装:De ...
- Netty源码分析 (六)----- 客户端连接接入accept过程
通读本文,你会了解到1.netty如何接受新的请求2.netty如何给新请求分配reactor线程3.netty如何给每个新连接增加ChannelHandler netty中的reactor线程 ne ...
- Flink源码分析 - 剖析一个简单的Flink程序
本篇文章首发于头条号Flink程序是如何执行的?通过源码来剖析一个简单的Flink程序,欢迎关注头条号和微信公众号"大数据技术和人工智能"(微信搜索bigdata_ai_tech) ...
- spring源码分析之玩转ioc:bean初始化和依赖注入(一)
最近赶项目,天天加班到十一二点,终于把文档和代码都整完了,接上继续整. 上一篇聊了beanProcess的注册以及对bean的自定义修改和添加,也标志着创建bean的准备工作都做好了,接下来就是开大招 ...
- WebRTC 源码分析(五):安卓 P2P 连接过程和 DataChannel 使用
从本篇起,我们将迈入新的领域:网络传输.首先我们看看 P2P 连接的建立过程,以及 DataChannel 的使用,最终我们会利用 DataChannel 实现一个 P2P 的文字聊天功能. P2P ...
- Android4.0 Launcher 源码分析2——Launcher内容加载绑定详细过程
Launcher在应用启动的时候,需要加载AppWidget,shortcut等内容项,通过调用LauncherModel.startLoader(),开始加载的工作.launcherModel中加载 ...
- 源码分析之struts1自定义方法的使用与执行过程
最近有人问我,你做项目中用户的一个请求是怎么与struts1交互的,我说请求的url中包含了action的名字和方法名,这样就可以找到相应方法,执行并返回给用户了. 他又问,那struts1中有什么方 ...
- Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)
概述 Host 是应用程序域级的单例,代表了Orchard应用程序.其处理应用程序生命周期中的初始化.BeginRequest事件.EndRequest事件等. 可以简单理解为HttpApplicat ...
随机推荐
- python自动化测试之函数(匿名函数lambda和三目运算等(高级用法))
''' 匿名函数: lambda ''' def Add(a,b): print(a+b) Add(2,3) per = lambda a,b:a+b print(per(2,3)) ''' 三目运算 ...
- printf 输出浮点数
在测试printf函数输出结果时,写了如下代码: /** * printf:格式化输出函数 * printf函数不会按照格式控制而对数据类型进行转换,不管三七二十一, * 抓到二进制数据就按照格式控制 ...
- find_in_set 函数的语法
find_in_set 函数的语法: FIND_IN_SET(str,strList) str 要查询的字符串 strList 字段名,参数以“,”分隔,如(1,2,6,8) 查询字段(strList ...
- scrapy爬虫框架教程(二)-- 爬取豆瓣电影
前言 经过上一篇教程我们已经大致了解了Scrapy的基本情况,并写了一个简单的小demo.这次我会以爬取豆瓣电影TOP250为例进一步为大家讲解一个完整爬虫的流程. 工具和环境 语言:python 2 ...
- Mybatis: 插件及分页
Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的). Mybatis支持对Executor.StatementHa ...
- 高阶函数---swift中的泛型介绍(一步步实现Map函数)
说明 本文内容均出自函数式 Swift一书, 此处整理仅仅是为了自己日后方便查看, 需要深入研究的话, 可以点进去购买, 支持原作者 本书由 王巍–新浪微博大神翻译 OneV's Den 喵神博客 接 ...
- 用jekyll和github把网站建起来!
先把这些天学习的用jekyll在github上搭建网站的步骤记录下来,留作参考. #安装jekyll 确定系统安装 Git, Ruby, RubyGems, Nodejs, Python2.7. 如何 ...
- 利用FinalData恢复shift+delete误删的文件
当前位置 : 首页 » 文章分类 : 生活 » 利用FinalData恢复shift+delete误删的文件 上一篇 有关可变形部件模型(Deformable Part Model)的一些说明 ...
- js进阶之重复的定时器
使用setInterval()创建的定时器确保了定时器代码规则的插入队列中,这个的问题是:定时器代码可能在代码再次被添加到队列之前还没有完成执行,结果导致定时器代码连续运行了好几次,而之间没有任何停顿 ...
- 7-44 jmu-python-区间数之和 (10 分)
输入一个区间,计算里面能被3整除或被5整除的数和. 输入格式: 每行输入一个数据,代表区间左界和右界.区间包含左界和右界.数据必须是整数. 输出格式: 满足条件数和. 输入样例: 2 10 输出样例: ...