gdb 调试的 9 条高级技巧 您所在的位置:网站首页 dak800ex调试技巧 gdb 调试的 9 条高级技巧

gdb 调试的 9 条高级技巧

2024-06-03 01:25| 来源: 网络整理| 查看: 265

gdb 是一种强大的命令行调试器,可以用来检查和修改正在运行的程序。除了常用的 file、b、s、n、q、disp、p 等命令,gdb 还有许多高级技巧,可以让调试更加方便快捷。本文将介绍 10 个常用的 gdb 高级技巧,希望对你有所帮助

1. 查看宏

gdb 可以显示宏的定义和展开结果,这对于调试使用了复杂宏的代码很有用。你可以使用 info macro 命令来查看宏的定义,例如:

(gdb) info macro MAX Defined at /usr/include/sys/param.h:224 #define MAX(a, b) (((a) > (b)) ? (a) : (b))

你也可以使用 macro expand 命令来查看宏的展开结果,例如:

(gdb) macro expand MAX(x, y) expands to: (((x) > (y)) ? (x) : (y)) 2. 执行一连串命令

有时候,你可能想要在 gdb 中执行一系列的命令,而不是每次都输入。gdb 提供了几种方法来实现这一点。

你可以使用分号来分隔多个命令,例如 (gdb) b main; r; p argc Breakpoint 1 at 0x4005ed: file test.c, line 5. Starting program: /home/user/test Breakpoint 1, main (argc=1, argv=0x7fffffffe4f8) at test.c:5 5 printf("Hello, world!\n"); $1 = 1 你可以使用 commands 命令来为一个断点指定一系列要执行的命令,例如: (gdb) b main Breakpoint 1 at 0x4005ed: file test.c, line 5. (gdb) commands 1 Type commands for breakpoint(s) 1, one per line. End with a line saying just "end". >p argc >c >end (gdb) r Starting program: /home/user/test Breakpoint 1, main (argc=1, argv=0x7fffffffe4f8) at test.c:5 5 printf("Hello, world!\n"); $1 = 1 Continuing. Hello, world! [Inferior 1 (process 12345) exited normally] 你可以使用 define 命令来自定义一个新的 gdb 命令,例如: (gdb) define mycmd Type commands for definition of "mycmd". End with a line saying just "end". >b main >r >p argc >end (gdb) mycmd Breakpoint 1 at 0x4005ed: file test.c, line 5. Starting program: /home/user/test Breakpoint 1, main (argc=1, argv=0x7fffffffe4f8) at test.c:5 5 printf("Hello, world!\n"); $1 = 1 3. 同时给多个函数打断点

如果你想要在多个函数中设置断点,你可以使用 rbreak 命令来根据正则表达式匹配函数名,并在所有匹配的函数中设置断点。例如:

(gdb) rbreak print 4. .gdbinit 文件

如果你经常使用 gdb,你可能会想要自定义一些 gdb 的设置,例如显示的格式、断点的行为、自定义的命令等。你可以使用 .gdbinit 文件来保存你的 gdb 配置,并在每次启动 gdb 时自动加载它们。.gdbinit 文件是一个普通的文本文件,它包含了一些 gdb 命令,每行一个。你可以将 .gdbinit 文件放在你的主目录或者当前工作目录中,gdb 会按照顺序查找它们。例如,你可以在 .gdbinit 文件中写入以下内容:

set print pretty on set print array on set print array-indexes on set print elements 0 set pagination off define hexdump dump binary memory /tmp/dump.bin $arg0 $arg0+$arg1 shell hexdump -C /tmp/dump.bin end

 这样,每次启动 gdb 时,就会自动设置一些打印选项,并定义一个 hexdump 命令。你可以根据你的需要修改或添加 .gdbinit 文件中的内容。

5. 自定义命令

除了使用 define 命令来创建简单的 gdb 命令外,你还可以使用 python 命令来编写更复杂的 gdb 命令,利用 gdb 的 Python API 来访问和控制 gdb 的内部状态。例如,你可以使用 python 命令来编写一个名为 fib 的 gdb 命令,它可以计算并打印斐波那契数列的第 n 项,如下:

(gdb) python >class FibCommand(gdb.Command): >>def __init__(self): >>>gdb.Command.__init__(self, "fib", gdb.COMMAND_USER) >> >>def invoke(self, arg, from_tty): >>>n = int(arg) >>>a = 0 >>>b = 1 >>>for i in range(n): >>>>a, b = b, a + b >>> >>>print(a) >> >end >FibCommand() >end (gdb) fib 10 55 6. 定义命令钩子

有时候,你可能想要在执行某个 gdb 命令之前或之后自动执行一些操作,例如打印一些信息、设置一些变量、检查一些条件等。你可以使用 hook 命令来定义一个命令钩子,它是一个与 gdb 命令同名的自定义命令,它会在执行原始命令之前或之后运行。例如,你可以定义一个 hook next 命令,它会在每次执行 next 命令之后打印当前行的源代码,如下:

(gdb) define hook next Type commands for definition of "hook next". End with a line saying just "end". >list $pc >end (gdb) b main Breakpoint 1 at 0x4005ed: file test.c, line 5. (gdb) r Starting program: /home/user/test Breakpoint 1, main (argc=1, argv=0x7fffffffe4f8) at test.c:5 5 printf("Hello, world!\n"); (gdb) n 6 return 0; Hello, world! (gdb) n 7 } [Inferior 1 (process 12345) exited normally] 7. GDB 中循环

有时候,你可能想要在 gdb 中重复执行某个命令多次,例如打印一个数组的元素、设置多个断点、修改多个变量等。你可以使用 while 命令来在 gdb 中创建一个循环,它接受一个条件表达式和一个命令块,当条件表达式为真时,就会重复执行命令块。例如,你可以使用 while 命令来打印一个数组的所有元素,如下:

(gdb) p arr $1 = {1, 2, 3, 4, 5} (gdb) set $i = 0 (gdb) while $i < 5 >p arr[$i] >set $i = $i + 1 >end $2 = 1 $3 = 2 $4 = 3 $5 = 4 $6 = 5 8. 函数测试

如果你想要在 gdb 中调用某个函数并查看其返回值或副作用,你可以使用 call 命令来实现。call 命令接受一个函数名和一组参数,并在当前上下文中执行该函数。例如,你可以使用 call 命令来调用 printf 函数并打印一些信息,如下:

(gdb) call printf("Hello from gdb!\n") Hello from gdb! $1 = 15

注意,call 命令会改变程序的状态,因此可能会影响程序的正常运行。你应该谨慎使用 call 命令,并在必要时使用 undo 命令来撤销 call 命令的效果。

9. 反汇编

如果你想要查看程序的汇编代码,你可以使用 disassemble 命令来实现。disassemble 命令接受一个函数名或一个地址范围,并显示该范围内的机器指令。例如,你可以使用 disassemble 命令来查看 main 函数的汇编代码,如下:

(gdb) disassemble main Dump of assembler code for function main: 0x00000000004005e6 : push %rbp 0x00000000004005e7 : mov %rsp,%rbp 0x00000000004005ea : sub $0x10,%rsp 0x00000000004005ee : mov %edi,-0x4(%rbp) 0x00000000004005f1 : mov %rsi,-0x10(%rbp) 0x00000000004005f5 : mov -0x10(%rbp),%rax 0x00000000004005f9 : add $0x8,%rax 0x00000000004005fd : mov (%rax),%rax 0x0000000000400600 : mov %rax,%rdi 0x0000000000400603 : callq 0x4004d6 0x0000000000400608 : mov %eax,%edi 0x000000000040060a : callq 0x400546 0x000000000040060f : mov %eax,%esi 0x0000000000400611 : lea 0xe9(%rip),%rdi # 0x400701 0x0000000000400618 : mov $0x0,%eax 0x000000000040061d : callq 0x4004e6 0x0000000000400622 : mov $0x0,%eax 0x0000000000400627 : leaveq 0x0000000000400628 : retq End of assembler dump.

你可以使用 /m 参数来显示源代码和汇编代码的混合视图,如下:

(gdb) disassemble /m main Dump of assembler code for function main: 5 int main(){ 0x00005555555546e6 : push %rbp 0x00005555555546e7 : mov %rsp,%rbp 6 int n; 7 scanf("%d",&n); 0x00005555555546ea : sub $0x10,%rsp => 0x00005555555546ee : lea -0xc(%rbp),%rax 0x00005555555546f2 : mov %rax,%rsi 0x00005555555546f5 : lea -0xb(%rip),%rdi # 0x5555555546f1 0x00005555555546fc : mov $0x0,%eax 0x0000555555554701 : callq 0x555555554560 8 printf("%d\n",f(n)); 0x0000555555554706 : mov -0xc(%rbp),%eax 0x0000555555554709 : mov %eax,%edi 0x00


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有