GDB(GNU 调试器)是一个强大的开源调试工具,用于调试 C、C++等程序。以下是一个简要的中文 GDB 教程,介绍基本的 GDB 命令和用法。
编译时加入调试信息
为了使用 GDB 进行调试,你需要在编译时包含调试信息。使用 -g 选项编译你的程序:
gcc -g -o your_program your_program.c使用 GDB:
-
启动 GDB:
在命令行中运行以下命令启动 GDB,并指定要调试的可执行文件:
终端窗口 # 当某些程序需要我们进行传参输入# gdb --args 参数# gdb之后,使用:set 参数# gdb之后,使用:r 参数# 当一个进程已经运行了,我们对它进行调试,可以如下两种命令gdb attach pidgdb --pid pidgdb your_programstart # 程序停在第一行run # 遇到断点才停# 调到多少行lineset p pretty # 结构体和数组显示规则更好看,每行一个 -
设置断点:
在你想要暂停程序执行的地方设置断点。例如,在
main函数的第 10 行设置断点:终端窗口 break main:10 # b# 查看断点设置信息info breakpoints # 显示观察点信息cleardisable xxenable xxbreak xxtb # 设置临时断点。对于一个循环来说,虽然断点在这,但只会执行一次,就不执行循环了rb # 对于带这个名字的函数都会进行标记,包括虚函数,或者命名不全的函数save breakpoints 文件名.txt # 保存断点信息source 文件名.txt # 还原断点信息#观察点watch <where>delete xx # denable xx # enadisable xxinfo watch # 显示观察点信息 -
运行程序
启动被调试的程序:
终端窗口 (gdb) runnext # n, 单步跟踪,若未指定count值,默认执行单条语句,否则执行后续的count条语句。Next指令遇到函数调用时,不会进行该函数。step # s, 单步跟踪,若未指定count值,默认执行单条语句,否则执行后续的count条语句。Step指令遇到函数调用时,会进入该函数。continue # c, 恢复程序运行,直到程序结束或者遇到断点until # 运行程序直到退出当前循环finish # 运行程序,直到当前函数完成,函数返回时打印出函数堆栈地址、参数值以及返回值等信息kill # 终止当前运行的程序set var 变量名 = 变量值 -
执行程序:
一旦程序停在断点处,你可以使用以下命令控制程序的执行:
continue(缩写为c):继续执行程序直到下一个断点。step(缩写为s):逐行执行程序,进入函数内部。next(缩写为n):逐行执行程序,不进入函数内部。
-
查看变量值:
在程序暂停时,你可以查看变量的值。例如:
终端窗口 print <format> <expr> # p, 使用指定的格式查看程序中的数据。命令兼容C语言中访问变量的方式,如数组下标、解引用、数据类型转换等操作。ptype # 打印变量类型display xx # 追踪查看变量的值undisplay xxwhatis var # 查看变量具体信息,例如属于哪一个类,是基类还是派生类#list # l, 查看当前上下文的源码list <filename>:<function> # 查看指定函数的源码set listsize <count> # 设置list查看的代码行数# 搜索search # 往下搜索forward-search # 往下搜索reverse-search # 往上搜索show directory # 显示当前代码搜索路径directory xxpath # 设置搜索代码的文件路径。一般有当前路径+工作路径# 反汇编disassemble [<where>] # a -
查看堆栈:
查看当前调用堆栈:
终端窗口 backtrace # bt, 查看函数栈信息,可简写为bt。若num是正数,打印栈顶上num层的函数调用信息;# 若num是负数,只打印栈顶下num层的函数调用信息;默认打印当前所有函数调用栈的信息frame # f 切换到frame#(frame#为栈层次编号,可使用info frame进行查询)指定的层次的函数栈up # 切换到上一层函数栈downinfo frame # iinfo localinfo args#查看内存x/nfu addrn: 待显示的内存单元个数f:显示格式u:每个内存单元的大小,按字节数来计算。默认是4 bytes.call malloc_stats() # 查看当前的内存分布call malloc_info(0,stdout) # 把内存分布情况按照xml格式输出,# 加入 -fsanitize=address选项,在发生内存泄漏,堆栈溢出,野指针,全局内存会直接定位到具体代码#查看寄存器register # r, rdi rsi rdx rcx r8 r9分别代表函数前6个。多了的就在栈里面info registerinfo all-register# rip和pc寄存器用来存放下一个需要执行指令的地址,因此我们就是找到需要执行指令的地址,然后设置rip寄存器# 调试时调用内部函数和外部函数p 函数(参数)call 函数(参数)# 多线程调试info threads # 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程thread ID(1,2,3…) # 切换当前调试的线程为指定ID的线程set scheduler-locking on/off # 设置on,只有当前被调试程序会执行;设置off不锁定任何线程,也就是所有线程都执行,这是默认值thread find # 查找线程(只能查找地址,id,名字。后面的函数不能)thread name # 设置线程名字(先切换到像改名字的线程,然后执行)b breakpoint thread id # 为线程设置断点(只有设置的线程会在中断处停止,其他线程不会)thread apply 2 3 i args # 显示线程2,线程3的参数thread apply all i args # 显示所有线程的参数thread apply 2 -s i args# 对线程2执行 i args命令,如果命令错误就没有提示thread apply 2 3 -q i args # 会把线程2,线程3的信息连在一起显示set follow-fork-mode child # 默认是父进程调试。如果要设置子进程需要进行设置i inferiors # 查看当前进程及其子进程信息inferior num # 切换进程号码set logging file xx.txt # 设置保存debug文件set logging on # 保存文件# 远程调试 利用客户端调试远程服务端。服务端需要按照gdbserver服务端:gdbserver ip:9988 ./调试文件客户端:1: gdb 2:target remote ip:9988# 反向执行undo# 软件密码“破解” : 通过 ri 汇编执行到出错位置,查看输入寄存器rsi和rdi,对比结果找到答案backtrace(或简写为bt)命令用于显示当前线程的调用栈。调用栈显示了程序执行过程中的函数调用顺序,它从最内层(当前正在执行的函数)开始,向外层(调用当前函数的函数)展开。- 最上面的是内层函数,也就是当前执行点所在的函数。
- 最下面的是外层函数,也就是程序开始执行的入口点。
寄存器 功能描述 存放数值(十六进制) 数值(十进制) 说明 rax 通用寄存器,返回值 0x1 1 可能表示布尔值或标志位 rbx 基指针寄存器,用于调用约定 0x0 0 可能未被使用或已清零 rcx 计数寄存器,用于循环和参数 0x5 5 可能用于循环计数或函数参数 rdx 通用寄存器,第二个返回值 0x7ff7ee25bce8 140702829100264 可能是指向内存的指针 rsi 源索引寄存器,参数传递 0x89c87f756c 591774315884 可能是函数参数或数据结构的指针 rdi 目的索引寄存器,参数传递 0x89c87f75a4 591774315940 同上 rbp 基指针,访问局部变量 0x0 0 基指针可能已经被清零 rsp 栈指针,指向栈顶 0x89c87f7330 - 栈顶地址,用于访问栈上的数据 r8-r15 用于参数传递和扩展 r8: 0x7ff7ee262cf0
r9: 0x762r8: 140702829128944
r9: 1890通常用于传递函数的第4-6个参数 rip 指令指针,指向下一条指令 0x7ffe952f924c - 指向 ucrtbased!_invoke_watson异常处理函数eflags 状态标志寄存器 0x202 - 包含CPU状态标志,如中断启用标志(IF) cs 代码段寄存器 0x33 51 用于选择代码段 ss 堆栈段寄存器 0x2b 43 用于选择堆栈段 ds 数据段寄存器 0x2b 43 用于选择数据段 es 附加段寄存器 0x2b 43 用于选择附加段 fs 额外段寄存器 0x53 83 用于线程特定数据 gs 机器特定数据寄存器 0x2b 43 用于机器特定数据 st0 浮点寄存器 - 无效的浮点值,可能未初始化或状态异常 st1 浮点寄存器 1.07587300579586992195e-4931 - 非常小的正浮点数,接近于0 st2 浮点寄存器 - 无效的浮点值 st3 浮点寄存器 - 同上 st4 浮点寄存器 -4.77986657081093372053e+986 - 非常大的负浮点数,可能是数值溢出 st5 浮点寄存器 - 无效的浮点值 st6 浮点寄存器 - 同上 -
退出 GDB:
在调试会话结束后,使用
quit或q命令退出 GDB:终端窗口 (gdb) quit # q
这是一个简单的入门教程,涵盖了一些基本的 GDB 命令。GDB 有更多强大的功能,可以帮助你诊断和修复程序中的错误。你可以通过阅读 GDB 的官方文档和教程深入了解更多功能。