本文分享一些高级的调试技巧, 不使用纯 gdb 命令行而是在 vscode 配合 c++ 插件更好的可视化调试
.gdbinit 是 GDB 的初始化文件,它用于在启动 GDB 时自动执行一些预定义的命令.这个文件可以包含用户自定义的设置、命令别名、断点、脚本等,方便调试环境的个性化配置
由于 .gdbinit 文件可以执行任意 GDB 命令,因此它具有很高的权限,可能被用于执行恶意命令.为了安全起见,GDB 默认会限制从当前工作目录加载 .gdbinit 文件. 因此首先创建 ~/.config/gdb/gdbinit 赋予安全权限
set auto-load safe-path /
linux kernel 提供了一个脚本用于调试
add-auto-load-safe-path ./scripts/gdb/vmlinux-gdb.py
gdb 还有一些其他的配置选项, 您可以创建 .gdbinit
文件(参考 xv6) 来辅助调试
使用 python 辅助调试 gdb 参见 python-gdb
默认调试时只会显示当前局部变量, 可以直接在 vscode 左侧监控填入变量名字
默认变量是以 10 进制显示, 如果希望以 2 进制或者以 16 进制查看可以修改为
var,b
var,x
如果变量是一个数组, 可以手动标注它的类型展示每一个元素
*(u8 *)numa_distance@10
如果调试的程序需要 sudo 权限去读取某些内核文件, 可以先调试一下, 此时会打开一个调试的 cppdbg 的终端, 在此终端中输入 su
进入 root 用户, 再次点击调试即可
当前 gdb 调试的对象是操作系统内核的代码, 如果我们希望执行一个可执行文件, 并且在可执行文件的代码中也加入断点
以下述代码为例, 其中使用到了 fork 多进程, 代码逻辑比较简单, 就是三个进程随机打印 ABC
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sched.h>
#include <unistd.h>
char bar[3968] = "\n";
char foo[4096] = "this is not a test\n";
void output_loop(char *str)
{
int i;
for (i = 0; i < 20; i++) {
write(2, str, strlen(str));
sched_yield();
}
}
int main()
{
int pid1, pid2, status;
write(2, foo, strlen(foo));
strcpy(foo, "you are modified\n");
write(2, foo, strlen(foo));
if (!(pid1 = fork())) {
output_loop("B ");
exit(0);
}
if (!(pid2 = fork())) {
output_loop("C ");
exit(0);
}
output_loop("A ");
waitpid(pid1, &status, 0);
waitpid(pid2, &status, 0);
write(2, "\n", 1);
while (1)
;
exit(0);
}
使用静态链接编译, 并加上调试选项 -g
gcc -static -g a.c -o a
使用 readelf 找到该文件的 .text 段的虚拟地址, 即 0x4011c0
$ readelf -S a | grep .text
[ 7] .text PROGBITS 00000000004011c0 000011c0
将该可执行文件拷贝到磁盘镜像当中, 比如前文的 initramfs/bin 下, 或者 mount 磁盘然后 copy 过去
修改 .gdbinit, 其中 add-symbol-file 表示添加一个文件, 然后跟一个代码段起始地址, 然后我们在 23 行(即write)的位置打一个断点
注意不要在 main 上打断点, 停不住
add-auto-load-safe-path ./scripts/gdb/vmlinux-gdb.py
add-symbol-file /home/kamilu/workspace/a 0x4011c0
b a.c:23
启动 qemu, 执行 /bin/a 即可发现程序停在 23 行的位置了
调试 32 位 x86 内核的情况, 需要指定 archi 为 i386
, 添加 gdb 的配置信息
# .gdbinit
set archi i386:x86-64
修改 launch.json
{
"version": "0.2.0",
"configurations": [
{
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set debuggee architecture",
"text": "set archi i386:x86-64",
"ignoreFailures": true
}
]
}
]
}