In this article, I will show you how to build GCC 9 as a cross compiler for Raspberry Pi. A cross compiler is a compiler that runs on an operating system and produces executables for another. This is really useful when you want to use your beefy computer to build a library or other large piece of code for Raspberry Pi. As a practical example, at the end of the article, I will show you how to use the cross compiler to build GCC itself as a native Raspberry Pi application.


To build and host the cross compiler, I’ve used Ubuntu 18.04 and Debian 9, but a similar procedure should work on other Linux distributions.
First, make sure your system is updated and install the required prerequisites:
sudo apt update
sudo apt upgrade
sudo apt install build-essential gawk git texinfo bison file

At the time of this writing, Raspbian comes with GCC 6.3.0, Binutils 2.28 and Glibc 2.24. It is really important that we build our cross compiler using the same Glibc version as the one from Raspbian. This will allow us to integrate nicely with the OS. If you are from the future and read this article, you can check the versions of the above software with these commands:
gcc --version
ld -v
ldd --version

If you are trying to build Glibc 2.24 with a more modern GCC, like 7.x or 8.x, you are going to get a lot of errors. Easiest approach is to build Glibc with GCC 6.3.0 and later use it with the latest and greatest GCC, which at this time is 9.1.
In the next instructions I’ll assume that you are doing all the steps in a separate folder and that you keep the same Terminal session open until everything is done. For example, you can create a working folder in your home:
sudo su
cd ~
mkdir gcc_all && cd gcc_all
Let’s download the software that we’ll use for building the cross compiler:
wget https://ftpmirror.gnu.org/binutils/binutils-2.28.tar.bz2
wget https://ftpmirror.gnu.org/gcc/gcc-6.3.0/gcc-6.3.0.tar.gz
wget https://ftpmirror.gnu.org/glibc/glibc-2.24.tar.bz2
wget https://ftpmirror.gnu.org/gcc/gcc-9.1.0/gcc-9.1.0.tar.gz
git clone --depth=1 https://github.com/raspberrypi/linux

Next, extract the archives and erase them:
tar xf binutils-2.28.tar.bz2
tar xf glibc-2.24.tar.bz2
tar xf gcc-6.3.0.tar.gz
tar xf gcc-9.1.0.tar.gz
rm *.tar.*

GCC also needs some prerequisites which we can download inside the source folder:
cd gcc-6.3.0
contrib/download_prerequisites
rm *.tar.*
cd ..
cd gcc-9.1.0
contrib/download_prerequisites
rm *.tar.*

Next, create a folder in which we’ll put the cross compiler and add it to the path:
cd ~/gcc_all
sudo mkdir -p /opt/cross-pi-gcc
sudo chown $USER /opt/cross-pi-gcc
export PATH=/opt/cross-pi-gcc/bin:$PATH
Copy the kernel headers in the above folder, see Raspbian documentation for more info about the kernel:
cd ~/gcc_all
cd linux
KERNEL=kernel7
make ARCH=arm INSTALL_HDR_PATH=/opt/cross-pi-gcc/arm-linux-gnueabihf headers_install
Next, let’s build Binutils:
cd ~/gcc_all
mkdir build-binutils && cd build-binutils
../binutils-2.28/configure --prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
make -j 8
make install

Open in a text editor ubsan.c from /home/pi/gcc_all/gcc-9.1.0/gcc, find line 1474:
|| xloc.file == '\0' || xloc.file[0] == '\xff'
and change it to:
|| xloc.file[0] == '\0' || xloc.file[0] == '\xff'
save and close the file.

GCC and Glibc are interdependent, you can’t fully build one without the other, so we are going to do a partial build of GCC, a partial build of Glibc and finally build GCC and Glibc.
cd ~/gcc_all
mkdir build-gcc && cd build-gcc
../gcc-6.3.0/configure --prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf --enable-languages=c,c++,fortran --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
make -j8 all-gcc
make install-gcc

Now, let’s partially build Glibc:
cd ~/gcc_all
mkdir build-glibc && cd build-glibc
../glibc-2.24/configure --prefix=/opt/cross-pi-gcc/arm-linux-gnueabihf --build=$MACHTYPE --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --with-arch=armv6 --with-fpu=vfp --with-float=hard --with-headers=/opt/cross-pi-gcc/arm-linux-gnueabihf/include --disable-multilib libc_cv_forced_unwind=yes
make install-bootstrap-headers=yes install-headers
make -j8 csu/subdir_lib
install csu/crt1.o csu/crti.o csu/crtn.o /opt/cross-pi-gcc/arm-linux-gnueabihf/lib
arm-linux-gnueabihf-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o /opt/cross-pi-gcc/arm-linux-gnueabihf/lib/libc.so
touch /opt/cross-pi-gcc/arm-linux-gnueabihf/include/gnu/stubs.h
Back to GCC:
cd ..
cd build-gcc
make -j8 all-target-libgcc
make install-target-libgcc
Finish building Glibc:
cd ..
cd build-glibc
make -j8
make install
Finish building GCC 6.3.0:
cd ..
cd build-gcc
make -j8
make install
cd ..
Optionally, write a small C or C++ test program, you can build the code with:
arm-linux-gnueabihf-g++ test.cpp -o test
The resulting executable, test, from above was built with our first cross compiler and will run on your Pi. You can check that using the file command:
file test
At this point, you have a full cross compiler toolchain with GCC 6.3.0. Make a backup, in case somethig goes wrong, before proceeding with the next step:
sudo cp -r /opt/cross-pi-gcc /opt/cross-pi-gcc-6.3.0
cd ~/gcc_all
Edit gcc-9.1.0/libsanitizer/asan/asan_linux.cc. You will need to define PATH_MAX if it is not defined. Add the next piece of code after about line 77:
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
save and close the file.
Next, we are going to use the above built Glibc to build a more modern cross compiler that will overwrite 6.3:
cd ~/gcc_all
mkdir build-gcc9 && cd build-gcc9
../gcc-9.1.0/configure --prefix=/opt/cross-pi-gcc --target=arm-linux-gnueabihf --enable-languages=c,c++,fortran --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib
make -j8
make install
At this point, you can use GCC 9.1 to cross compile any C, C++ or Fortran code for your Raspberry Pi. You can invoke any of the cross compilers by using the prefix:
arm-linux-gnueabihf-
examples: arm-linux-gnueabihf-gcc, arm-linux-gnueabihf-g++, arm-linux-gnueabihf-gfortran.
In order to stress test our cross compiler, let’s use it to cross compile itself for the Pi:
sudo mkdir -p /opt/gcc-9.1.0
sudo chown $USER /opt/gcc-9.1.0
cd ~/gcc_all
mkdir build-native-gcc9 && cd build-native-gcc9
../gcc-9.1.0/configure --prefix=/opt/gcc-9.1.0 --build=$MACHTYPE --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --enable-languages=c,c++,fortran --with-arch=armv6 --with-fpu=vfp --with-float=hard --disable-multilib --program-suffix=-9.1
make -j 8
make install-strip
You should end up with a a native ARM GCC in your /opt/gcc-9.1.0 folder.
As a side note, building GCC 9.1 with the above cross compiler took about 15 minutes on a Debian 9 x86-64 machine. Compare this with the 5 hours I needed to build GCC 8.1 directly on my Pi 3 in the past and you will see the advantage of having a cross compiler on your main machine. Someone told me that compiling GCC 7 on a Raspberry Pi Zero took about 5 days!
If you want to permanently add the cross compiler to your path, use something like:
echo 'export PATH=/opt/cross-pi-gcc/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
You can now, optionally, safely erase the build folder. Assuming you’ve followed my advice, from your home folder use the next command:
cd ~
rm -rf gcc_all
Let’s archive the compiler and save it to our home folder:
cd /opt
tar -cjvf ~/gcc-9.1.0-armhf-raspbian.tar.bz2 gcc-9.1.0
cd ~
Copy gcc-9.1.0-armhf-raspbian.tar.bz2 to your RPi. For the remaining of this article I’ll assume you are on your RPi and that the above archive is in your home folder:
cd ~
tar xf gcc-9.1.0-armhf-raspbian.tar.bz2
rm gcc-9.1.0-armhf-raspbian.tar.bz2
sudo mv gcc-9.1.0 /opt
Next, we are going to add the new compilers to the path and create a few symbolic links:
echo 'export PATH=/opt/gcc-9.1.0/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/opt/gcc-9.1.0/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
sudo ln -s /usr/include/arm-linux-gnueabihf/sys /usr/include/sys
sudo ln -s /usr/include/arm-linux-gnueabihf/bits /usr/include/bits
sudo ln -s /usr/include/arm-linux-gnueabihf/gnu /usr/include/gnu
sudo ln -s /usr/include/arm-linux-gnueabihf/asm /usr/include/asm
sudo ln -s /usr/lib/arm-linux-gnueabihf/crti.o /usr/lib/crti.o
sudo ln -s /usr/lib/arm-linux-gnueabihf/crt1.o /usr/lib/crt1.o
sudo ln -s /usr/lib/arm-linux-gnueabihf/crtn.o /usr/lib/crtn.o
At this point, you should be able to invoke the compilers with gcc-9.1, g++-9.1 or gfortran-9.1.
Let’s try to compile and run a C++17 code that uses an if block with init-statement (the example is a bit silly, but it will show you how to compile C++17 programs):
#include <iostream>
int main() {
// if block with init-statement:
if(int a = 5; a < 8) {
std::cout << "Local variable a is < 8\n";
} else {
std::cout << "Local variable a is >= 8\n";
}
return 0;
}
Save the above code in a file named if_test.cpp and compile it with:
g++-9.1 -std=c++17 -Wall -pedantic if_test.cpp -o if_test
This is what I see on my RPi:
pi@raspberrypi:~ $ g++-9.1 -std=c++17 -Wall -pedantic if_test.cpp -o if_test
pi@raspberrypi:~ $ ./if_test
Local variable a is < 8
pi@raspberrypi:~ $
Let’s also try to use the C++17 filesystem:
#include <iostream>
#include <filesystem>
int main() {
for(auto &file : std::filesystem::recursive_directory_iterator("./")) {
std::cout << file.path() << '\n';
}
}
Save the above code in a file named fs_test.cpp and compile it with:
g++-9.1 -std=c++17 -Wall -pedantic fs_test.cpp -o fs_test
This is what I see on my RPi (don’t run the next code in your home folder if you have a lot of files because it will recursively print all of them, for example you can move it in a folder with a smaller numbers of files):
pi@raspberrypi:~ $ g++-9.1 -std=c++17 -Wall -pedantic fs_test.cpp -o fs_test
pi@raspberrypi:~ $ ./fs_test
"./.nano"
"./.profile"
"./.bash_logout"
"./fs_test.cpp"
"./.bashrc"
"./if_test.cpp"
"./if_test"
"./fs_test"
"./.bash_history"
pi@raspberrypi:~ $



