QEMU
Contents
Intro
Sometimes, we need debug program, because static analisys can't cover all our hopes. Sometimes we need run program to see value of register or variable. For usual program it is so simple - just start any degugger, load program, run and debug, debug... But, not for bootloaders, even more for embedded platform. But this is also possible. We need to know that we are luckies - qemu (now - the best system emulator) support gdbserver mode, which allow run everything you want in qemu, stop, and then connect from any other place with gdb or it's frontend to the running qemu, as for real hardware. But, if PC emulation support BIOS loading/debugging, mainstream QEMU doesnt support such thing for ARM. Here you can find patched qemu, for support booting and debugging bootrom, and bootloaders. Also here you can find examples of its use.
Install
Download sources from Github: [1]
Build them :
./configure --target-list=arm-softmmu make sudo make install
Use case
You can use loading your custom bootrom from file by using parameters: -bios and -L dir. They let choose bios (bootrom) name and path Because we need ARM, we need use name "bootrom.bin" as bios name, and it must be valid 32K file
Also, if something goes wrong, for example:
qemu: fatal: Trying to execute code outside RAM or ROM at 0x00014748 R00=0001b860 R01=4020fcb0 R02=0000002c R03=00014748 R04=00014000 R05=00000000 R06=0000030f R07=4001b82f R08=00000000 R09=00000000 R10=4001b840 R11=4001b860 R12=00000000 R13=4020fcac R14=40014724 R15=00014748 PSR=400001d3 -Z-- A svc32
you can enable tracing in qemu by enabling option "-d cpu,exec,in_asm" - it create trace log in /tmp/qemu.log, for example:
---------------- IN: 0x40014708: ea000000 b 0x40014710 R00=00c51878 R01=00000001 R02=00000000 R03=00000000 R04=00014000 R05=00000000 R06=0000030f R07=00000000 R08=00000000 R09=00000000 R10=00000000 R11=00000000 R12=00000000 R13=4020fcac R14=40014904 R15=40014710 PSR=600001d3 -ZC- A svc32 ---------------- IN: 0x40014710: e28f0028 add r0, pc, #40 ; 0x28 0x40014714: e8900c00 ldm r0, {sl, fp} 0x40014718: e08aa000 add sl, sl, r0 0x4001471c: e08bb000 add fp, fp, r0 0x40014720: e24a7001 sub r7, sl, #1 ; 0x1 0x40014724: e15a000b cmp sl, fp 0x40014728: 0a000164 beq 0x40014cc0 R00=40014740 R01=00000001 R02=00000000 R03=00000000 R04=00014000 R05=00000000 R06=0000030f R07=4001b82f R08=00000000 R09=00000000 R10=4001b830 R11=4001b860 R12=00000000 R13=4020fcac R14=40014904 R15=4001472c PSR=800001d3 N--- A svc32 ----------------
Such log can help you find the place of error (or in qemu or in running image)
so, for example you can run qemu:
qemu-system-arm -M milestone -m 256 -L . -bios bootrom.bin -mtdblock mbmloader-1.raw -d in_asm,cpu,exec -nographic
Here you can find examples of trace logs:
Debugging
Also it is possible use qemu for debugging purposes: you need only two options for that: -s and -S
- -s option run qemu in gdbserver mode at localhost port 1234
- -S stop execution when debugging start
for example:
qemu-system-arm -M milestone -m 256 -L . -bios bootrom.bin -mtdblock mbmloader-1.raw -d in_asm,cpu,exec -s -S -nographic
And now we can connect with gdb or any its frontend to localhost:1234 and start debuging process:
(gdb) target remote localhost:1234 (gdb) set architecture arm
Useful commands:
- "x/i $pc" - enable printing each executed instruction
- "si" - step one machine instruction (enter inside each function)
- "ni" - step one machine instruction (without entering functions)
- "c" - continue execution
- "bt" - backtrace - show stack
- "i p" - show current state of the program
- "i r" - show all registers
- "p $eax" - show content of eax register (can be used with any register/variable)
My ~/.gdbinit file:
python import sys sys.path.insert(0, '/home/xvilka/gdb/python') from libstdcxx.v6.printers import register_libstdcxx_printers register_libstdcxx_printers (None) end set history save on set disassembly-flavor intel display/4i $pc
Here you can see example of my session:
(gdb) show architecture The target architecture is set automatically (currently i386) (gdb) target remote localhost:1234 Remote debugging using localhost:1234 warning: Architecture rejected target-supplied description 0x00000000 in ?? () 1: x/4i $pc => 0x0: add BYTE PTR [eax],al 0x2: add BYTE PTR [eax],al 0x4: add BYTE PTR [eax],al 0x6: add BYTE PTR [eax],al (gdb) show architecture The target architecture is set automatically (currently i386) (gdb) set architecture arm The target architecture is assumed to be arm (gdb) i prog Debugging a target over a serial line. Program stopped at 0x0. It stopped with signal SIGTRAP, Trace/breakpoint trap. (gdb) si Prologue scan stopped at 0xfffffff8 Prologue scan stopped at 0xfffffff8 0x400148c0 in ?? () 1: x/4i $pc => 0x400148c0: ldr r0, [pc, #148] ; 0x4001495c 0x400148c4: ldr r0, [r0] 0x400148c8: lsr r0, r0, #8 0x400148cc: and r0, r0, #7 (gdb) si Prologue scan stopped at 0xfffffff8 0x400148c4 in ?? () 1: x/4i $pc => 0x400148c4: ldr r0, [r0] 0x400148c8: lsr r0, r0, #8 0x400148cc: and r0, r0, #7 0x400148d0: cmp r0, #3 (gdb) i r r0 0x480022f0 1207968496 r1 0x0 0 r2 0x0 0 r3 0x0 0 r4 0x0 0 r5 0x0 0 r6 0x0 0 r7 0x0 0 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0x0 0 r12 0x0 0 sp 0x0 0x0 lr 0x0 0 pc 0x400148c4 0x400148c4 cpsr 0x400001d3 1073742291 (gdb) si Prologue scan stopped at 0xfffffff8 0x400148c8 in ?? () 1: x/4i $pc => 0x400148c8: lsr r0, r0, #8 0x400148cc: and r0, r0, #7 0x400148d0: cmp r0, #3 0x400148d4: bne 0x400148f0 (gdb) si Prologue scan stopped at 0xfffffff8 0x400148cc in ?? () 1: x/4i $pc => 0x400148cc: and r0, r0, #7 0x400148d0: cmp r0, #3 0x400148d4: bne 0x400148f0 0x400148d8: ldr r0, [pc, #124] ; 0x4001495c (gdb) si Prologue scan stopped at 0xfffffff8 0x400148d0 in ?? () 1: x/4i $pc => 0x400148d0: cmp r0, #3 0x400148d4: bne 0x400148f0 0x400148d8: ldr r0, [pc, #124] ; 0x4001495c 0x400148dc: ldr r0, [r0] (gdb) si Prologue scan stopped at 0xfffffff8 0x400148d4 in ?? () 1: x/4i $pc => 0x400148d4: bne 0x400148f0 0x400148d8: ldr r0, [pc, #124] ; 0x4001495c 0x400148dc: ldr r0, [r0] 0x400148e0: mov r6, r0 (gdb) si Prologue scan stopped at 0xfffffff8 0x400148d8 in ?? () 1: x/4i $pc => 0x400148d8: ldr r0, [pc, #124] ; 0x4001495c 0x400148dc: ldr r0, [r0] 0x400148e0: mov r6, r0 0x400148e4: and r0, r0, #31 (gdb) i r r0 0x3 3 r1 0x0 0 r2 0x0 0 r3 0x0 0 r4 0x0 0 r5 0x0 0 r6 0x0 0 r7 0x0 0 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0x0 0 r12 0x0 0 sp 0x0 0x0 lr 0x0 0 pc 0x400148d8 0x400148d8 cpsr 0x600001d3 1610613203 (gdb)
IDA Pro
First, start IDA Pro, open idb for your file, go to menu Debugging -> Debugging Options -> edit all that you want For start debugging just open Debugging -> Attach to process -> Remote GDB debugger -> localhost:1234 (if you use "-s" option for qemu) run!
Here you can see example of debugging bootrom in the IDA Pro disassembler:
Usually i'm also enabling tracing log / instruction tracing - it is very useful: