gdbのDynamic Printfを使う


はじめに

gdbのDynamic pinrtf機能を使うことで動的にprintfを挿入できます。
breakすることなくプログラムの変数 / 実行パス を確認できます。

使い方

QEMU / gdb で Linux kernel の動きを確認するを例に説明します。

consoleAでQEMUを起動します。

consoleA
TOP_LINUX=~/linux-3.13.0
TOP_BUSYBOX=~/busybox-1.26.2
qemu-system-x86_64 -s -nographic \
  -kernel $TOP_LINUX/arch/x86/boot/bzImage \
  -initrd $TOP_BUSYBOX/rootfs.img \
  -append "root=/dev/ram rdinit=/sbin/init console=ttyS0"

別のconsoleBでgdbを起動してtarget remoteします。

consoleB
gdb vmlinux
GNU gdb (Ubuntu 7.7-0ubuntu3) 7.7
(snip)
Reading symbols from vmlinux...done.
(gdb) target remote :1234

プログラムを実行するとkernel の do_execveがcallされます。
do_execveの引数 filenameを表示してみます。

dprintf do_execve, "do_execve fname=%s\n", filename

consoleB
(gdb) l do_execve
1577    }
1578    
1579    int do_execve(const char *filename,
1580        const char __user *const __user *__argv,
1581        const char __user *const __user *__envp)
1582    {
1583        struct user_arg_ptr argv = { .ptr.native = __argv };
1584        struct user_arg_ptr envp = { .ptr.native = __envp };
1585        return do_execve_common(filename, argv, envp);
1586    }
(gdb) dprintf do_execve, "do_execve fname=%s\n", filename
(gdb) c

consoleAで ls / find / catのコマンドを入力するとconsoleBにdprintfの出力が表示されます。

consoleB
do_execve fname=/bin/ls
do_execve fname=/usr/bin/find
do_execve fname=/bin/cat

address指定

dprintfで表示する場所をaddress指定できます。
do_execveのアドレスを調べます。アドレス指定でdprintfを設定します。

dprintf *0xffffffff8108bc62, "do_execve rax=%llx\n", $rax

consoleB
(gdb) disas do_execve 
Dump of assembler code for function do_execve:
   0xffffffff8108bc60 <+0>: push   %r15
   0xffffffff8108bc62 <+2>: mov    0xffffffff8141d080,%rax
   0xffffffff8108bc6a <+10>:    push   %r14
   0xffffffff8108bc6c <+12>:    mov    %rsi,%r14
(snip)
(gdb) dprintf *0xffffffff8108bc62, "do_execve rax=%llx\n", $rax
(gdb) c
Continuing.
do_execve rax=ffff88000005800

consoleAでコマンドを入力するとdprintfの結果が表示されます。

do_execve rax=ffff88000005800

削除

breakの削除と同じです。

(gdb) i b
Num     Type           Disp Enb Address            What
8       dprintf        keep y   0xffffffff8108bc62 in do_execve 
                                                   at /home/user/linux-3.13.0/arch/x86/include/asm/current.h:14
    breakpoint already hit 2 times
        printf "do_execve rax=%llx\n", $rax
(gdb) d 8
(gdb) i b
No breakpoints or watchpoints.
(gdb) 

Breakpoint Command Lists

break pointを通過したときに実行したいCommand Listsを登録できます。
Breakpoint Command Listsといいます。この機能を使うとdprintfと類似のことができます。

consoleB
(gdb) b do_execve 
Breakpoint 18 at 0xffffffff8108bc60: file fs/exec.c, line 1582.
(gdb) commands 18
Type commands for breakpoint(s) 18, one per line.
End with a line saying just "end".
>silent
>p filename
>c
>end
(gdb) c
Continuing.
$7 = 0xffff880000058020 "/bin/ls"
$8 = 0xffff880000058020 "/bin/ls"
$9 = 0xffff880000058020 "/bin/cat"

(gdb) i b
Num     Type           Disp Enb Address            What
18      breakpoint     keep y   0xffffffff8108bc60 in do_execve 
                                                   at fs/exec.c:1582
    breakpoint already hit 4 times
        silent
        p filename
        c
(gdb) 

dprintfとの違いについてはこちらを参照ください。

references