QEMU is able to emulate ARM architectures with many details. One thing that the qemu-system-arm
program implements is the Angel interface, that enables semihosting. Semihosting involves two machines: a target (the embedded device), and a host (such as a PC) that is connected to the target through a debug interface. An embedded program running in semihosted mode is executing on the target architecture, but is able to see some resources of the host machine.
I created a simple bare metal program to test the semihosting functionality. The C code of the program is the following:
05 |
printf ( "Hello World!\n" ); |
06 |
fp = fopen ( "log.txt" , "w" ); |
07 |
if (fp == NULL) return ; |
08 |
while ( fgets (line, 100, stdin) != NULL) { |
09 |
fprintf (fp, "%s" , line); |
10 |
if (line[0] == 'q' ) break ; |
The purpose is to try to access the standard output, standard input and filesystem of the host. To compile this example on my Linux machine I used the toolchain Sourcery G++ Lite 2010q1-188, that comes with a series of libraries and linker scripts that allow to create programs running in a hosted environment. In my case (depending on the installation path) the directory “~/CodeSourcery/Sourcery_G++_Lite/arm-none-eabi/lib/
” contains the linker script “generic-hosted.ld
” that is almost OK to use: it needs just a little change in the memory description because of QEMU behavior.
I copied “generic-hosted.ld
” into a local file that I called “qemu-hosted.ld
” and then I opened to modify it. The problem is that QEMU loads the program binary into address 0x10000
, but the linker script assumes that the program is executed at address 0x0
. I decided to change the memory section of the script to tell the linker that the memory starts at 0x10000
; in practice I changed the following lines:
3 |
ram (rwx) : ORIGIN = 0x0, LENGTH = 128M |
…into these:
3 |
ram (rwx) : ORIGIN = 0x10000, LENGTH = 127M |
To compile the program and run it inside QEMU the commands are (assuming the C source file has been called “main.c
“):
1 |
$ arm-none-eabi-gcc -T qemu-hosted.ld main.c -o main.elf |
2 |
$ arm-none-eabi-objcopy -O binary main.elf main.bin |
3 |
$ qemu-system-arm -semihosting -kernel main.bin |
The terminal will display the “Hello World!
” line and then accept user input. The input is written inside the file “log.txt
” and when the standard input is closed (using CTRL-D
) or the line starts with the character ‘q
‘ the program ends. To start QEMU without the graphic window, use the following command:
1 |
$ qemu-system-arm -nographic -monitor null -serial null -semihosting -kernel main.bin |
The “-monitor null -serial null
” options are necessary to avoid conflicts on the standard streams.
What happens underneath this emulation? To discover it, it’s interesting to watch the assembly code that is run. The following command disassembles the program into the file main.code:
1 |
$ arm-none-eabi-objdump -d main.elf > main.code |
Examining the code, it can be traced that the main
function calls fopen
, which eventually calls a low-level function called _open
, that contains a SuperVisor Call (SVC) instruction.
3 |
13b18: e3a00001 mov r0, #1 |
4 |
13b1c: ef123456 svc 0x00123456 |
The SVC instruction, when using 0x123456
as parameter, tells QEMU to use its implementation of the Angel interface. The r0
register contains the code of the function to emulate, according to this table. Other registers can be used as other parameters to pass, like a function call.
The semihosting functionality is useful in prototyping, especially to emulate some parts of the systems to speed up development; but the possibilities are various. For example:
- when data coming from a sensor is difficult to control, it can be emulated by getting data from a file on the host machine instead.
- the program can dump intermediate data to file, without problems of non-volatile memory space.
- the embedded device can get/send data with the Internet through the host PC.
Posted on 2010/11/04 by Balau
2