Displaying each assembly instruction executed in gdb

后端 未结 3 906
别跟我提以往
别跟我提以往 2020-12-14 22:04

I currently have a tricky bug that occurs in a place where I don\'t have access to source or symbols, i.e. I can see the instruction and its address where the crash occurs,

3条回答
  •  旧巷少年郎
    2020-12-14 22:34

    Python scripting

    This will give more flexibility than GDB scripting to achieve your crazy ideas.

    The main problem here, just like with GDB scripts, is that this will likely be too slow for most applications without target hardware support, e.g.: a C hello world takes 1 minute for only 18k instructions.

    gdb.py

    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()
    

    GitHub upstream.

    main.S

    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
    

    GitHub upstream.

    Assemble and run:

    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
    

    Output:

    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
    

    QEMU emulation

    This performed much faster than the GDB python solution, the C hello works runs instantaneously! However, the log was only 10k instructions long instead of 18k on the same executable, so it must be skipping something that normally gets run TODO understand.

    E.g. in user mode simulation:

    qemu-x86_64 -d in_asm ./main.out
    

    Output:

    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
    

    See also.

    Tested in Ubuntu 18.10, GDB 8.2, QEMU 2.12.0.

提交回复
热议问题