I've tried several different tools for building toolchains, in the end I just made a script based off the GCC docs https://gcc.gnu.org/wiki/InstallingGCC, and a few other things. This builds a pretty fully featured arm toolchain with gcc
, gdb
, newlib-nano
and picolibc
. It also has libssp
which is missing from the official builds from arm for some reason:
#!/bin/bash
set -e
set -v
# Install prerequisites
apt-get -y install libgmp-dev \
libmpfr-dev \
libmpc-dev \
libexpat-dev \
liblzma-dev \
libsource-highlight-dev \
libpython3-dev \
libbabeltrace-dev \
libipt-dev \
libncurses5-dev \
libdebuginfod-dev \
meson \
ninja-build \
texinfo \
pkg-config \
python3 \
python-is-python3 \
flex \
diffutils \
git
apt-get -y install --install-suggests gcc
# For mingw
# pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-mpc mingw-w64-x86_64-mpfr mingw-w64-x86_64-gmp mingw-w64-x86_64-meson mingw-w64-x86_64-ninja texinfo bison flex diffutils git
# Setup build directory
PREFIX=$HOME/gcc-cross-compiler/build
mkdir -p $PREFIX
cd $PREFIX/..
# Clone repositories (may be wise to checkout specific versions i.e. latest release tag)
git clone --depth=1 -b releases/gcc-13.1.0 git://gcc.gnu.org/git/gcc.git
git clone --depth=1 -b newlib-4.3.0 https://sourceware.org/git/newlib-cygwin.git
git clone --depth=1 -b gdb-13.2-release git://sourceware.org/git/binutils-gdb.git
git clone --depth=1 -b 1.8.2 https://github.com/picolibc/picolibc.git
# Build binutils
cd $PREFIX/.. && mkdir -p binutils-build && cd binutils-build
../binutils-gdb/configure \
--target=arm-none-eabi \
--prefix=$PREFIX \
--with-pkgversion=my-custom-$(date -I) \
--with-sysroot \
--with-lzma \
--with-expat \
--with-python \
--enable-ld=yes \
--enable-gold=no \
--enable-plugins \
--enable-multilib \
--enable-lto \
--enable-source-highlight \
--disable-werror \
--disable-nls \
--disable-warn-rwx-segments
make -j$(nproc)
make install
# Build gcc (state-1), this is so that our libc implementation below (newlib)
# gets compiled with the nice shiny new compiler version as well. This build
# will only build the gcc compiler itself, not all of the libraries like libgcc etc.
# because for that we need a libc implementation... which we haven't built yet.
#
# You could alternatively skip this step by installing a arm-none-eabi-gcc
# compiler from somewhere else (i.e. sudo apt install gcc-arm-none-eabi)
# and use that to compile newlib instead, which will probably work so long as
# it has been compiled with multilib support for at least rmprofile
# (because of the `--with-multilib-list=rmprofile` below)
cd $PREFIX/.. && mkdir -p gcc-stage1-build && cd gcc-stage1-build
../gcc/configure \
--prefix=$PREFIX \
--enable-languages=c \
--target=arm-none-eabi \
--with-pkgversion=stage1-my-custom-$(date -I) \
--with-newlib \
--with-sysroot=$PREFIX/arm-none-eabi \
--with-multilib-list=rmprofile \
--without-headers \
--enable-lto \
--enable-multiarch \
--disable-libssp \
--disable-nls \
--disable-tls \
--disable-threads \
--disable-shared
make -j$(nproc) all-gcc
make install-gcc
export PATH=$PREFIX/bin:$PATH
# Build newlib using our stage1 compiler (this is essentially newlib-nano)
cd $PREFIX/.. && mkdir -p newlib-build && cd newlib-build
../newlib-cygwin/configure \
--target=arm-none-eabi \
--prefix=$PREFIX \
--disable-newlib-supplied-syscalls \
--disable-newlib-io-float \
--disable-newlib-io-long-double \
--disable-newlib-io-pos-args \
--disable-newlib-io-c99-formats \
--disable-newlib-io-long-long \
--disable-newlib-multithread \
--disable-newlib-retargetable-locking \
--disable-newlib-wide-orient \
--disable-newlib-fseek-optimization \
--disable-newlib-fvwrite-in-streamio \
--disable-newlib-unbuf-stream-opt \
--disable-newlib-atexit-dynamic-alloc \
--enable-newlib-nano-malloc \
--enable-newlib-nano-formatted-io \
--enable-newlib-global-atexit \
--enable-lite-exit \
--enable-newlib-reent-small \
--enable-libssp \
--enable-target-optspace
make -j$(nproc)
make install
# Build gcc (stage-2), this builds the whole gcc featureset,
# including the compiler, and all the gnu compiler libraries
cd $PREFIX/.. && mkdir -p gcc-build && cd gcc-build
../gcc/configure \
--prefix=$PREFIX \
--enable-languages=c,c++ \
--target=arm-none-eabi \
--with-pkgversion=my-custom-$(date -I) \
--with-newlib \
--with-sysroot=$PREFIX/arm-none-eabi \
--with-multilib-list=rmprofile \
--without-headers \
--enable-lto \
--enable-target-optspace \
--enable-multiarch \
--disable-libssp \
--disable-nls \
--disable-tls \
--disable-threads \
--disable-shared
make -j$(nproc)
make install
# Build picolibc
cd $PREFIX/.. && mkdir -p picolibc-build && cd picolibc-build
../picolibc/scripts/do-configure arm-none-eabi \
-Dsysroot-install=true \
-Dfast-strcmp=false \
-Dio-c99-formats=false \
-Dnewlib-global-atexit=true \
-Dnewlib-multithread=false \
-Dnewlib-retargetable-locking=false
ninja
ninja install
Maintained here: https://gist.github.com/badcf00d/2f6054441375d9c94896aaa8e878ab4f
The main caveat with this is the nano.specs
file won't really work, because "newlib-nano" is just simply newlib compiled with some flags to make it smaller. The arm ABE build thing just compiles newlib twice, and cp
's the smaller one to *-nano.a. But I don't care about the larger newlib build so I'm just compiling newlib once, with the smaller configuration, so lib/libc.a
is newlib-nano. I usually just edit the nano.specs
file and remove -nano
from everything.
Once built, just add it to your PATH
and you should be good to go, something like:
export PATH="$HOME/gcc-cross-compiler/build/bin/:$PATH"
You can also use this script in mingw/msys2 to compile toolchains on Windows (see the comment with the pacman
command).