Arm - Geek went Freak!

Arm

ARM architectures, cores and families

ARM is an age old beast with multiple architectures, cores and families. It could easily get confusing to deal with the relationship between them.

Relationship between ARM architecture and core

Architecture Core
ARMv5 ARM7, ARM9, ARM10
ARMv6 ARM11
ARMv6-M Cortex-M0, Cortex-M1
ARMv7-A Cortex-A
ARMv7-R Cortex-R
ARMv7-M Cortex-M3
ARMv7E-M Cortex-M4, Cortex-M7
ARMv8-A Cortex-A53, Cortex-A57, Cortex-A72

Relationship between ARM architecture and core for Cortex-M family.

Core Architecture
Cortex-M0 ARMv6-M
Cortex-M1 ARMv6-M
Cortex-M0+ ARMv6-M
Cortex-M3 ARMv7-M
Cortex-M4 ARMv7E-M
Cortex-M7 ARMv7E-M

References

  1. List of ARM microarchitectures

LDR - an arm pseudo instruction to load immediate values

We have two dedicated instructions MOV and MVN to move data between registers. But their ability to load immediate values is very limited. They support an 8-bit immediate value, giving only a range of 0-255. These 8 bits can then be rotated right through an even number of positions(ie RORs by 0, 2, 4,..30). Though it gives a much larger range, It misses out large number of constants too. There is no single instruction which will load a 32 bit immediate constant into a register without performing a data load from memory. All ARM instructions are 32-bit long and ARM doesn’t use instruction stream as data. The reason which lead to the creation of LDR instruction. This is a pseudo instruction and is converted into a proper ARM instruction when assembled. The syntax of LDR instruction is,

LDR rd, = [ numeric constant | label ]

This is what the assembler does when it finds LDR instruction,

  • If the constant can be constructed using either a MOV or MVN then this will be the instruction actually generated.
  • Otherwise, the assembler will produce an LDR instruction with a PC-relative address to read the constant from the nearest literal pool.
Thus, these instructions

LDR r1, =5
LDR r2, =-2
LDR r3, =256703
LDR r4, =49152

are converted into

MOV r1, #5
MVN r2, #1
LDR r3, [pc, #8] ; here 8 is the pc relative offset to literal pool where 256703 is stored
MOV r4, #3, 18

cross compiling for ARM - the LLVM way

I have never used a compiler other than gcc before to cross-compile c code for ARM. This is the first time I am straying outside gcc. All these days I have always heard all kinds of promising reviews on LLVM. So Let us try it out now. If you are using ubuntu, you can install llvm using apt,

apt-get install llvm clang

If you are using any other distribution, follow these instructions to compile llvm from source.

Its time to get our hands dirty. Here is the simple code we are going to compile,

#include
int main()
{
  printf("Hello World!");
  return 0;
}

The compiling process is like this, 1. compile c code into llvm assembly using clang 2. convert llvm assembly into ARM assembly using llc 3. convert ARM assembly code into binary/ELF using binutils

clang -emit-llvm hello.c -c -o hello.bc

-emit-llvm option instructs clang to generate llvm assembly. The generated llvm assembly, hello.bc can be executed using lli.

lli hello.bc

Let us convert this llvm assembly into ARM assembly,

llc -march=arm hello.bc -o hello.s

Now, we can binutils(as + ld) to assemble and link these generated ARM assembly files.

arm-elf-gcc hello.s -o hello.elf

You can use gdb to simulate the generated elf.

Update: Here is the single command which compiles, assembles and links the given c code,

clang -march=armv7-a -ccc-host-triple arm-elf -ccc-gcc-name arm-elf-gcc hello.c