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:
#include <stdio.h>
void main() {
FILE *fp;
char line[100];
printf("Hello World!\n");
fp = fopen("log.txt", "w");
if(fp == NULL) return;
while(fgets(line, 100, stdin) != NULL) {
fprintf(fp, "%s", line);
if(line[0] == 'q') break;
}
close(fp);
}
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:
MEMORY
{
ram (rwx) : ORIGIN = 0x0, LENGTH = 128M
}
…into these:
MEMORY
{
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“):
$ arm-none-eabi-gcc -T qemu-hosted.ld main.c -o main.elf $ arm-none-eabi-objcopy -O binary main.elf main.bin $ 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:
$ 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:
$ 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.
00013a9c <_open>: [...] 13b18: e3a00001 mov r0, #1 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.
Entries
Geoffrey Brown
2011/01/14
I found this writeup helpful. Note that Qemu understands elf files and hence there
is no need for the objcopy step. Furthermore, the elf file generated with the generic-hosted.ld
linker script is fine since it provides a starting address — thus, there’s no need for the
special linker script you created.
Balau
2011/01/15
Thanks for the suggestion!
I tried it, attached to QEMU with arm-none-eabi-gdb, and verified that it behaves differently when you give it an ELF file instead of a binary file.
halim
2012/03/15
hello balau
what is the difference between code sourcery EABI and other one gnueabi
and what is the function of each ?
another question
i got arm mini6410 but i have problem with screen resolution as it is H43 and the zImage came with n43
i cross compiled kernel with h43 config file but it still the same problem
so does the kernel is the reasonable for that or rootfs
finally where i will put my qt Gui in my rootfs and how to run it when the OS start
Balau
2012/03/15
- CodeSourcery does not have gnueabi, so I think you are asking me about the difference between CodeSourcery eabi toolchains and other gnueabi toolchains such as Linaro and EmDebian. I think the eabi and gnueabi string in the toolchain name (the triplet of Architecture-OperatingSystem-BinaryInterface) means exactly the same, but gnueabi is the GNU open source implementation.
- you mean each toolchain? The “function” is declared in the toolchain prefix. For CodeSourcery, the “arm-none-eabi” is for bare metal, the “arm-none-linux-eabi”, “arm-uclinuxeabi” and “arm-none-symbianelf” are for user space programs of the corresponding operating system.
- I think you should have a kernel module that supports the resolution, an Xserver (in rootfs) that can implement it and, if the resolution is not the default, a configuration file (in rootfs) that enables it.
- depends on the Linux distribution that you are using. You should understand how the distribution handles the Linux runlevels and add your program to the multi-user graphical runlevel (often it is level 5).
Roman
2012/08/17
Thanks for this short but very helpful post!!!
I got it running with qemu 0.10.2 (used the windows binary included in qemu manager v6).
But qemu 0.11.1 (included in qemu manager v7) seems to hang bevore writing something into log.txt.
Balau
2012/08/17
If QEMU hangs before writing into log.txt then the problem is not in your program, because QEMU doesn’t even begin to execute it.
Could be many things:
I don’t know QEMU Manager, I think you should ask the author or the other users (is there a forum or a mailing list?) about it.
Lendar
2013/03/28
Hello Balau,
thanks for the great guide!
A foreword: I am a totally newbie on this field, so please forgive me if I ask anything absurd or miss easy steps.
I’m using
gcc version 4.5.2 (Sourcery G++ Lite 2011.03-42)
and running
QEMU emulator version 1.1.2
on Ubuntu 10.04 (installed from repositories)
I was able to successfully compile and run this example, but I was wondering what would I have to do to to use semihosting when running code for a Cortex-M3 device (specifically an STM32, but I guess that every kind of M3 cortex would do).
I tried to add the command switch from your “Using CodeSourcery bare metal toolchain for Cortex-M3″ example, but had no success.
It looks like adding the “-mthumb -march=armv7 -mfix-cortex-m3-ldrd” arguments to the compiler prevents qemu from running properly, or at least to enable semihosting.
This is the sequence of commands that I use:
arm-none-eabi-gcc -mthumb -march=armv7 -mfix-cortex-m3-ldrd -T qemu-hosted.ld main.c -o main.elf
arm-none-eabi-objcopy -O binary main.elf main.bin
qemu-system-arm -semihosting -kernel main.bin
Qemu seems to start, because I see the “VNC server running” trace, but I don’t get the expected “Hello world” print on the console.
Do you have any idea? Thanks again for the great blog!
Balau
2013/03/29
ARM processors are very different from one another. An architecture is usually composed by one (or more) CPUs (ARM926, Cortex-A8, Cortex-M3, …) and many peripherals (UART, SPI, timers, GPIOs, …) that are organized in a memory map and accessed through address read/write. Programs compiled for one chip usually don’t run on another chip, because the CPU or the memory map are different. For this reason, compilation options and the QEMU options must target the same chip. QEMU can’t emulate STM32, but just some other chips like the lm3s6965evb (you can see the complete list by running “
qemu-system-arm -M ?“. I suggest you follow the tutorial exactly, get it to work as it is, and then try to change a bit about it (such as adding semihosting).Another thing: the “VNC server running” message means that QEMU is not opening a graphical window. Ubuntu 10.04 does not offer QEMU 1.1.2 (http://packages.ubuntu.com/lucid/qemu-kvm-extras), you could get 1.2.1 by Debian testing or by recompiling it yourself, and if you recompiled it then you need to add SDL support to show a graphical window. Since you intend to use it to emulate micro-controllers, then maybe you don’t need graphical windows, but be aware of that.