详解gdb常用指令

Last Updated: 2023-10-06 09:02:40 Friday

-- TOC --

一直以来,我都习惯了使用printf来调试程序,有时为了躺在“舒适区”,拒绝学习新东西。我们应该勇敢的走出自己的舒适区,去探索更多更强的工具!

熟练使用gdb,可以干下面几件事情:

用gdb调试C或C++代码,在编译代码时,需要增加-g-g3 -gdwarf-2参数。(详解gcc常用编译参数

gdb有个图形前端DDD,很强!

gdb命令行参数

$ gdb [-q] [program]

-q(quiet)的作用在于,不用打印gdb的那些about信息,很有多命令行工具都有此参数。指定program的命令行参数,可以使用gdb内的set args命令,或者--args,如下:

$ gdb [-q] --args <program> <args>

启动对program的调试,在命令直接设置program的启动参数。

gdb调试指令

退出gdb的q指令。

在gdb中,用help指令查看各种指令的联机帮助。

如果在启动gdb的时候,没有指定program,在gdb内,用file指令指定。

用于在run程序之前,指定此程序的命令行参数,也可以用来去掉命令行的参数,如下:

(gdb) set args [arguments]

漂亮的打印信息,总是需要的。

进入gdb后,一般我们都要先设置断点,然后再启动程序。

b指令用于设置断点,后面除了跟行号,还可以:

  1. 如果是多文件的场景设置断点:b <file_name>:<line_number>,此时file_name无需路径。
  2. 还可以直接用函数名设置断点:b function_name,比如:b main
  3. 或者: b <file_name>:<function_name>
  4. 还可以在设置断点的时候,增加一个condition:b location if condition
  5. 将断电设置在一个地址上,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文件

需要用原始程序文件和其产生的coredump文件,一起让gdb启动。

$ gdb program coredump

然后btdisass.....:)

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

使用TUI界面

(gdb) layout src

上下键有效。

如果屏幕有点花了,Ctrl-L重置屏幕。

退出TUI,Ctrl-X-A

本文链接:https://cs.pynote.net/sf/c/cdm/202111235/

-- EOF --

-- MORE --