CrossCompiling for Arm on Debian

Compile for ARM Hard Float from the comfort of your debian laptop
C tech embedded gcc cross-compiler IOT M2M

CrossCompiling for Arm on Debian

ARM boards

Arm processors are everywhere, and will soon land on the desktop as well. So far, they provide quite nice functionalities for embedded application. Chances are, you will have some ARM board lying around. You probably also have a way to install a compiler on top of your board, and compile directly on it.

But if you are either doing something more professional, or you want for example introduce some CI tool, you don’t want to compile on the board, and you are going to need a cross-compiling setup.

This used to be more complex that it is now, and I’d encourage anyone to setup a build chain in order to be able to compile code for an architecture different from the one you are running on.

The code

Let’s say you are trying to compile some C fragment that goes like this:

#include <stdio.h>

int main(int argc, char *argv[]){                               
    printf("whatever\n");                                        
    return 0;                                                   
}  

The command that will bring a cross-compiler to life for yourself is the following:

apt-get install gcc-arm-linux-gnueabihf

Needless to say, you need to be root in order to install packages, or you can use sudo if you want. Now that you have installed the cross tools, you will see that you will have available some variation of the following commands:

arm-linux-gnueabihf-addr2line
arm-linux-gnueabihf-ar
arm-linux-gnueabihf-as
arm-linux-gnueabihf-c++filt
arm-linux-gnueabihf-cpp
arm-linux-gnueabihf-cpp-6
arm-linux-gnueabihf-dwp
arm-linux-gnueabihf-elfedit
arm-linux-gnueabihf-gcc
arm-linux-gnueabihf-gcc-6
arm-linux-gnueabihf-gcc-ar
arm-linux-gnueabihf-gcc-ar-6
arm-linux-gnueabihf-gcc-nm
arm-linux-gnueabihf-gcc-nm-6
arm-linux-gnueabihf-gcc-ranlib
arm-linux-gnueabihf-gcc-ranlib-6
arm-linux-gnueabihf-gcov
arm-linux-gnueabihf-gcov-6
arm-linux-gnueabihf-gcov-dump
arm-linux-gnueabihf-gcov-dump-6
arm-linux-gnueabihf-gcov-tool
arm-linux-gnueabihf-gcov-tool-6
arm-linux-gnueabihf-gprof
arm-linux-gnueabihf-ld
arm-linux-gnueabihf-ld.bfd
arm-linux-gnueabihf-ld.gold
arm-linux-gnueabihf-nm
arm-linux-gnueabihf-objcopy
arm-linux-gnueabihf-objdump
arm-linux-gnueabihf-ranlib
arm-linux-gnueabihf-readelf
arm-linux-gnueabihf-size
arm-linux-gnueabihf-strings
arm-linux-gnueabihf-strip

Probably, in order to compile your dummy code sample, the following would be enough:

arm-linux-gnueabihf-gcc whatever.c -o whatever

But I’d not recommend it, because if you’re serious about writing code, you’ll probably find yourself repeating the same commands a thousand times, then maybe you want to run it on your current architecture, and then you want to use the linker… if you don’t see the point by now: it will quickly start to explode in tiny micro-issues that need to be handled by hand.

You might have different preferences when it comes to build systems, but I think the old Make is still a good tool for the trade. If it isn’t for yourself, you’d probably won’t have a hard time to adapt the following ideas to your favourite shiny build tool.

As a practical example, let’s say you have a file with a main: whatever.c and a file with function definitions blink.c. You’ll normally compile them with a Makefile like that:

all: whatever                                                   
                                                                
whatever.o: whatever.c
	cc -c whatever.c                                            

blink.o: blink.c blink.h                                 
	cc -c blink.c                                            

whatever: whatever.o blink.o
	cc whatever.o blink.o -o whatever                                            
                                                                
clean:  whatever                                                
	rm *.o; rm whatever  

The hint is to replace explicit calls to compiler with variables, and not just the compiler, but also linker and other binutils. To put it simply, you can just prefix your Makefile with:

CC=${CROSS_COMPILE}gcc
CXX=${CROSS_COMPILE}g++
RANLIB=${CROSS_COMPILE}ranlib
LD=${CROSS_COMPILE}ld
LDXX=${CROSS_COMPILE}g++
AR=${CROSS_COMPILE}ar 

Now that you’re in the game, I’d recommend trowing in also the flags:

CFLAGS= -g -Wall 
LDFLAGS= -g -Wall 

that are just so damn useful when you’re putting together some meaningful piece of code.

So, to recap, if your code is the following:

//blink.c 
#include <stdio.h>
#include "blink.h"
void blink_something(){
	printf("maybe blink something");
}

and:

//blink.h 
#ifndef H_BLINK_SOMETHING
#define H_BLINK_SOMETHING

void blink_something();

#endif

the main file:

//whatever.c 
#include <stdio.h>

int main(int argc, char *argv[]){                               
    printf("whatever\n");                                        
    return 0;                                                   
}  

You can have the following makefile:

CC=${CROSS_COMPILE}gcc                                                             
CXX=${CROSS_COMPILE}g++                                                            
RANLIB=${CROSS_COMPILE}ranlib                                                      
LD=${CROSS_COMPILE}ld                                                              
LDXX=${CROSS_COMPILE}g++                                                           
AR=${CROSS_COMPILE}ar   

all: whatever                                                   
                                                                
whatever.o: whatever.c
	${CC} -c whatever.c                                            

blink.o: blink.c blink.h                                 
	${CC} -c blink.c                                            

whatever: whatever.o blink.o
	${CC} whatever.o blink.o -o whatever                                            
                                                                
clean:  whatever                                                
	rm *.o; rm whatever  

This is supposed to work like this, for your native architecture, just issue the command:

make

and you’ll be able to compile. The following, in order to clean the builĂds:

$ make clean
rm *.o; rm whatever  
gcc -c whatever.c                                            
gcc -c blink.c                                            
gcc whatever.o blink.o -o whatever      

Now you can exploit the use of variables, and issue the command:

make CROSS_COMPILE=arm-linux-gnueabihf-

You will see the result will be that the cross-compiler is used, instead of the one for your running architecture:

arm-linux-gnueabihf-gcc -c whatever.c
arm-linux-gnueabihf-gcc -c blink.c
arm-linux-gnueabihf-gcc whatever.o blink.o -o whatever

Your whatever binary is now compiled for ARM hard float, and you can check it like that:

$ file whatever
whatever: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=5673161126e8511971a0b8111868d2ac9a723057, not stripped

You will probably throw the command itself in a script, like:

cat build.sh 
#! /bin/bash -x

make CROSS_COMPILE=arm-linux-gnueabihf-