Cross-compiling ARM on Travis using clang and qemu

Wolf Vollprecht
3 min readApr 14, 2018

--

We’ve recently added ARM NEON instructions to our xsimd package — which in itself is already an exciting change! However, as there is not much material online how to cross-compile and unit-test on e.g. the popular CI service Travis, we’ve decided to do a short write-up of this.

ARM NEON instructions are SIMD (single-instruction-multiple-data) CPU specific instructions that can be used to speed up code by performing the same operation on more than one data “element” at once. For example, with NEON one can multiply four float numbers at the same time (vs. with a regular instruction one can only multiply one float at a time).

One challenge is that, as mentioned, these instructions are CPU specific, and version specific. For ARM NEON there exist 3 relevant versions: ARM v7 and ARM v8. ARM v8 comes in a 32- and a 64bit flavor. These 3 variants differ in the instructions and datatypes they support (for example, ARMv7 does not have support for the double data-type etc.). But testing all the different variants requires hardware and time.

Luckily, we can easily cross-compile on Linux! Both, clang and gcc support a cross-compilation workflow, however, in this example we’ll use the clang one as it is somewhat easier (clang can “natively” cross-compile, where for gcc you need to install a target platform specific compiler).

To select an ARM target on clang, one has to specify the following command line flags:

clang++ -march=armv8-a -target aarch64-linux-gnueabi ...

This will compile with the armv8-a architecture, and use the 64bit architecture. Note, that in order to compile this, you need to have the STL for this architecture installed. On ubuntu (which is used on Travis) you can use the following

g++-4.8-aarch64-linux-gnu
gcc-4.8-aarch64-linux-gnu
g++-4.8-multilib
gcc-4.8-multilib

To install the necessary headers. You will also need to specify the correct include directories to find the appropriate ARM STL headers. The 4.8.4 in the following include directories refer to the GCC version you’ve installed for cross-compilation — so be careful to change it if you have another version installed.

/usr/aarch64-linux-gnu/include/c++/4.8.4/aarch64-linux-gnu/
/usr/aarch64-linux-gnu/include/c++/4.8.4/
/usr/aarch64-linux-gnu/include/

Note that if you test with e.g. google-test, you will want to download and build that library as well — as you can’t link with an x86 library.

Also, if using CMAKE, one needs to set the following to not link with any system libraries.

set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
set(CMAKE_THREAD_LIBS_INIT)

Regarding the linker, there is also a bug in clang on the version of ubuntu we were using, which prevents clang from automatically using the correct linker. To correct this, we add a prefix path to the compilers search path, and create a symbolic link to the correct linker

mkdir $HOME/linker_bin
ln -s /usr/bin/aarch64-linux-gnu-ld $HOME/linker_bin/ld
clang++ ... --prefix=$HOME/linker_bin/

Now, when you have this configuration nailed down, you can go and compile an executable for ARM! The last step is to execute your executable in an emulator. The free qemu is excellent:

qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./your_executable

--

--

Wolf Vollprecht
Wolf Vollprecht

Written by Wolf Vollprecht

I work as a scientific and robotics software developer for QuantStack in Paris and Berlin. We do Open Source for a living!

No responses yet