常见指令
这里记录常用的指令和基本使用方式。
单步调试相关指令
next(n)
next
(n
)指令用于逐行执行程序,并跳过函数调用。按行执行代码,但不会进入当前行中的函数调用,(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>
可以是变量、数组、结构体、指针、表达式等。
- 打印变量的值:
- 要打印一个变量的值,只需在
print
命令后面跟上该变量的名称即可。例如:print x
,将打印变量x
的当前值。 - 如果变量是数组或结构体类型,你可以使用点操作符(
.
)或箭头操作符(->
)来访问其成员。例如:print my_struct.member
,将打印结构体变量my_struct
的member
成员的值。 - 如果变量名有冲突,(全局变量名,静态全局变量,局部变量名冲突),可以使用
::
符号限定范围,'filename'::var
,funciont::var
,如'f1.c'::num
,main::num
。另外gdb能在C++程序中区分::
符号,不会冲突。
- 要打印一个变量的值,只需在
- 打印表达式的值:
- 除了打印变量,
print
命令还可以计算并显示复杂表达式的结果。可以在print
命令中使用各种运算符、函数调用和其他表达式。 - 例如,
print a + b
将计算变量a
和b
的和,并显示结果;print func(x)
将计算函数func
在参数x
上的返回值。
- 除了打印变量,
- 打印数组的值:
- 如果要打印数组的值,可以使用以下语法:
print <array>@<length>
,其中<array>
是数组的名称,<length>
是要打印的元素数量。 - 例如,
print my_array@5
将打印数组my_array
的前5个元素的值。 - 动态申请的内存也可以打印(
p=malloc(...)
),支持类型转换解释,p *(char*)p@3
,p *(char*)(p+2)@3
,print *((unsigned int*)p)@3
,都是可以的。
- 如果要打印数组的值,可以使用以下语法:
- 打印指针指向的值:
- 如果要打印指针变量指向的值,可以在
print
命令前面加上*
操作符。例如,print *ptr
将打印指针变量ptr
所指向的值。
- 如果要打印指针变量指向的值,可以在
- 格式化输出:
- 可以使用
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
命令使用。
汇编相关指令
一般的单步调试都是基于源代码的行调试的,容易理解,也比较直观。不过对于汇编层面,一行代码通常并不是一条汇编指令,如果希望基于汇编指令层面的调试, 需要其他指令。如stepi
,nexti
指令,就是汇编级的单步,真实的对应一条汇编指令码。
在运行中,可以使用disassemble
指令,查看当前运行位置附近的汇编代码。不过,可以使用layout split
指令,同时显示源代码和汇编码及对应关系,更方便。 测试示例:
其他可以使用 info registers
指令查看寄存器状态。使用x
指令查看内存情况。
分篇,分类型
多线程调试
以下是GDB中常用的一些指令:
info threads
:显示线程的信息。
参考
man gdb