ARM64 on Docker

One of the easiest ways to get started using ARM64 (also known as AARCH64) is with Docker or Virtual Box. While setting up a Raspberry Pi with an AArch64 OS is doable (particularly with OpenSUSE), it can be much faster to just emulate a linux box or image on an x86 based machine. This post assumes you already have docker installed and running.

Docker

First, start a Debian command line:

docker run -it debian /bin/bash

It’s as simple as that.

Hello AArch64

There are two main dependencies for working with AArch64: qemu and aarch64-linux-gnu-gcc. The first emulates execution of AArch64 binaries and the latter is a cross compiler to AArch64.

The following commands compiles and prints the classic “hello, world\n” message: (Note, replace vim with your favorite editor i.e. emacs)

apt-get update
apt-get install -y qemu-user-static gcc-aarch64-linux-gnu vim
vim hello.c
aarch64-linux-gnu-gcc -static hello.c -o hello
qemu-aarch64-static hello

Where hello.c is:

#include <stdio.h>

int main(void) {
    printf("hello, world\n");
    return 0;
}

In Debian, running qemu-aarch64-static hello is the same as running ./hello for an AArch64 binary hello. This does not necessarily happpen with other operating systems.

Examine the Binary

To inspect the binary, it will be easier to look at the non-statically compiled version, so re-run gcc:

aarch64-linux-gnu-gcc hello.c -o hello
aarch64-linux-gnu-objdump -d hello > hello.dmp
vim hello.dmp

And inspect the <main>: address to see the disassembly:

0000000000000828 <main>:
 828:	a9bf7bfd 	stp	x29, x30, [sp, #-16]!
 82c:	910003fd 	mov	x29, sp
 830:	90000000 	adrp	x0, 0 <_init-0x658>
 834:	91238000 	add	x0, x0, #0x8e0
 838:	97ffffa6 	bl	6d0 <puts@plt>
 83c:	52800000 	mov	w0, #0x0                   	// #0
 840:	a8c17bfd 	ldp	x29, x30, [sp], #16
 844:	d65f03c0 	ret

Another way of looking at the pre-linked assembly code is of course to just ask GCC for it: aarch64-linux-gcc hello.c -S and then open hello.s:

	.arch armv8-a
	.file	"hello.c"
	.section	.rodata
	.align	3
.LC0:
	.string	"hello, world"
	.text
	.align	2
	.global	main
	.type	main, %function
main:
	stp	x29, x30, [sp, -16]!
	add	x29, sp, 0
	adrp	x0, .LC0
	add	x0, x0, :lo12:.LC0
	bl	puts
	mov	w0, 0
	ldp	x29, x30, [sp], 16
	ret
	.size	main, .-main
	.ident	"GCC: (Debian 6.3.0-18) 6.3.0 20170516"
	.section	.note.GNU-stack,"",@progbits

Examining the differences between these files can be interesting, for example the add instruction below main: was changed to a mov instruction in the actual binary.

Compile Dynamically-Linked Binary

Why is the -static flag needed for cross-compiling the AArch64 binaries? Without it, you will see something like this message:

root@30941edc6d88:~# ./hello
/lib/ld-linux-aarch64.so.1: No such file or directory

See “Qemu AArch64 User-Space Emulation” on just the topic.