Отображение каждой инструкции по сборке, выполненной в GDB - PullRequest
10 голосов
/ 12 января 2012

В настоящее время у меня есть хитрая ошибка, которая возникает в месте, где у меня нет доступа к источнику или символам, то есть я вижу инструкцию и ее адрес, где происходит сбой, но это все. Мне бы хотелось, чтобы 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.

То, что я делал, настраивало отображение счетчика программы, например:

(gdb) display/i $pc

Изатем пробегая код с помощью stepi:

(gdb) stepi
1: x/i $pc  0x2818a7c0: push   %ebp

Однако сбой происходит на расстоянии сотен или тысяч инструкций, и я хотел бы увидеть все (вместе, если желательно), безприходится многократно нажимать «войти».Кроме того, если бы я делал это вручную, я бы увидел подсказку (gdb) между каждой инструкцией, что меньше, чем хотелось бы.

Один маршрут, который я кратко изучил, это scripting , но моя единственная мысль - установить на main(), отобразить его и сделать еще один перерыв (для следующей инструкции), а затем продолжить, но тогда я не могу использовать commands в блоке commands, поэтомуне сработает так, как я себе это представляю.

В случае, если это имеет значение, я работаю над FreeBSD.

Ответы [ 3 ]

15 голосов
/ 13 января 2012

Следующее должно делать то, что вы просили:

# 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

Однако такой подход к отладке, вероятно, окажется бесполезным: просто слишком много инструкций, выполняемых даже в большинстве тривиальных программ.

Лучшим подходом может быть запуск программы до сбоя, попытка понять, что делает текущая функция и кто ее вызывает, и правильная установка точек останова.

В x86 вы часто можете определить границы функций даже в полностью удаленном исполняемом файле.

Еще одна вещь, которую вы захотите посмотреть, - это strace/truss output, чтобы вы могли видеть, какие системные вызовы непосредственно предшествуют точке сбоя.

1 голос

Сценарии Python

Это даст большую гибкость, чем сценарии GDB, для реализации ваших безумных идей.

Основная проблема, как и в случае сценариев GDB, заключается в том, чтоэто, вероятно, будет слишком медленным для большинства приложений без поддержки целевого оборудования, например: мир приветствия C занимает 1 минуту только для 18k инструкций.

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

GitHubupstream .

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 .

Сборка и запуск:

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

Вывод:

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

Это выполняется намного быстрее, чем решение GDB Python, C hello работает мгновенно!Однако в журнале было всего 10 тыс. Инструкций вместо 18 тыс. В одном и том же исполняемом файле, поэтому он должен пропускать то, что обычно запускается при запуске TODO.

Например, при моделировании в пользовательском режиме:

qemu-x86_64 -d in_asm ./main.out

Вывод:

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

См. Также .

Проверено в Ubuntu 18.10, GDB 8.2, QEMU 2.12.0.

0 голосов
/ 13 января 2012
  1. Разберите двоичный файл отдельно (например, с помощью objdump) и просмотрите список при отладке
  2. Используйте IDA и его отладчик. Гораздо лучше опыт ИМО.

(отказ от ответственности: я работаю на Hex-Rays)

...