显示在gdb中执行的每个汇编指令

Dan*_*ego 10 debugging assembly gdb freebsd

我目前有一个棘手的错误发生在我无法访问源或符号的地方,即我可以看到崩溃发生的指令及其地址,但就是这样.我想做的是gdb运行而不需要交互并显示每条指令,但我还没有找到办法.

我希望实现的是这样的:

(gdb) /* some command */
0x2818a7c0: push   %ebp
0x2818a7c1: mov    %esp,%ebp
0x2818a7c3: push   %ebx
0x2818a7c4: sub    $0x4,%esp
...
0x28563622: mov    %esi,0x0(%eax)
Program received signal SIGSEGV, Segmentation fault.
Run Code Online (Sandbox Code Playgroud)

我一直在做的是为程序计数器设置一个显示器,如下所示:

(gdb) display/i $pc
Run Code Online (Sandbox Code Playgroud)

然后运行代码stepi:

(gdb) stepi
1: x/i $pc  0x2818a7c0: push   %ebp
Run Code Online (Sandbox Code Playgroud)

然而,崩溃是数百或数千条指令,我想要一种方法来看每一个(一起,如果可取的话),而不必多次点击"输入".另外,如果我是手动完成的,我会(gdb)在每条指令之间看到一个提示,这是不太理想的.

我简要介绍的一条路线是脚本,但我唯一的想法是设置main(),显示和另一个中断(下一条指令),然后继续,但后来我不能commands在一个commands块内使用,所以它不会像我想象的那样工作.

如果它很重要,我正在使用FreeBSD.

Emp*_*ian 15

以下内容应该按照您的要求进行:

# not strictly required, but you'll likely want the log anyway
(gdb) set logging on

# ask gdb to not stop every screen-full
(gdb) set height 0

(gdb) while 1
 > x/i $pc
 > stepi
 > end
Run Code Online (Sandbox Code Playgroud)

但是,这种调试方法可能会徒劳无功:即使在大多数琐碎的程序中也只执行了太多的指令.

更好的方法可能是运行程序直到崩溃,尝试了解当前函数正在做什么以及谁调用它,并适当地设置断点.

在x86上,您甚至可以在完全剥离的可执行文件中推断出函数边界.

您需要注意的另一件事是strace/truss输出,因此您可以看到崩溃点之前的系统调用.


Cir*_*四事件 5

Python脚本

这将提供比 GDB 脚本更多的灵活性来实现您的疯狂想法。

这里的主要问题,就像 GDB 脚本一样,对于大多数没有目标硬件支持的应用程序来说,这可能太慢了,例如:C hello world 只需要 18k 指令就需要 1 分钟。

数据库文件

class TraceAsm(gdb.Command):
    def __init__(self):
        super().__init__(
            'trace-asm',
            gdb.COMMAND_BREAKPOINTS,
            gdb.COMPLETE_NONE,
            False
        )
    def invoke(self, argument, from_tty):
        argv = gdb.string_to_argv(argument)
        if argv:
            gdb.write('Does not take any arguments.\n')
        else:
            done = False
            thread = gdb.inferiors()[0].threads()[0]
            last_path = None
            last_line = None
            with open('trace.tmp', 'w') as f:
                while thread.is_valid():
                    frame = gdb.selected_frame()
                    sal = frame.find_sal()
                    symtab = sal.symtab
                    if symtab:
                        path = symtab.fullname()
                        line = sal.line
                    else:
                        path = None
                        line = None
                    if path != last_path:
                        f.write("path {}{}".format(path, os.linesep))
                        last_path = path
                    if line != last_line:
                        f.write("line {}{}".format(line, os.linesep))
                        last_line = line
                    pc = frame.pc()
                    f.write("{} {} {}".format(hex(pc), frame.architecture().disassemble(pc)[0]['asm'], os.linesep))
                    gdb.execute('si', to_string=True)
TraceAsm()
Run Code Online (Sandbox Code Playgroud)

GitHub 上游.

电源

global _start
_start:
    ; Write.
    mov rax, 1
    mov rdi, 1
    mov rsi, hello_world
    mov rdx, hello_world_len
    syscall

    ; Exit.
    mov rax, 60
    mov rdi, 0
    syscall

hello_world db "hello world", 10
hello_world_len equ $ - hello_world
Run Code Online (Sandbox Code Playgroud)

GitHub 上游.

组装并运行:

as -o main.o main.S
ld -o main.out main.o
gdb -nh -batch -ex 'source ~/test/gdb.py' -ex 'starti' -ex 'trace-asm' ./main.out
cat trace.tmp
Run Code Online (Sandbox Code Playgroud)

输出:

0x401000 mov    $0x1,%rax 
0x401007 mov    $0x1,%rdi 
0x40100e mov    $0x402000,%rsi 
0x401015 mov    $0xc,%rdx 
0x40101c syscall  
0x40101e mov    $0x3c,%rax 
0x401025 mov    $0x0,%rdi 
0x40102c syscall
Run Code Online (Sandbox Code Playgroud)

QEMU 仿真

这比 GDB python 解决方案执行得快得多,C hello 工作立即运行!但是,日志只有 10k 条指令长,而不是同一可执行文件上的 18k 条指令,因此它必须跳过通常可以运行 TODO 理解的内容。

例如在用户模式模拟中:

qemu-x86_64 -d in_asm ./main.out
Run Code Online (Sandbox Code Playgroud)

输出:

warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
----------------
IN: 
0x0000000000401000:  mov    $0x1,%rax
0x0000000000401007:  mov    $0x1,%rdi
0x000000000040100e:  mov    $0x402000,%rsi
0x0000000000401015:  mov    $0xc,%rdx
0x000000000040101c:  syscall 

hello world
----------------
IN: 
0x000000000040101e:  mov    $0x3c,%rax
0x0000000000401025:  mov    $0x0,%rdi
0x000000000040102c:  syscall
Run Code Online (Sandbox Code Playgroud)

另见

在 Ubuntu 18.10、GDB 8.2、QEMU 2.12.0 中测试。

  • @ttb 嗨,您只需像常规 GDB 脚本一样获取它,请参阅更新的答案。绑定内置于 GDB 中,无需额外安装任何东西。我在回顾这个时也对 QEMU 有一些乐趣,并补充说。 (2认同)