Home gdb常用指令二
Post
Cancel

gdb常用指令二

常见指令

这里记录常用的指令和基本使用方式。

单步调试相关指令

next(n)

nextn)指令用于逐行执行程序,并跳过函数调用。按行执行代码,但不会进入当前行中的函数调用,(GDB会一次性执行整个函数)。 可以跳过函数内部的细节,只关注函数的结果。

next指令可以带一个参数,表示执行N行。不写默认是1,即单次执行一行

1
2
3
(gdb)next 
# 或
(gdb)next <count>

step(s)

也是单步执行,和next类似,但step会陷入函数中,可以确认函数的执行细节。如果函数嵌套过多,频繁使用step会影响效率, 可以和next指令配合使用。

用法和next指令近似:

1
2
3
(gdb)step 
# 或
(gdb)step <count>

until(n)

until(u)指令用于不断运行程序直到达到指定的行号或函数返回。它可以用于快速运行过一段代码,比如程序中有循环结构,循环多次时,可以until快速指定运行到循环体后面。

用法:

1
2
3
(gdb)until 
# 或
(gdb)until <line-number>

默认不加行号参数时,就是运行到下一行,效果同next指令,使用行号参数,可以一直运行到指定的行号停止,可以快速跳出循环。

finish

finish指令通常用于执行完当前函数并返回到调用它的地方。(一直运行到当前栈帧返回,一般就是运行完一个函数,具体也要看程序编译情况)。 当进入函数调用后,想要继续执行完整个函数而不逐行调试时,finish指令非常有用。

finish在运行到返回时,还会显示返回值,(返回值一般根据对应架构的c和汇编的接口标准放在特定的寄存器,如x86的rax寄存器,arm架构的r0寄存器)。

另外,多次使用finish指令可以快速跳出多重函数调用,提高效率。

return

return指令和finish指令有些相似,它用于在函数内部强制执行函数返回(直接跳转到函数的末尾,即返回之前)。这样可以跳过当前函数内部代码的执行, 并将控制权返回到调用该函数的地方。另外,它还有个方便的功能:可以手动填写函数的返回值,可以模拟函数的返回操作,非常方便灵活

用法

1
(gdb)return [retval]

jump(j)

jump指令用于跳转到程序中的指定位置,并从该位置开始执行代码。允许用户直接修改程序的执行流程,跳过或重复运行特定的代码段。 需要注意,jump指令可能会破坏程序的执行状态和一致性。因为你在跳转后改变了程序的执行流程,所以可能会导致不符合预期的结果或出现错误, 需要对程序的结构非常清晰再使用,否则很容易导致程序的不稳定和不可预测的行为。

1
2
(gdb) jump <location>
# location 一般是行号,也可以使用 *ADDR,ADDR可以为地址表达式

continue(c)

continue(c)可以让停止的程序继续运行,相当于结束单步调试,直到下一个断点停下。

查看和修改状态

查看值或表达式print

print(p)命令用于打印变量或表达式的值。它是一个非常有用的调试指令,配合断点,可以查看程序在特定位置时的变量的当前值,或者计算并显示复杂表达式。

基本语法:

1
(gdb)print <expression>

<expression>可以是变量、数组、结构体、指针、表达式等。

  1. 打印变量的值:
    • 要打印一个变量的值,只需在print命令后面跟上该变量的名称即可。例如:print x,将打印变量x的当前值。
    • 如果变量是数组或结构体类型,你可以使用点操作符(.)或箭头操作符(->)来访问其成员。例如:print my_struct.member,将打印结构体变量my_structmember成员的值。
    • 如果变量名有冲突,(全局变量名,静态全局变量,局部变量名冲突),可以使用 ::符号限定范围,'filename'::varfunciont::var,如'f1.c'::nummain::num。另外gdb能在C++程序中区分::符号,不会冲突。
  2. 打印表达式的值:
    • 除了打印变量,print命令还可以计算并显示复杂表达式的结果。可以在print命令中使用各种运算符、函数调用和其他表达式。
    • 例如,print a + b将计算变量ab的和,并显示结果;print func(x)将计算函数func在参数x上的返回值。
  3. 打印数组的值:
    • 如果要打印数组的值,可以使用以下语法:print <array>@<length>,其中<array>是数组的名称,<length>是要打印的元素数量。
    • 例如,print my_array@5将打印数组my_array的前5个元素的值。
    • 动态申请的内存也可以打印(p=malloc(...)),支持类型转换解释,p *(char*)p@3p *(char*)(p+2)@3print *((unsigned int*)p)@3,都是可以的。
  4. 打印指针指向的值:
    • 如果要打印指针变量指向的值,可以在print命令前面加上*操作符。例如,print *ptr将打印指针变量ptr所指向的值。
  5. 格式化输出:
    • 可以使用print命令的选项来控制输出的格式。例如,print /x value将以十六进制格式打印变量value的值。 其他格式包括 /d , /u为有符号,无符号的十进制输出 ,/o八进制输出,/t二进制输出,/f浮点数输出,/c字符输出

print还有其他多种选项,具体查看帮助,(gdb)help p

自动打印(display)

如果在单步调试过程中,希望不断打印变量或表达式,避免每次使用 print命令,可以使用display命令自动打印。(每次单步执行后,自动打印)

display命令用法和print基本一致。使用display后,每次单步,都会自动打印相关表达式的值。

一些相关的管理命令

1
2
3
(gdb)info display                          //查看display的表达式列表,里面有每个表达式的序号[display No]
(gdb)enable/disable display [display No]   //开启/禁用某个表达式的自动打印
(gdb)undisplay [display No]                //删除对应的自动打印的表达式

设置/修改变量的值

在单步调试过程中,可以使用 set命令修改变量或表达式的值。

1
(gdb)set VAR = EXP

需要注意,VAR必须是在当前可见的作用域内才可以修改。EXP是表达式,可以是常量,也可以直接写一个函数,使用其返回值来设置。

查看内存数据

可以使用x命令查看内存数据情况,命令全称应该是examine。具体查看帮助,不如print命令方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) x /[count][format][size]  ADDR

(gdb) help x
Examine memory: x/FMT ADDRESS.
ADDRESS is an expression for the memory address to examine.
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
  t(binary), f(float), a(address), i(instruction), c(char), s(string)
  and z(hex, zero padded on the left).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).
The specified number of objects of the specified size are printed
according to the format.  If a negative number is specified, memory is
examined backward from the address.

使用示例:指针p指向的内存的内容"abcd1234"

1
2
3
4
5
6
7
# 打印指针 p 所指向内存的开头往后3个字节,以字符打印,每次越过一个字节
(gdb) x /3cb  p
0x5555555596b0:	97 'a'	98 'b'	99 'c'
(gdb) x /3ch  p
0x5555555596b0:	97 'a'	99 'c'	49 '1'
(gdb) x /3xb  p
0x5555555596b0:	0x61	0x62	0x63

查看堆栈情况

使用 bt(backtrace)命令可以查看当前函数的调用堆栈层次情况。它会打印出调用链中的函数调用顺序,以及每个函数的调用位置和参数信息。

bt命令输出的每一行都表示调用堆栈中的一个帧。每个帧显示了函数的调用位置和函数参数的信息(如果存在)。通常,每个帧的信息包括以下内容:

  • 帧编号(Frame number):帧在调用堆栈中的顺序编号。
  • 函数调用位置(Function call location):指示函数调用发生的位置,包括源文件名和行号。
  • 函数名(Function name):调用的函数名称。
  • 函数参数(Function arguments):显示函数调用时传递的参数值。

调用堆栈中的最后一个帧通常表示当前正在执行的函数。通过查看调用堆栈,可以了解函数之间的调用关系,以及在调试期间定位问题。

查看当前栈中的局部变量

使用 info locals命令可以查看当前栈中的局部变量内容,可以配合bt命令使用。

汇编相关指令

一般的单步调试都是基于源代码的行调试的,容易理解,也比较直观。不过对于汇编层面,一行代码通常并不是一条汇编指令,如果希望基于汇编指令层面的调试, 需要其他指令。如stepinexti指令,就是汇编级的单步,真实的对应一条汇编指令码。

在运行中,可以使用disassemble指令,查看当前运行位置附近的汇编代码。不过,可以使用layout split指令,同时显示源代码和汇编码及对应关系,更方便。 测试示例:

layout-split示例

其他可以使用 info registers指令查看寄存器状态。使用x指令查看内存情况。


分篇,分类型

多线程调试


以下是GDB中常用的一些指令:

  1. info threads:显示线程的信息。

参考

man gdb

This post is licensed under CC BY 4.0 by the author.

gdb常用指令一

gdb调试多线程程序