Last Updated: 2023-10-06 09:02:40 Friday
-- TOC --
一直以来,我都习惯了使用printf来调试程序,有时为了躺在“舒适区”,拒绝学习新东西。我们应该勇敢的走出自己的舒适区,去探索更多更强的工具!
熟练使用gdb,可以干下面几件事情:
用gdb调试C或C++代码,在编译代码时,需要增加
-g
或-g3 -gdwarf-2
参数。(详解gcc常用编译参数)gdb有个图形前端
DDD
,很强!
$ gdb [-q] [program]
-q(quiet)的作用在于,不用打印gdb的那些about信息,很有多命令行工具都有此参数。指定program
的命令行参数,可以使用gdb内的set args
命令,或者--args
,如下:
$ gdb [-q] --args <program> <args>
启动对program的调试,在命令直接设置program的启动参数。
退出gdb的q指令。
在gdb中,用help指令查看各种指令的联机帮助。
如果在启动gdb的时候,没有指定program,在gdb内,用file指令指定。
用于在run程序之前,指定此程序的命令行参数,也可以用来去掉命令行的参数,如下:
(gdb) set args [arguments]
漂亮的打印信息,总是需要的。
进入gdb后,一般我们都要先设置断点,然后再启动程序。
b指令用于设置断点,后面除了跟行号,还可以:
b <file_name>:<line_number>
,此时file_name无需路径。b function_name
,比如:b main
。b <file_name>:<function_name>
。b location if condition
。b *0x400540
,有个*
,调试汇编代码时很好用。gdb还有很多其它花样的break:
临时断点,作用一次后自动消失,设置方式与break相同。
对符合regex正则表达式条件的函数名设置断点,效果与break一样,不会自动消失。
在gdb中查看源代码和对应的行号。
l指令输入后,默认从当前位置打印10行代码出来显示,按回车(自动执行上一条gdb命令)会自动继续显示10行代码。改变list指令默认behavior可以这样:
(gdb) l <line_number> # 从指定行号开始打印10号代码
(gdb) l <start_line_number>,<end_line_number>
(gdb) l <function_name>
查看l指令显示代码行数。
设置l指令显示代码的行数,默认为10。
r指令(重新)启动程序,直到程序结束,或Crash,或者遇到breakpoint。
程序在断点停下来后,就要开始一点点推进程序代码的执行,在此过程中仔细检查各变量和相关内存区域的值。用r指令也可以在任何时候重新运行程序!r指令也可以带上给调试进程的命令行参数,但在重复run时,要去掉args,只能使用set args
。
启动调试程序,停在main处,使用start指令,gdb自动在main处设置了一个临时断点。同run指令一样,start也可以重复执行,每次都会停在main开始的位置。
stop debugging.
执行下一条指令,如果下一条指令是函数调用,不进入函数。
直接回车,重复上一个命令!如果上一个命令是n,就很有用。
执行下一条指令,如果下一条指令是函数调用,进入函数。
继续,continue,直到下一个breakpoint,或者程序结束,或者程序Crash。所以,r指令一般只用一次,然后就不再用了,后面要让程序继续跑起来,用c指令。
将当前的function执行完,返回function调用的地方。
显示backtrace,查看当前程序调用栈信息,以及程序当前停在哪一行的。
advance指令实现让代码自动运行到指定函数的入口,并进入函数。
可以简单地用advance bar
,来代替tbreak bar; continue
。如果没有到达指定位置,执行会在当前函数末尾停下来(Execution will also stop upon exit from the current stack frame)。
Ignore a function while stepping.
用于显示各种信息。
查看所有breakpoint信息,此处会显示出断点的编号。
查看程序使用的动态链接库信息。
查看所有local variables。
查看当前的stack frame。
help info可以看到更多关于info的选项。
删除N号断点。
清除所有断点。
关闭/开启N号断点。
print指令,后面跟一个表达式,显示表达式的值。
同时显示多个变量的值:print {var1, var2, var3}
,like this...
可以通过/fmt
格式来增加格式信息,比如:p /x *b
,以hex的方式打印b地址的内容。格式与x指令通用。p指令还可以用来查macro的“值”,但是需要gcc编译的时候,带上-g3 -gdwarf-2
参数。
print $rax # print contents of rax in decimal
print /x $rax # print contents of rax in hex
print /x $rip
print /t $rax # print contents of rax in binary
print 0x100 # print decimal representation of 0x100
print /x 555 # print hex representation of 555
print /x ($rsp+8) # print contents of rsp+8 in hex
print *(long*)0x7fffffffe818 # print long integer at 0x7fffffffe818
print *(long*)($rsp+8)
examine memory,这里的memory,是指程序的虚拟地址空间。
help x # check format info
x/2g 0x7fffffffe818 # examine two 8-byte-words starting at 0x7fffffffe818
x/20b funcname # examine first 20 bytes of function
x/s <address> # 从address地址处开始显示字符串
x/s $rdi # 以rdi寄存器的值作为地址,显示字符串
显示当前rip指向地址的4条汇编指令:
(gdb) x /4i $pc
设置在程序停止运行时,自动显示的表达式。在单步调试的时候,这个指令很有用,每次next,自动显示你关心的变量值。可以设置多个display,用多行命令,或者display {exp1, exp2, exp3}
。
取消display。
调试代码中的macro。复杂代码中到底哪个macro在起作用?!
(gdb) help macro
macro exp
最有用...
将当前RIP位置周围的代码进行反汇编显示,或反汇编指定的函数,或某个地址范围进行反汇编显示。
disass # show一下周围的汇编指令
disass $pc,+16 # 显示从rip地址开始,向后16bytes的汇编
disass funcname # disassemble function
disass 0x400544
disass 0x400544, 0x40054d
(gdb) show disassembly-flavor
The disassembly flavor is "att".
(gdb) set disassembly-flavor intel
(gdb) disass
是时候学点汇编了!
显示所有整型寄存器的值。也可以指定显示某个寄存器:
(gdb) i r rax
rax 0x2c63652 46544466
显示所有寄存器。
将源代码中的某行,与地址范围对应起来。例如:
(gdb) info line 5
Line 5 of "test.c" starts at address 0x401146 <main> and ends at 0x40114e <main+8>.
显示所有线程,每个线程有个编号。
切换到编号为N的线程。
让一个或多个线程执行gdb的command命令。
需要用原始程序文件和其产生的coredump文件,一起让gdb启动。
$ gdb program coredump
然后bt
,disass
.....:)
coredump文件保存了进程Crash时的现场,调用栈,寄存器,变量,虚拟内存.....
调试没有使用-g
参数编译的程序,或者就是需要按汇编指令调试时。
显示一下条指令的汇编:
(gdb) set disassemble-next-line on
(gdb) display/i $pc
两个单独指令:
(gdb) ni # 对应n指令,nexti
(gdb) si # 对应s指令,stepi
在某一行汇编设置断点,需要按照*<function_name>+offset
的格式来实现:
$ gdb -q test
Reading symbols from test...
(gdb) disass main
Dump of assembler code for function main:
0x0000000000401146 <+0>: push %rbp
0x0000000000401147 <+1>: mov %rsp,%rbp
0x000000000040114a <+4>: sub $0x10,%rsp
0x000000000040114e <+8>: movl $0x2a,-0x4(%rbp)
0x0000000000401155 <+15>: mov -0x4(%rbp),%eax
0x0000000000401158 <+18>: mov %eax,%esi
0x000000000040115a <+20>: mov $0x402010,%edi
0x000000000040115f <+25>: mov $0x0,%eax
0x0000000000401164 <+30>: call 0x401040 <printf@plt>
0x0000000000401169 <+35>: mov $0x4f,%edi
0x000000000040116e <+40>: call 0x401050 <malloc@plt>
0x0000000000401173 <+45>: mov %rax,-0x10(%rbp)
0x0000000000401177 <+49>: mov -0x10(%rbp),%rax
0x000000000040117b <+53>: mov %rax,%rdi
0x000000000040117e <+56>: call 0x401030 <free@plt>
0x0000000000401183 <+61>: mov $0x0,%eax
0x0000000000401188 <+66>: leave
0x0000000000401189 <+67>: ret
End of assembler dump.
(gdb) b *main+25
Breakpoint 1 at 0x40115f: file test.c, line 7.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040115f in main at test.c:7
(gdb) layout src
上下键
有效。
如果屏幕有点花了,Ctrl-L
重置屏幕。
退出TUI,Ctrl-X-A
。
本文链接:https://cs.pynote.net/sf/c/cdm/202111235/
-- EOF --
-- MORE --