Getting QEMU to run an ARM Thumb binary requires configuring QEMU to emulate an ARM processor in Thumb mode. The key steps are: choosing the right ARM emulator in QEMU, enabling Thumb mode, setting up the memory layout, and specifying the ARM binary to load. With the right settings, QEMU can successfully execute ARM Thumb binaries for testing and development purposes.
Introduction to ARM Thumb
Thumb is an instruction set architecture (ISA) variant of the ARM processor architecture. It is more compact (has higher code density) than the standard 32-bit ARM ISA, with 16-bit Thumb instructions instead of 32-bit instructions. This allows better performance in constrained environments where code size matters.
The Thumb ISA is a subset of the ARM ISA. All Thumb instructions have equivalent ARM instructions, but Thumb code is not directly executable on ARM processors without some mode switching. ARM processors since the ARMv4T architecture have support for a Thumb mode which allows seamlessly switching between ARM and Thumb instruction sets.
When compiling code for ARM, the -mthumb compiler switch generates Thumb code instead of ARM code. The resulting binary contains either Thumb or Thumb-2 instructions. Thumb-2 was introduced in ARMv6T2 and has some 16-bit and some 32-bit instructions for better performance while retaining small code size.
Choosing the ARM Emulator in QEMU
QEMU supports emulating a variety of ARM chips. To run Thumb code, you need to choose an ARM emulator in QEMU that supports Thumb instructions. Here are some options:
- arm926ej-s – ARM9TDMI core with ARMv5TEJ architecture, supports Thumb and Jazelle (for Java acceleration)
- arm1136-r2 – Cortex-R1136 core with ARMv6 architecture, includes Thumb-2 and VFPv2
- cortex-a8 – Cortex-A8 core with ARMv7-A architecture, supports Thumb-2 and NEON advanced SIMD
- cortex-a15 – Cortex-A15 core with ARMv7-A architecture, supports Thumb-2 and NEON
For example, to emulate a Cortex-A8 processor, pass the -M cpu=cortex-a8 option to QEMU: qemu-arm -M cpu=cortex-a8 [other options…]
Enabling Thumb Mode in QEMU
By default, the emulated ARM CPU will start in ARM state and expect ARM instructions. To run Thumb binaries, the CPU’s Thumb state must be enabled first.
There are two ways to do this in QEMU:
- Start emulation with semihosting enabled. The semihosting syscall interface will automatically transition the processor into Thumb state.
- Modify the CPU’s CPSR register value to change the T bit/Thumb state bit before loading the binary. This can be done using the GDB stub or QEMU monitor.
For example, to enable Thumb mode with semihosting when launching QEMU: qemu-arm [..] -g 1234 -semihosting-config enable=on,target=native [..]
Or to modify the CPSR register to set T bit using the GDB stub: (gdb) target remote :1234 (gdb) set $cpsr = $cpsr | (1 << 5)
Setting Up Memory Layout
The ARM emulator in QEMU loads the binary at address 0x00008000 by default. But Thumb binaries are position-independent code (PIC) and expect to be loaded at address 0x00000000.
To load the Thumb binary at the right base address, the memory and code entry point need to be set up properly. This can be done by specifying the -kernel option in QEMU: qemu-arm [..] -kernel my_thumb_binary.elf [..]
This will load the binary at the base address 0x00000000 as needed for Thumb PIC code.
Alternatively, a simple bootloader can be used to load the Thumb binary into the correct memory location. The bootloader code runs in ARM state, copies the Thumb binary to address 0x00000000, enables Thumb state, and jumps to the entry point to begin Thumb execution.
Specifying the Thumb Binary
Finally, the compiled Thumb binary must be passed to QEMU to load into the emulated ARM machine.
For an ELF formatted Thumb binary, use the -kernel option: qemu-arm [..] -kernel my_thumb_binary.elf [..]
For a raw binary, load it using the -mtdblock option: qemu-arm [..] -mtdblock my_thumb_binary.bin [..]
Make sure to compile your ARM program to target Thumb, for example with gcc: $ gcc -mthumb -o my_thumb_binary.elf mycode.c
Launching QEMU with Thumb Binary
Putting all of this together, a typical QEMU launch command to run an ARM Thumb binary would be: qemu-arm -M cpu=arm926ej-s -m 256 -kernel my_thumb_binary.elf -semihosting-config enable=on,target=native -g 1234
This emulates an ARM926EJ-S core with 256MB RAM, loads the Thumb ELF binary using the -kernel option, enables semihosting to start in Thumb state, and opens port 1234 for GDB debugging.
Verifying Thumb Execution in QEMU
To verify the ARM emulator is actually running Thumb code correctly, we can check the value of the CPSR register as well as watch Thumb-specific instructions being executed.
In the GDB session, CPSR will show the Thumb state bit enabled: (gdb) info reg cpsr cpsr 0x000000d3 219
Single stepping through instruction execution with stepi should show 16-bit Thumb instructions like push, pop, mov, cmp etc. This confirms the CPU is decoding and running Thumb binary instructions.
Limitations of Thumb Emulation
QEMU’s emulated ARM CPU may have some limitations or deviations when running Thumb code compared to real hardware. A few things to watch out for:
- Timing – Instruction timings may not exactly match a real chip
- Undocumented opcodes – Behavior of illegal instruction exceptions may differ
- CP15/CP14 differences – Thumb EE state handling may not be fully emulated
- No Thumb-2 support – Only basic Thumb instruction set is implemented
For more thorough testing, real ARM hardware or an FPGA prototype is recommended. QEMU is best suited for general software development and experimentation with Thumb.
QEMU is capable of emulating ARM processors in Thumb execution state, allowing ARM Thumb binaries to be run. Key steps are choosing the right ARM emulator, enabling Thumb mode, configuring the memory layout, and loading the Thumb binary. With the proper configuration, the powerful ARM emulation capabilities of QEMU can be leveraged for working with Thumb code.
Some limitations exist compared to real hardware, so care must be taken when testing corner cases. But overall, running ARM Thumb programs on QEMU is a useful development tool for software targeting the Thumb ISA and embedded ARM processors.