Reputation: 68
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 :
helloarm.elf
main.elf
a.out
c_entry.bin
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:
With helloarm.elf binary, or any .elf file :
$ armulator helloarm.elf
Error:Out of Code Segment:0x24
* Error in `armulator': double free or corruption (top): 0x0000000001000550 *
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7f0f32cf4725]
/lib/x86_64-linux-gnu/libc.so.6(+0x7ff4a)[0x7f0f32cfcf4a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f0f32d00abc]
armulator[0x40489d]
armulator[0x4022d2]
armulator[0x401f3a]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f0f32c9d830]
armulator[0x402109]
======= Memory map: ========
00400000-0040e000 r-xp 00000000 08:01 3802861 /usr/local/bin/armulator
0060d000-0060e000 r--p 0000d000 08:01 3802861 /usr/local/bin/armulator
0060e000-0060f000 rw-p 0000e000 08:01 3802861 /usr/local/bin/armulator
00fe4000-01016000 rw-p 00000000 00:00 0 [heap]
7f0f2c000000-7f0f2c021000 rw-p 00000000 00:00 0
7f0f2c021000-7f0f30000000 ---p 00000000 00:00 0
7f0f32974000-7f0f32a7c000 r-xp 00000000 08:01 3281469 /lib/x86_64-linux-gnu/libm-2.23.so
7f0f32a7c000-7f0f32c7b000 ---p 00108000 08:01 3281469 /lib/x86_64-linux-gnu/libm-2.23.so
7f0f32c7b000-7f0f32c7c000 r--p 00107000 08:01 3281469 /lib/x86_64-linux-gnu/libm-2.23.so
7f0f32c7c000-7f0f32c7d000 rw-p 00108000 08:01 3281469 /lib/x86_64-linux-gnu/libm-2.23.so
7f0f32c7d000-7f0f32e3d000 r-xp 00000000 08:01 3281399 /lib/x86_64-linux-gnu/libc-2.23.so
7f0f32e3d000-7f0f3303c000 ---p 001c0000 08:01 3281399 /lib/x86_64-linux-gnu/libc-2.23.so
7f0f3303c000-7f0f33040000 r--p 001bf000 08:01 3281399 /lib/x86_64-linux-gnu/libc-2.23.so
7f0f33040000-7f0f33042000 rw-p 001c3000 08:01 3281399 /lib/x86_64-linux-gnu/libc-2.23.so
7f0f33042000-7f0f33046000 rw-p 00000000 00:00 0
7f0f33046000-7f0f3305c000 r-xp 00000000 08:01 3281437 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f0f3305c000-7f0f3325b000 ---p 00016000 08:01 3281437 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f0f3325b000-7f0f3325c000 rw-p 00015000 08:01 3281437 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f0f3325c000-7f0f333ce000 r-xp 00000000 08:01 3672061 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f0f333ce000-7f0f335ce000 ---p 00172000 08:01 3672061 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f0f335ce000-7f0f335d8000 r--p 00172000 08:01 3672061 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f0f335d8000-7f0f335da000 rw-p 0017c000 08:01 3672061 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f0f335da000-7f0f335de000 rw-p 00000000 00:00 0
7f0f335de000-7f0f33604000 r-xp 00000000 08:01 3281371 /lib/x86_64-linux-gnu/ld-2.23.so
7f0f337e1000-7f0f337e6000 rw-p 00000000 00:00 0
7f0f33800000-7f0f33803000 rw-p 00000000 00:00 0
7f0f33803000-7f0f33804000 r--p 00025000 08:01 3281371 /lib/x86_64-linux-gnu/ld-2.23.so
7f0f33804000-7f0f33805000 rw-p 00026000 08:01 3281371 /lib/x86_64-linux-gnu/ld-2.23.so
7f0f33805000-7f0f33806000 rw-p 00000000 00:00 0
7ffc24c19000-7ffc24c3a000 rw-p 00000000 00:00 0 [stack]
7ffc24ca4000-7ffc24ca6000 r--p 00000000 00:00 0 [vvar]
7ffc24ca6000-7ffc24ca8000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
Aborted (core dumped)
With helloarm.bin binary:
$ 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
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