C code & Arm assembly: emulating execution using QEMU

Nikos Mouzakitis
5 min readDec 17, 2018

‘An Arm programming example combined with C programming in an embedded device’

In this article , we are attempting to write and execute code for {Versatilepb/arm926ej-s } in a native Ubuntu OS system, by cross-compiling the code and emulating the execution using the QuickEmulator(QEMU)

Code can be found in my Github Arm_Generic repository, here: https://github.com/NikosMouzakitis/Arm_generic/tree/master/10

A short intoduction of the tools used / required to recreate the complete functionality shown.

Requirments:

  1. qemu
  2. arm-none-eabi
  3. gdb-multiarch

A quick description of what the program should do, it that, after entering the startup code(startup.s), execution switch to a C code file( test.c ) and at some point it is going to enter in an assembly written function that it is implementing positive integer division( division.s ). Soon after it returns an implemented function in C code ( print_uart0() ) will be called to actually print out the result of the positive division in the terminal emulated by qemu.

Makefile

Above we are inspecting the Makefile, that will run the program.

In the first two commands, we do use the portable GNU assembler to generate the object files and as well we are declaring the cpu that will run it. Afterwards we do compile the test.c code for the same cpu. Linking occurs in the next command and copy and translation of object files in the 7th line. Finally qemu is called for the exact platform with features for memory and the binary.

Next we will have a look into the test.ld file used for linking.

test.ld

We can see that our sections in the device’s memory start at the address 0x10000, followed by the .startup(of startup.s) after is the(division.s) then .text segment, .data and .bss segment.We do provide a 4 kb of stack memory and set the stack_top at 0x2000.

Next we will have a look in the startup.s file

startup.s

We can see the declaration of the global _Reset that was also mentioned in the linker file above, and quick after executing stack_top address is loaded into the sp (stack_pointer_register) and we branch by linking (bl) in the c_entry function.(defined in the test.c C code file.)

The test.c C code file is displayed now( 2 pictures)

test.c

We see that after entering c_entry function, soon enough we will call division(), that is declared as extern int division(int, int) and the result will be converted( we are not going into details about how convert() works-it is obvious by the provided code). Instead we will talk about the next function called the print_uart0() before going into describing the division function.

After having a look in the manual of the board that we are running the code on, we need to find the exact address that refers to the Uart0(to print something in the terminal in qemu, so we can see the output we generate) we find out that the peripheral base address for the UART zero is 0x1F1000. And this is assignment we do on the UART0DR variable in our C code.

Picture taken from the reference manual : http://infocenter.arm.com/help/topic/com.arm.doc.ddi0287b/DDI0287B_arm926ejs_dev_chip_technical_reference_manual.pdf

In the function print_uart0() we are providing a char *, and till we reach a ‘\0’ character we are writting in the base address defined the characters one by one resulting the output in the Qemu terminal.

Now in the context of division() function, we have the division.s file where the function it is implemented in ARM assembly. The two arguments are passed into the register r0 and r1, while also the return value we intend to send back in the C code got to be before we branch back in the r0. The code follows :

division.s

Declaration of the global division and the most important things here are the cmp r5,r0 where if the integer in r5 is less than 0 we will branch to exit and follow the lr(link register ) to return back in the address of PC+4 before we entered the division() function back in the test.c .

The return value is acquired since is saved in the r0 register.

Demonstrating now, after we call make, in a new terminal we do run the command ‘ gdb-multiarch -quiet -x /usr/bin/com_file’ where in my case com_file consists of the commands :

/usr/bin/com_file

layout next
layout next
layout next
layout next
target remote localhost:1234
file test.elf
b c_entry
continue

where in the first four commands we just change the layout, afterwards we are connecting remotely, setting a breakpoint in the defined c_entry and all we need to do is press ‘s’ and ‘enter’ to do step by step debbuging.

demonstration dividing 18/15, resulting printing 1 in the output as seen.

We can see after dividing 18 by 15 the output of the generated integer division is 1.

--

--

Nikos Mouzakitis

Graduate of Mathematics Department in University of Aegean, Currently Computer Engineer undergrad.