4

For my current project we are having an issue which we can solve if we are able to recompile the arm toolchain (gcc, c++stdlib, nanolibc, etc) from source.

From the arm website I can download a snapshot of the source. I found a blog but it's outdated. The pdf he refers to no longer exists in this snapshot anyway.

Browsing through the extracted archive I can't seem to find any instructions how to compile.

Where can I find documentation how to compile arm-none-eabi from source?

bas
  • 13,550
  • 20
  • 69
  • 146
  • `we are having an issue which we can solve if we are able to recompile` that must be a _very odd_ issue. What issue is it? What is "nanolibc" (did you mean newlib)? Why do you need to recompile gcc _and_ c++stdlib _and_ libc (_and_ "etc"??) to solve that issue? What issue is it?! What issue could it be?! `how to compile arm-none-eabi from source?` There is no "arm-none-eabi", it's gcc compiler, documentation is available online. https://gcc.gnu.org/install/ The best is to view package configuration (like PKGBUILD in archlinux) or CI/CD pipelines that have all the commands needed. – KamilCuk May 30 '22 at 22:53
  • https://stackoverflow.com/questions/50154137/how-to-rebuild-newlib-and-newlib-nano-of-gnu-arm-embedded-toolchain/50348732#50348732 and especially https://launchpad.net/gcc-arm-embedded might be of interest to you . From archlinux https://github.com/archlinux/svntogit-community/blob/packages/arm-none-eabi-gcc/trunk/PKGBUILD https://github.com/archlinux/svntogit-community/blob/packages/arm-none-eabi-newlib/trunk/PKGBUILD etc... – KamilCuk May 30 '22 at 23:04
  • Hi @KamilCuk, well, to be honest, it's not a fact just yet. Looking at how strong you are coming of on this I am less certain this is correct. The problem in as little text as possible in next comment :) – bas May 31 '22 at 11:49
  • 2
    We are building a typical iot application on an MCU. we *need* something which is I think less common on microcontrollers: we need to be able to deploy our app twice on flash, and let our custom bootloader decide to which of the two apps it should jump. That way, we can update "the other" app over the air. So I compile my project with `-fpic`, copy vectors/.got and c++ init array to sram during startup. This way, I can run my app on any location on flash. This works (after a long ride to be fair). The ONLY problem I now have, is that any call to std library hardfaults *when app is relocated*. – bas May 31 '22 at 11:52
  • 2
    I can imagine nanolibc (yeah, indeed, newlibs functionality, but smaller in size), and other libraries that are available in the arm toolchain are NOT compiled with -fpic, and thus cause issues when I am relocating my app. Again, I might be wrong, but I have no other explanation that makes any sense. Hence I thought to give it a try, recompile arm toolchain with -fpic and see if it indeed fixes my problem (didn't manage to explain this in one comment ;-)) – bas May 31 '22 at 11:54
  • So, to summarize, you asked XY question. No, you _do not_ need to recompile anything to implement that, and it is not that "less" common, it is quite common. From experience, I deployed such bootloader and 2 apps on a NRF52 chip. `arm toolchain with -fpic` No, this is invalid - whatever you compile arm toolchain with does not affect compiled executable. And `-fpic` makes no sense when there is no CPU support for such code and no dynamic linker on target. `nanolibc (yeah, indeed, newlibs functionality,` It's called "newlib-nano", it's newlib with proper `./configure` options. – KamilCuk May 31 '22 at 12:33
  • 2
    @KamilCuk, No clue what you are talking about regarding `And -fpic makes no sense when there is no CPU support for such code and no dynamic linker on target`. Compiling an app with -fpic and being able to deploy a binary on a different location then its originally linked locations saves the trouble of managing 2 different binaries linked for location a and another for location b. The only thing I need to take care of is patching the addresses in the .got section in startup code of the app. – bas May 31 '22 at 13:08
  • 2
    The libraries that are coming from arm toolchain are **libc_nano.a**, not newlib-nano. Or c++ std which should be in libstdc++_nano.a. Why are you saying that recompiling these with -fpic will not solve my problem? How can you be so certain? Doesn't it make perfect sense that a static library I use which is not compiled with -fpic will cause issues that global vars/functions used in that library are not properly linked into the .got table? – bas May 31 '22 at 13:10

4 Answers4

1

The release notes available at the download site include build-from-source instructions.

I've made a quick transcription here, but future readers should be warned it may have become out-of-date.

How to build the toolchain from sources

You can build Arm GNU Toolchain from sources using Linaro ABE (Advanced Build Environment) and provided ABE manifest files.

Below example shows how to build gcc-arm-aarch64-none-elf toolchain from sources using Linaro ABE build system.

Instructions

ABE has a dependency on git-new-workdir and needs this tool to be installed in /usr/local/bin directory:

$ wget https://raw.githubusercontent.com/git/git/master/contrib/workdir/git-new-workdir
$ sudo mv git-new-workdir /usr/local/bin
$ sudo chmod +x /usr/local/bin/git-new-workdir 

Clone ABE from the URL below and checkout the stablebranch (see Getting ABE):

$ git clone https://git.linaro.org/toolchain/abe.git

Create the build directory and change to it. Any name for the directory will work:

$ mkdir build && cd build

Configure ABE (from the build directory):

$ ../abe/configure

Download the toolchain manifest file, from https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads, into the build folder, for the required toolchain, for example, gcc-arm-aarch64-none-elf-abe-manifest.txt:

$  wget https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/manifest/gcc-arm-aarch64-none-elf-abe-manifest.txt

Build toolchain (from the build directory):

$ ../abe/abe.sh --manifest gcc-arm-aarch64-none-elf-abe-manifest.txt --build all

The built toolchain will be installed and available for use in the builds/destdir/x86_64-unknown-linux-gnu/bin/ directory.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
1
# Usage
# sudo ./build_arm

# Setup vars
export TARGET=arm-none-eabi
export PREFIX=/opt/gnuarm
export PATH=$PATH:$PREFIX/bin
export JN='-j 8'

export GCCVER=11.2.0
export BINUVER=2.37

rm -rf build-*
rm -rf gcc-*
rm -rf binutils-*

# Get archives
wget https://ftp.gnu.org/gnu/binutils/binutils-$BINUVER.tar.gz
wget https://ftp.gnu.org/gnu/gcc/gcc-$GCCVER/gcc-$GCCVER.tar.gz

# Extract archives
tar xf binutils-$BINUVER.tar.gz
tar xf gcc-$GCCVER.tar.gz

# Build binutils
mkdir build-binutils
cd build-binutils
../binutils-$BINUVER/configure --target=$TARGET --prefix=$PREFIX
echo "MAKEINFO = :" >> Makefile
make $JN all
sudo make install

# Build GCC
mkdir ../build-gcc
cd ../build-gcc
../gcc-$GCCVER/configure --target=$TARGET --prefix=$PREFIX --without-headers --with-newlib  --with-gnu-as --with-gnu-ld --enable-languages='c' --enable-frame-pointer=no
make $JN all-gcc
sudo make install-gcc

# Build libgcc.a
make $JN all-target-libgcc CFLAGS_FOR_TARGET="-g -O2"
sudo make install-target-libgcc
old_timer
  • 69,149
  • 8
  • 89
  • 168
1

For those who are running into the same problem, I found a solution for the arm toolchain 12.2. It might also work for the previous version of the toolchain, I haven't checked. 12.2 brings c++ 20 support.

I containerized this as follows

# install dependencies for python3.8
RUN apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev wget -y

# install arm toolchain
RUN ARM_TOOLCHAIN_VERSION=12.2.Rel1
RUN curl -Lo gcc-arm-none-eabi.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu/12.2.Rel1/binrel/arm-gnu-toolchain-12.2.Rel1-x86_64-arm-none-eabi.tar.xz"
RUN mkdir -p /opt/gcc-arm-none-eabi
RUN tar xf gcc-arm-none-eabi.tar.xz --strip-components=1 -C /opt/gcc-arm-none-eabi
ENV PATH="/opt/gcc-arm-none-eabi/bin:${PATH}"

# test arm-none-gcc
RUN arm-none-eabi-gcc --version

# install python3.8
RUN wget https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tgz
RUN tar -xf Python-3.8.12.tgz
WORKDIR /Python-3.8.12
RUN ./configure --enable-optimizations
RUN make -j 4
RUN make altinstall

#attempt to fix libncursesw.so.5
RUN apt install libncurses5 -y
RUN apt install libncursesw5 -y

# test arm-none-gdb
RUN arm-none-eabi-gdb --version

So it basically :

  • installs a bunch of dependencies,
  • downloads arm toolchain 12.2
  • compiles python3.8 from sources and installs python3.8 next to any existing python version.
  • tests if it can execute arm-none-eabi-gdb

If you want to execute this on your host OS, remove the docker RUN commands and add some sudo's here and there :).

If it's of any help, my full docker file (which installs a bunch more, like jlink support to be able to compile/run/debug from vscode when attached to this docker container)

FROM ubuntu

ENV UDEV=on

RUN apt-get update -y
RUN apt-get upgrade -y

# Install dependencies for JLink
RUN apt install libxcb-render-util0-dev -y
RUN apt install libxrender1 libxcb-shape0 libxcb-randr0 libxcb-xfixes0 libxcb-sync1 libxcb-shm0 libxcb-icccm4 libxcb-keysyms1 libxcb-image0 libxkbcommon0 libxkbcommon-x11-0 libfontconfig1 libfreetype6 libxext6 libx11-xcb1 libsm6 libice6 libglib2.0-0 -y

# Install dependencies for JLinkServer
RUN apt install libxcursor-dev libxfixes3 libxrandr2 -y

# install jlink
RUN mkdir -p /home/Downloads
COPY JLink_Linux_V786b_x86_64.deb /home/Downloads/JLink_Linux_V786b_x86_64.deb
RUN dpkg --unpack /home/Downloads/JLink_Linux_V786b_x86_64.deb
RUN rm /var/lib/dpkg/info/jlink.postinst -f
RUN dpkg --configure jlink
RUN apt install -yf 

# Install curl
RUN apt install curl bzip2 -y


# install dependencies for arm-none-eabi-gdb
#RUN apt install libncurses5 -y

# install dependencies for python3.8
RUN apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev wget -y

# install arm toolchain
RUN ARM_TOOLCHAIN_VERSION=12.2.Rel1
RUN curl -Lo gcc-arm-none-eabi.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu/12.2.Rel1/binrel/arm-gnu-toolchain-12.2.Rel1-x86_64-arm-none-eabi.tar.xz"
RUN mkdir -p /opt/gcc-arm-none-eabi
RUN tar xf gcc-arm-none-eabi.tar.xz --strip-components=1 -C /opt/gcc-arm-none-eabi
ENV PATH="/opt/gcc-arm-none-eabi/bin:${PATH}"

# test arm-none-gcc
RUN arm-none-eabi-gcc --version

# install cmake
RUN apt install cmake -y
RUN apt install udev -y
RUN /lib/systemd/systemd-udevd --daemon

# install git
RUN apt install git -y

# install ninja
RUN apt install ninja-build python3 pip -y
RUN pip install meson

# install python3.8
RUN wget https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tgz
RUN tar -xf Python-3.8.12.tgz
WORKDIR /Python-3.8.12
RUN ./configure --enable-optimizations
RUN make -j 4
RUN make altinstall

#attempt to fix libncursesw.so.5
RUN apt install libncurses5 -y
RUN apt install libncursesw5 -y


# test arm-none-gdb
RUN arm-none-eabi-gdb --version

ARG USER_ID
ARG GROUP_ID

RUN addgroup --gid $GROUP_ID user && adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user
USER user

WORKDIR /home/dev/

CMD bash

bas
  • 13,550
  • 20
  • 69
  • 146
0

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).

Peter Frost
  • 351
  • 3
  • 6