Skip to the content.

Build a GCC-based cross compiler toolchain for Linux (with sysroot) – AArch64

This tutorial describes how to build a GCC-based cross compiler toolchain for a AArch64 Linux platform.

1 Prerequisites

To build a cross-compiler, we need the source code of gcc, glibc, binutils, linux-kernel and some standard packages.

1.1 Source code

https://ftp.gnu.org/gnu/gcc/gcc-11.1.0/gcc-11.1.0.tar.xz

https://ftp.gnu.org/gnu/glibc/glibc-2.30.tar.xz

https://ftp.gnu.org/gnu/binutils/binutils-2.37.tar.xz

https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.230.tar.xz

Download the source code from the links above. Building gcc and glibc requires linux kernel headers. That’s why the source code of linux kernel is downloaded.

1.2 Standard packages

To install the standard packages, run the following command

$ sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev

2 Setup the build environment

Assume “topdir” is the directory we will carry out the build process. We put all the source code in directory “${topdir}/srcs”. We create a directory “${topdir}/build” for building the software and a directory “${topdir}/install” for installing the software.

2.1 Create the “srcs”, “build” and “install” directory

$ mkdir ${topdir}/srcs
$ mkdir ${topdir}/build
$ mkdir ${topdir}/install

2.2 Define a few environment variables

$ export srcdir=${topdir}/srcs
$ export builddir=${topdir}/build
$ export installdir=${topdir}/install
$ export sysrootdir=${installdir}/sysroot
$ export targetarch=aarch64-unknown-linux-gnu

2.3 Unzip the source code

$ tar xvf gcc-11.1.0 -C ${srcdir}
$ tar xvf glibc-2.30.tar.xz -C ${srcdir}
$ tar xvf binutils-2.37.tar.xz -C ${srcdir}
$ tar xvf linux-4.19.230.tar.xz -C ${srcdir}

3 The cross-compiler build process

3.1 Build binutils

binutils provides assembler (as) and linker (ld).

$ mkdir ${builddir}/build-binutils
$ cd ${builddir}/build-binutils
$ ${srcdir}/binutils-2.37/configure --prefix=${installdir} \
                                    --target=${targetarch} \
                                    --with-sysroot=${sysrootdir} \
                                    --disable-multilib \
                                    --disable-werror \
                                    --disable-nls \
                                    --with-expat=yes \
                                    --disable-gdb \
                                    --disable-sim \
                                    --disable-libdecnumber \
                                    --disable-libreadline

$ make
$ make install

3.2 Build gcc stage1

At this stage, only “c” is enabled as glibc requires only “aarch64-unknown-linux-gnu-gcc”.

“c++” can not be enabled as it causes the following error:

“checking dynamic linker characteristics… configure: error: Link tests are not allowed after GCC_NO_EXECUTABLES.”

”–with-newlib –without-headers” tells the configuration not to worry about headers and libc at this stage.

“libsanitizer” is disabled as it causes an error during the building process.

$ mkdir ${builddir}/build-gcc-stage1
$ cd ${builddir}/build-gcc-stage1
$ ${srcdir}/gcc-11.1.0/configure --prefix=${installdir} \
                                 --target=${targetarch} \
                                 --with-sysroot=${sysrootdir} \
                                 --with-newlib \
                                 --without-headers \
                                 --disable-libsanitizer \
                                 --disable-shared \
                                 --disable-threads \
                                 --with-system-zlib \
                                 --enable-tls \
                                 --enable-languages=c \
                                 --disable-libatomic \
                                 --disable-libmudflap \
                                 --disable-libssp \
                                 --disable-libquadmath \
                                 --disable-libgomp \
                                 --disable-nls \
                                 --disable-bootstrap \
                                 --src=${srcsdir}/gcc-11.1.0 \
                                 --enable-checking=yes \
                                 --disable-multilib

$ make
$ make install

3.3 Get linux headers

Specify “ARCH=arm64” instead of “ARCH=aarch64” is because the linux-kernel only contains “arm64”.

$ mkdir -p ${sysrootdir}/usr
$ cd ${srcdir}/linux-4.19.230
$ make ARCH=arm64 INSTALL_HDR_PATH=${sysrootdir}/usr headers_install

3.4 Build glibc

Modify glibc-2.30/Makefile and glibc-2.30/support/Makefile. So “links-dso-program-c.c” is used instead of “links-dso-program.cc”, which requires a c++ compiler.

glibc-2.30/Makefile

#ifeq (,$(CXX))
LINKS_DSO_PROGRAM = links-dso-program-c
#else
#LINKS_DSO_PROGRAM = links-dso-program
#endif

glibc-2.30/support/Makefile

#ifeq (,$(CXX))
LINKS_DSO_PROGRAM = links-dso-program-c
#else
#LINKS_DSO_PROGRAM = links-dso-program
#LDLIBS-links-dso-program = -lstdc++ -lgcc -gcc_s $(libunwind)
#endif

Build glibc with “aarch64-unkonwn-linux-gnu-gcc” built in gcc stage1.

3.4.1 Build glibc headers

$ export PATH=${installdir}/bin:$PATH
$ export CC=aarch64-unknown-linux-gnu-gcc
$ unset LD_LIBRARY_PATH
$ buildglibcheaderdir=${builddir}/build-glibc-headers
$ [ -d ${buildglibcheaderdir} ] || mkdir -p ${buildglibcheaderdir}
$ cd ${buildglibcheaderdir}
$ ${srcdir}/glibc-2.30/configure --host=${targetarch} \
                                 --prefix=${sysrootdir}/usr \
                                 --enable-shared \
                                 --with-headers=${sysrootdir}/usr/include \
                                 --disable-multilib \
                                 --enable-kernel=3.0.0
$ make install-headers

3.4.2 Build glibc libs

$ export PATH=${installdir}/bin:$PATH
$ export CC=aarch64-unknown-linux-gnu-gcc
$ mkdir ${builddir}/build-glibc
$ cd ${builddir}/build-glibc
$ ${srcdir}/glibc-2.30/configure --host=${targetarch} \
                                 --prefix=/usr \
                                 --disable-werror \
                                 --enable-shared \
                                 --enable-obsolete-rpc \
                                 --with-headers=${sysrootdir}/usr/include \
                                 --disable-multilib \
                                 --enable-kernel=3.0.0 \
                                 --libdir=/usr/lib \
                                 libc_cv_slibdir=/lib \
                                 libc_cv_rtlddir=/lib
$ make
$ make install install_root=${sysrootdir}

3.5 Build gcc stage2

This is the final stage.

$ mkdir ${builddir}/build-gcc-stage2
$ cd ${builddir}/build-gcc-stage2
$ ${srcdir}/gcc-11.1.0/configure --prefix=${installdir} \
                                 --target=${targetarch} \
                                 --with-sysroot=${sysrootdir} \
                                 --with-system-zlib \
                                 --enable-shared \
                                 --enable-tls \
                                 --enable-languages=c,c++,fortran \
                                 --disable-libmudflap \
                                 --disable-libssp \
                                 --disable-libquadmath \
                                 --disable-nls \
                                 --disable-bootstrap \
                                 --src=${srcsdir}/gcc-11.1.0 \
                                 --enable-checking=yes \
                                 --disable-multilib
$ make
$ make install

After this stage is finished, we are ready to use the cross toolchain.

4 Use the cross-compiler toolchain

To use the cross-compiler toolchain, set the following environment:

$ export CROSSGCCDIR=${installdir}
$ export PATH=${CROSSGCCDIR}/bin:$PATH

Then we are ready to use aarch64-unknown-linux-gnu-gcc, aarch64-unknown-linux-gnu-g++, and aarch64-unknown-linux-gnu-gfortran.