构建gdb调试go程序简介gdb安装更新brewgdb镜像安装gdb go build编译gdb执行gdb命令gdb调试问题整理参考
前言
您已经学会了使用gdb调试golang,并通过简单的go程序验证简单入口程序的源代码调用顺序。
gdb安装开发环境是Mac,可以使用brew进行gdb安装
更新brew brew update以查看gdb镜像brew search gdb是否存在。 通过安装gdb brew install gdb go build并编译有关go的编译命令,可以在go help build中查看命令参数的详细信息。
在这里,我们使用go build-GC flags=all='-n-l '-ldflags=-compress dwarf=false进行编译。 这里的编译参数是指关闭函数内联和编译器的代码优化。 这样可以更好地查看编译源代码的显示,而不会干扰编译。
下面说明用于编译的参数的含义。
编译参数含义-关闭-gcflags=all='-N -l '内联优化; 关闭编译器函数优化-ldflags=-compressdwarf=false生成压缩gdb并运行gdb main进入gdb环境后,即可调试和使用gdb环境
$ gdbprogramreadingsymbolsfromprogram . loadinggoruntimesupport.gdb命令在进入gdb调试之前,对常规gdb调试命令进行了如下科普
命令说明文件名加载调试的可执行文件run并恢复可执行文件。 rstart单步执行、程序执行、在第一个执行语句list中停止并查看源代码。 lset设置变量的值next步调试、nstep步调试和缩写sbacktrace查看函数调用的堆栈框架和层次关系。 缩写btframe切换函数的堆栈帧。 缩写finfo查看函数内部的局部变量的值。 缩写ifinish退出当前函数并返回函数调用点continue继续执行。 缩写cprint打印值和地址。 缩写pquit结束gdb。 缩写q以上命令的跟踪参数也可以是gdb
目的是在gdb调试下,基于简单的golang程序进行gdb调试,了解golang程序的初始化过程。
运行list以检查当前文件的源代码。 这里显示我们创建的原始go程序文件
(gdb ) list1packagemain23import'fmt'4)5funcmain ()6fmt.println ) (Helloworld ' )7)执行info files将函数内部的局部变量的值转换为
(gdb ) infofilessymbolsfrom '/users/guanjian/workspace/go/program/program '.local execfile 3360 `/users/管理文件类型mach-o-x86-64.entry point :0 x 10701 e 033600 x 000000000100000-0x 00000010 CBC 53 is.text0x 000000000010 CBC 60 80-0x 000000001100 b19is _ text._ _ rodata0x 0000001100 b 20-0x 00000000 01012 c0-0x 000000001101328 is _ text._ itab ext._ gosymtter is _ text._ gopclntab0x 000000015 e 000-0x 0000000015 e 020 is _ data._ go _ buildinfo0x 000000015 e e e a4is _ data._ noptrdata0x 0000000016 C4 c0-0x 00000001738 f0is.data0x 0000000000116 C4 c0-0x 000000000000008
0x00000000011a1180 - 0x00000000011a62f0 is __DATA.__noptrbss(gdb)这里看到最上面的入口点是Entry point: 0x10701e0:,我们在这个入口进行断点设置进行这个程序的调试,执行break *0x10701e0:(这里是根据数据地址进行端点,前面加了星号 *)
(gdb) break *0x10701e0Breakpoint 1 at 0x10701e0: file /usr/local/go/src/runtime/rt0_darwin_amd64.s, line 8.可以看到已经添加断点成功了,然后看到了当前编译的go程序入口在/usr/local/go/src/runtime/rt0_darwin_amd64.s文件,我们进入到该目录下查看所有rt0开头的文件全是.s结尾的汇编语言实现的,如下:
$ ls /usr/local/go/src/runtime/rt0_rt0_aix_ppc64.s rt0_ios_arm64.s rt0_netbsd_arm.srt0_android_386.s rt0_js_wasm.s rt0_netbsd_arm64.srt0_android_amd64.s rt0_linux_386.s rt0_openbsd_386.srt0_android_arm.s rt0_linux_amd64.s rt0_openbsd_amd64.srt0_android_arm64.s rt0_linux_arm.s rt0_openbsd_arm.srt0_darwin_amd64.s rt0_linux_arm64.s rt0_openbsd_arm64.srt0_darwin_arm64.s rt0_linux_mips64x.s rt0_openbsd_mips64.srt0_dragonfly_amd64.s rt0_linux_mipsx.s rt0_plan9_386.srt0_freebsd_386.s rt0_linux_ppc64.s rt0_plan9_amd64.srt0_freebsd_amd64.s rt0_linux_ppc64le.s rt0_plan9_arm.srt0_freebsd_arm.s rt0_linux_riscv64.s rt0_solaris_amd64.srt0_freebsd_arm64.s rt0_linux_s390x.s rt0_windows_386.srt0_illumos_amd64.s rt0_netbsd_386.s rt0_windows_amd64.srt0_ios_amd64.s rt0_netbsd_amd64.s rt0_windows_arm.s接下来我们查看rt0_darwin_amd64.s文件,如下:
% cat -n rt0_darwin_amd64.s 1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5#include "textflag.h" 6 7TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8 8JMP_rt0_amd64(SB)//这里上一步的断点位置 9 10// When linking with -shared, this symbol is called when the shared library 11// is loaded. 12TEXT _rt0_amd64_darwin_lib(SB),NOSPLIT,$0 13JMP_rt0_amd64_lib(SB)第8行的_rt0_amd64(SB) 这里是调用入口,即
Breakpoint 1 at 0x10701e0: file /usr/local/go/src/runtime/rt0_darwin_amd64.s, line 8.下面我们需要查找_rt0_amd64这个汇编方法的位置了,查找方法可以根据自己的方式来查看,我这里为了更方便查看,下载了golang的sdk,然后导入IDE中进行全局搜索,定位到文件runtime/asm_amd64.s,如下:
接着,我们再来查找runtime·rt0_go(SB)方法,如下
runtime·rt0_go(SB)方法包含了很多初始化方法,这里摘下雨痕文章中的示例如下:
到这里,以上所有的go程序入口调用都是在汇编文件下进行的,后面将进入go语言的世界,通过go文件来编写,$runtime·main(SB)是函数出口,由此进入go语言的编写逻辑。我们通过gdb断点来查看调用,如下:
(gdb) b runtime.mainBreakpoint 1 at 0x103b4c0: file /usr/local/go/src/runtime/proc.go, line 115.至此,我们已经搭建了gdb的调试环境和一些查看golang函数代码的实践。
问题整理 问题1: macOs下gdb签名限制 (gdb) runStarting program: /xxx/main Unable to find Mach task port for process-id 35564: (os/kern) failure (0x5).(please check gdb is codesigned - see taskgated(8))简单的方法可以通过root来解决签名受限问题,但是这样调试程序风险很高,可以参考mac下如何对gdb进行证书签名
问题2: (No debugging symbols found in ./main) 而且(gdb) list 找不到调试文件 (gdb) listNo symbol table is loaded. Use the "file" command.我这边的解决方法是在编译go时,增加参数go build -ldflags=-compressdwarf=false可以输出调试信息便可以了
问题3: 关于函数调用查找方法 IDE导入go的sdk进行全局搜索(比较方法,但是不一定准确,需要充分理解调用链路)进入gdb环境,通过 b [file].[method]方式进行断点标记,可以显示当前断点的源文件位置包含行号,也可以通过info breakpoints查看断点信息 问题4: gdb断点的行号显示在IDE中不准确可以打开系统中的命令行进行查看,一般是准确的,使用cat -n file | head -n [number] 命令即可问题5: gdb 执行run后卡住 & 打断点不生效的可能原因
安装完gdb后应该进行代码签名,这里是个gdb在系统中的权限问题,参考gdb 初次运行卡住 Starting program: [New Thread 0x1103 of process 843] 参考
《Go语言学习笔记》雨痕
go build 命令参数详解
使用GDB调试Go语言
mac下如何对gdb进行证书签名
Go使用gdb调试
Debugging Go Code with GDB
使用 GDB 调试 Go 代码
【Go源码阅读】使用GDB调试Go程序
gdb命令
使用gdb添加断点的几种方式
gdb 初次运行卡住 Starting program: [New Thread 0x1103 of process 843]