The Hyperpessimist

The grandest failure.

First Steps in ARM Assembly

As I believe the Raspberry Pi is quite a nice device for developing (basically a programming console, like game consoles) I thought it might be a nice idea to do some low-level programming on that thing. It is after all a rather slow deviceā€¦

There is some tutorial specifically for the Raspberry Pi already, but this is very basic and the code that he writes is rather complicated with complex instructions like stmfd and ldmfd. I went a slightly easier route with this tutorial.

The ARM architecture is kind-of a strange beast, because it can be low-level programmed in various ways. The first chips used 32 Bit instructions that are called “ARM” today. Some time later they added a 16 Bit instruction set called “Thumb”. Some years later, “Thumb2” came along, as a revised version of the original Thumb instruction set. The ARM1176 that is built into the Raspberry Pi supports ARM and Thumb instruction sets (some reviews state also Thumb2 which is not true – ARM1156 supports Thumb2 but that does not mean that ARM1176 does too). In the Raspberry Pi world, nobody seems to use Thumb1, therefore I’ll focus on the classical ARM instructions.

I’m assuming that you do have some experience with programming, so you more or less know how a C program looks like. In my experience it is usually quite easy to take a C program and go from there to the assembly program.

Without further ado, let’s start with one of the easiest programs ever:

So, if we take this and translate it roughly into ARM assembler:

To get an executable file, it is easiest to just run gcc on it:

1
2
$ gcc hello.c -o hello-c
$ gcc hello.S -o hello-asm

Run them and you’ll see they work just file and display Hello World just as expected. Let’s look at the binaries a bit closer to see what we actually did:

1
2
3
4
5
6
7
$ file hello-asm
hello-asm: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, not stripped
$ ldd hello-asm
        /usr/lib/arm-linux-gnueabihf/libcofi_rpi.so (0x401e5000)
        libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x401ee000)
        /lib/ld-linux-armhf.so.3 (0x4004c000)
$ objdump -d hello-asm

The file command tells us that we created an ARM executable file with 32 bit that uses shared libs. Which shared libraries? ldd tells us that it is libc, because of printf(). In case you wonder what libcofi_rpi is – an optimized implementation of memcpy()/memset() for the Raspberry Pi CPU. You don’t need to worry about this one.

The last command disassembles the binary again, so you can check what GCC did exactly. If you check the disassembly of the main label, you’ll see the push and pop instructions in the disassembly, and in the message label you can find the “Hello World!\n” string which is rather interesting if you’re new to ARM:

1
2
3
4
5
6
000083bc <message>:
    83bc:       6c6c6548        .word   0x6c6c6548
    83c0:       6f77206f        .word   0x6f77206f
    83c4:       21646c72        .word   0x21646c72
    83c8:       0000000a        .word   0x0000000a
    83cc:       000083bc        .word   0x000083bc

The first column is the address, the second column is the value at that address and the other colums are the decoded values. In the case of our string the address 83bc (as it is hexadecimal 0x83bc means the same thing) contains the value 6c6c6548 (again, in hex). If you know ASCII, you’ll notice that the ASCII representation of ‘H’ is the number 72 or in hex 0x48. If we continue, ‘e’ is 0x65 and ‘l’ is 0x6c. So the address 0x83bc contains the string “lloH”. Now, why is it reverse? Because the ARM architecture used in the Raspberry Pi is little-endian, which means that the representation is always reversed. By the way, this is also valid for x86 machines, they also use a little-endian representation. For more information, Wikipedia delivers.

And that concludes our short trip into ARM assembly. Stay tuned in case I’ll write some more about it.