IceTea
IceTea

Reputation: 68

Compile ARM binaries, run them in ARMulator

Here is my problem:

Introduction

I am currently trying to execute some basic code on an ARM processor. As I currently (and probably for a long time) do not have any ARM hardware around me, I’ve been using QEMU (an ARM emulator) for some days now, which, I’ve got to say it, works like a charm. But using QEMU, I feel like drawing my sword to kill a fly. So I looked for some lighter emulators, and found out about ARMulator.

“The ARMulator is a family of programs which emulate the instruction sets of various ARM processors and their supporting architectures. The ARMulator is transparently connected to compatible ARM debuggers to provide a hardware-independent ARM software development environment. Communication takes place via the Remote Debug Interface (RDI).”
(Source: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0032f/index.html)
“Accuracy is good, although it is classed as cycle count accurate rather than cycle accurate, this is because the ARM pipeline isn't fully modeled (although register interlocks are). Resolution is to an instruction, as a consequence when single stepping the register interlocks are ignored and different cycle counts are returned than if the program had simply run, this was unavoidable.”
(Source: https://en.wikipedia.org/wiki/ARMulator)

Environment

From there, I’ve tried several ARMulator versions. Got to say there are not a lot around, in fact I ‘ve only tried 3 versions (names are not official, thats simply a name I gave them to recognize them):
- XYZ Armulator : https://github.com/x-y-z/armulator 2016-02-20
- SourceForge Armulator : https://sourceforge.net/projects/armulator/ 2013-04-26
- Phanrahan Armulator : https://github.com/phanrahan/armulator 2014-11-11

I am not sure these versions are official, and I think I’ve seen several times on different websites there was some versions really official, probably proprietary and including more than the tools needed. Here’s some examples of what I am talking about :
- On ARM Connected Community, they talk about a RealViewDevelopmentSuite, which seems to contain ARMulator : https://community.arm.com/message/12272#12272
- …Will add others when I find one of them again But those solutions are not free.

Now about the Toolchain. Two different Toolchains are stated in the resources I’ve found :
- Arm-elf-abi : stated on the XYZ ARMulator GitHub, it is recommended to use this command ($ arm-elf-gcc -mthumb -Bstatic -o ) to compile the binary executable.
The only version I found was for Windows… sadly, I could not find any for Unix.
- Arm-none-eabi : stated on this tutorial : http://www.bravegnu.org/gnu-eprog/hello-arm.html, this is the one I’ve been using with QEMU. I’ve read somewhere the Arm-elf toolchain was not required when compiling ARM assembly, and Arm-none was enough for this case.

The two programs I tested were made to be the most simple possible :

One in Assembly : helloarm.s

  .global _start
 .text  
entry:  b _start  
  .align  
_start: 
  mov   r4, #5         @ Load register r4 with the value 5
  mov   r3, #4         @ Load register r3 with the value 4
  add   r0, r4, r3     @ Add r3 and r4 and store in r0
  b stop
stop:  b stop @  Infinite loop

One in C : test.c

int c_entry() {
  return 0;
}

Compilation process

At first, I used the same way which worked on QEMU explained in this tutorial : http://www.bravegnu.org/gnu-eprog/hello-arm.html . On QEMU, everything worked fine, but the emulation process is slightly different. I had to load the binary into the RAM first before executing QEMU. The way to launch ARMulator ($ armulator ) being different, I suppose the binary is automatically loaded is the RAM.

I tried three different ways to compile, not sure about which is the most appropriate one.Here it is :

Assembly :

 $ arm-none-eabi-as –s -g helloarm.s -o helloarm.o    
 $ arm-none-eabi-ld -Ttext=0x0 -o helloarm.elf helloarm.o    
 $ arm-none-eabi-objcopy -O binary helloarm.elf helloarm.bin      

We should now have two ‘binaries’, the .bin one and the .elf one.
I still don’t really know what is the difference. Need to read some more.

C :

 $ arm-none-eabi-gcc -mthumb -Bstatic --specs=nosys.specs srcs/main.c –o a.out  

An extra one :
I’ve also tried the following method explained in this tutorial, which made me think Armulator was made to execute .elf binaries. Files made on this method are called c_entry.
https://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/ Issues are the same.

From then, we have 6 binaries :

Issues

When using SourceForge and Phanrahan Armulator with any binary file (elf or bin):

$ ./armulator asm-helloarm.bin
Error opening file 00000000.bin
$ ./armulator a.out
Error opening file 00000000.bin
$ ./armulator helloarm.elf
Error opening file 00000000.bin

When using XYZ Armulator:

$ armulator helloarm.bin
Segmentation fault (core dumped)

$ armulator a.out
Unexpect Instr:

Possible causes
- ARMulator does not know how to decode some instructions. This is probably the case, but my program seems too much basic to be the case… It does nothing, and returns 0…
- I am using a bad toolchain, or using the correct toolchain badly.
- Armulator should not be used this way.

NOTES
When running binaries with arm-none-eabi-gdb, I can’t run or start the program. Only this command works : target , but it only resets the target file to the binary already chosen.
When I type start, it says “No symbol table loaded. Use the "file" command.”

Thanks in advance for your help, or at least thanks for reading,
Hoping I am not the only one who had a hard time with Armulator.

Regards,
John

Upvotes: 4

Views: 1811

Answers (1)

old_timer
old_timer

Reputation: 71556

I think the point is your comment about not having an ARM. A high percentage of things that have some sort of on/off switch. Have an ARM. If you have an x86 computer to read this web page, that computer probably has several ARMS as well as some number of other processors in it.

Anyway. Thanks for pointing out those links, very cool. Looking at the last one the Phanrahan Armulator source, we can see some special addresses in the PutWord function. I didnt read your whole question TL;DR, so just going to jump in a simple working example.

hello.s:

.globl _start
_start:
    b reset
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang

hang: b hang

reset:
    mov r0,#0x16000000
    mov r1,#0x55
    str r1,[r0]
    add r1,r1,#1
    str r1,[r0]
    mov r0,#0xF0000000
    str r1,[r0]
    b hang

memmap:

MEMORY
{
    ram : ORIGIN = 0x00000000, LENGTH = 0x1000
}
SECTIONS
{
    .text : { *(.text*) } > ram
}

Makefile:

#ARMGNU ?= arm-none-linux-gnueabi
ARMGNU ?= arm-none-eabi
#ARMGNU ?= arm-linux-gnueabi
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding 


all : hello.bin

clean :
    rm -f *.o
    rm -f *.bin
    rm -f *.elf
    rm -f *.list
    rm -f *.srec
    rm -f *.hex

hello.o : hello.s
    $(ARMGNU)-as hello.s -o hello.o


hello.bin : hello.o memmap
    $(ARMGNU)-ld -T memmap hello.o -o hello.elf
    $(ARMGNU)-objdump -D hello.elf > hello.list
    $(ARMGNU)-objcopy hello.elf -O ihex hello.hex
    $(ARMGNU)-objcopy hello.elf -O srec hello.srec 
    $(ARMGNU)-objcopy hello.elf -O binary hello.bin 

Once built the copy hello.bin to 00000000.bin just as the error message implies.

then

./armulator

r0 = 16000000
r1 = 00000055
Ur1 = 00000056
Vr0 = f0000000

ERROR PutWord(0xF0000000,0x56)
NumScycles 8
NumNcycles 7
NumIcycles 0
NumCcycles 0
NumFcycles 0
NumInstrs  8
TotlCycles 15

we see the U and V characters come out (0x55 and 0x56) plus the other special addresses reacting.

hello.list

00000000 <_start>:
   0:   ea000007    b   24 <reset>
   4:   ea000005    b   20 <hang>
   8:   ea000004    b   20 <hang>
   c:   ea000003    b   20 <hang>
  10:   ea000002    b   20 <hang>
  14:   ea000001    b   20 <hang>
  18:   ea000000    b   20 <hang>
  1c:   eaffffff    b   20 <hang>

00000020 <hang>:
  20:   eafffffe    b   20 <hang>

00000024 <reset>:
  24:   e3a00416    mov r0, #369098752  ; 0x16000000
  28:   e3a01055    mov r1, #85 ; 0x55
  2c:   e5801000    str r1, [r0]
  30:   e2811001    add r1, r1, #1
  34:   e5801000    str r1, [r0]
  38:   e3a0020f    mov r0, #-268435456 ; 0xf0000000
  3c:   e5801000    str r1, [r0]
  40:   eafffff6    b   20 <hang>

as you may have read so far for a full sized the reset handler has to be the first INSTRUCTION at address zero. And it is, ideally either a branch or a load pc since you only have one instruction to get out/over the exception table.

bin, hex, elf, etc. Think of gif, jpg, png, etc. Various different file formats that have both pixel data as well as other information like how many pixels wide and tall the image is. Maybe some other encodings or compressions or whatever. You can examine the .hex and .srec with a text editor they are popular ascii file formats. Each of these is just a different way to store the instructions and data for our program. For various reasons there are various formats just like image files have their reasons why someone decided to make a new one. the ".bin" format in this case which is not what all files with that extension mean, but when you use -O binary with objcopy you get a raw memory image

hexdump -C hello.bin 
00000000  07 00 00 ea 05 00 00 ea  04 00 00 ea 03 00 00 ea  |................|
00000010  02 00 00 ea 01 00 00 ea  00 00 00 ea ff ff ff ea  |................|
00000020  fe ff ff ea 16 04 a0 e3  55 10 a0 e3 00 10 80 e5  |........U.......|
00000030  01 10 81 e2 00 10 80 e5  0f 02 a0 e3 00 10 80 e5  |................|
00000040  f6 ff ff ea                                       |....|
00000044

and comparing that to the listing above you see this is just raw instruction/program data. Which apparently is what this emulator wants.

If you want to extend that into C programs you at a minimum need to setup a stack (mov sp,#0x4000 for example) and then branch link to the name of the entry point, does not have to be main() and sometimes you dont want main() as some compilers add on extra junk. then in my case after the bl to notmain I b to hang to handle a return if there is one.

You want to use all those COPS flags in my makefile.

They way I write my baremetal I can use any of the variations of gcc compilers arm-none-eabi, arm-none-linux-gnueabi, and so on. The differences are supposed to have to do with the C library included or supported. The normal glibc vs newlib in these cases. I dont call C library functions so I dont have a problem there, I just need a raw compiler to get from C to object. The linker script can be as simple as I have shown you could add .data and .bss as needed. They can get a lot more complicated if you feel the need. If you dont call out objects in the linker script then the linker uses the objects in command line order so your entry object (the one with the exception table at the beginning), has to be first in the list of objects.

Upvotes: 1

Related Questions