Cross compiling for ARM with Ubuntu 16.04 LTS

The goal of cross compiling is to compile for one architecture on machine running another one. In this post, I’ll try to explain the steps required to be able to compile software that is executable on ARM-based hardware using a “normal” x64-based PC or virtual machine. ARM-based devices are usually limited in processing power and are mostly running stripped-down, embedded versions of Linux. This makes it sometimes difficult to compile on the target device directly.

In the meanwhile, I updated the article for the most recent version of Debian and Ububtu and I have included info regarding aarch64 and static linking. You can find this article over here: https://jensd.be/1126/linux/cross-compiling-for-arm-or-aarch64-on-debian-or-ubuntu

If you are interested, I also created a YouTube video from that new blogpost. If you prefer classic text, you can just follow the rest of this article:

For this post, I was using Ubuntu 16.04 LTS (Xenial). I’m starting with a minimal install (Standard System Utilities + SSH server) as a starting point. This makes sure that the next steps should function on about any installation without missing dependencies or installation steps.

Terminology

In cross compiling, the following terminology is used:

  • Build platform: Architecture of the build machine
  • Host platform: The architecture you are building for
  • Target platform: The architecture that will run the binary output of the compilation

If the above three platforms are the same, which in mostly the case, then it’s called a “native” compilation. If build and host platform are the same, then we’re talking about a “cross” compilation, which this post is covering. When all three platforms are different, it’s called a “canadian”. This is used to build a cross compiler for another architecture.

Just to be clear, in this post, the build and host platform are x86_64 (standard PC) and the target is ARM.

Prerequisites

Before we can start compiling, we need to install the necessary packages and tools for cross compiling for ARM. These include the standard tools needed for compiling:

jensd@ubu:~$ sudo apt-get install gcc make gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi
Reading package lists... Done
...
Do you want to continue? [Y/n] Y
...
jensd@ubu:~$

Compiling a simple C program from source

Once we have installed the prerequisites, we can try and compile a simple C program. Let’s first do a so-called native compile for the PC we’re compiling from just to make sure that our program does what we want.

Save the source as helloworld.c:

#include<stdio.h>
int main()
{
        printf("Hello World!\n");
        return 0;
}

Compile the source “native” and write the binary as helloworld-x86_64

jensd@ubu:~$ vi helloworld.c
jensd@ubu:~$ gcc helloworld.c -o helloworld-x86_64

To see what type and for which platform the result of our compilation is, we can check the output with file:

jensd@ubu:~$ file helloworld-x86_64
helloworld-x86_64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9c154e6cd317de62941101211e3a26f8ea548a81, not stripped

We can execute the binary to check the result:

jensd@ubu:~$ ./helloworld-x86_64
Hello World!
jensd@ubu:~$

The next step is to compile the same source for ARM. We simply do this by using a different compiler (arm-linux-gnueabi-gcc instead of gcc). When compiling for a platform like ARM, it’s usually also a good thing to create a statically linked binary. This means that the binary will not depend on dynamically linked libraries (which can be missing/broken dependencies).

jensd@ubu:~$ arm-linux-gnueabi-gcc helloworld.c -o helloworld-arm -static
jensd@ubu:~$ file helloworld-arm
helloworld-arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=1aec53a65fdd71ec68e761b5ef4cd2fae6e4e75c, not stripped

Now we can test the execution of our binary on an ARM-based machine:

[root@embed ~]# uname -a
Linux embed 3.10.70 #1 SMP Fri Aug 5 00:17:41 PDT 2016 armv7l GNU/Linux
[root@embed ~]# chmod +x helloworld-arm
[root@embed ~]# ./helloworld-arm
Hello World!

As you see in the above output, our small program works fine on ARM after cross compiling it!

Cross compiling with configure and make

The above example was pretty simple but when compiling source from larger projects, it’s usually done by generating a makefile with configure and then running the compile and other necessary steps with make. To replace gcc with another, target platform specific compiler would be a lot of work. Fortunately most of the times you can just specify the platform which you are compiling for when running configure.

As an example, I’ll create a binary for ARM of strace. To avoid getting into problems with dependencies on my embedded ARM device, I’ll compile static as well.

First step is to get the source of strace from: https://sourceforge.net/projects/strace/files/strace/4.14/ and extract it:

jensd@ubu:~$ sudo apt-get install xz-utils
...
jensd@ubu:~$ tar -xf strace-4.14.tar.xz
jensd@ubu:~$ cd strace-4.14/
jensd@ubu:~/strace-4.14$

The next step is to run configure. But here we need to specify that we want to end up with a binary (statically linked) for ARM:

jensd@ubu:~/strace-4.14$ ./configure --build x86_64-pc-linux-gnu --host arm-linux-gnueabi LDFLAGS="-static"
...
config.status: creating config.h
config.status: executing depfiles commands

At this point we’re ready to do the actual cross compile by running make:

jensd@ubu:~/strace-4.14$ make
...
jensd@ubu:~/strace-4.14$ file strace
strace: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=af6112e085e542a1376e77c2091f12ea9c86519a, not stripped

As you can see in the output of the last command, the result is a statically linked binary for arm.

As with the small C-program, it’s time to test the compiled binary on ARM:

[root@embed ~]# uname -a
Linux embed 3.10.70 #1 SMP Fri Aug 5 00:17:41 PDT 2016 armv7l GNU/Linux
[root@embed ~]# chmod +x strace
[root@embed ~]# ./strace
./strace: must have PROG [ARGS] or -p PID
Try './strace -h' for more information.

In most cases compiling for ARM (or another cross-platform) is not very difficult but it sometimes requires a little extra effort to find the correct options and flags for the configure script.

14 thoughts on “Cross compiling for ARM with Ubuntu 16.04 LTS

  1. thanks very helpful post ,short straight forward yet concise ..it worked even though i thought it might glitch cause the arm-linux-gnueabihf variant , next i’m gonna try to cross compile JTR which will require dynamic links i guess .I have a pi cluster with mpi all set up i’d like to run some benchmarks with paswrd cracker using 20nodes:p I had to find just the right version to get it compile for x64 finally i did find one on github, the one on openwall kept failing due to some MD5 linkage..
    thanks again ; )

  2. I’ve always wondered, let us say you are cross-compiling for Raspberry Pi on Ubuntu, and you need libraries and headers typically retrieved by ‘sudo apt-get’. How do I ‘sudo-apt-get’ an ARM version to put into the toolchain?

  3. Jens, your article is awesome and helped me to finally got cross compilation working; even I used a platform-specific compiler (for my Synology NAS on ARM) – but as you said, it was a whole lot of work!

    So here’s my question:
    “Fortunately most of the times you can just specify the platform”
    Could you go into a bit more detail, what considerations are there to take, what advantages or necessities are there which could justify the usage of a platform specific compiler?

  4. Awesome post! Thanks.
    FYI, I used this on Fedora 28 along with Linaro’s x86-to-ARM toolchain (gcc 7.2) and it worked!

    $ uname -a
    Linux 4.16.13-300.fc28.x86_64 #1 SMP Wed May 30 14:31:00 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
    $ arm-linux-gnueabihf-gcc –version
    arm-linux-gnueabihf-gcc (Linaro GCC 7.2-2017.11) 7.2.1 20171011
    Copyright (C) 2017 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions. There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    $

  5. Great help to my efforts, thank you!
    One remark that can give some help – I needed to deploy a cross-compiled .so module with little dependencies but C runtime. I got along with gcc switch -static-libstdc++ and created shared library which linked C/C++ runtime statically

  6. sh ./strace
    ./strace: line 1: syntax error: word unexpected (expecting “)”)
    when i run ./strace command in arm processor it give me the above error .

  7. cvar1984@backbox:~/tools$ arm-linux-gnueabi-gcc lite.c -o lite -lcurl
    /usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/bin/ld: cannot find -lcurl
    collect2: error: ld returned 1 exit status

  8. “Just to be clear, in this post, the build and host platform are x86_64 (standard PC) and the target is ARM.”
    small remark
    –target is for building compiler itself, here –host is ARM and –build is x86_64

Leave a Reply

Your email address will not be published. Required fields are marked *