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输出,因此您可以看到崩溃点之前的系统调用.
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)
电源
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)
组装并运行:
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 中测试。