Cross-Compiling GCC toolchain for ARM Cortex-M Processors

Keywords: Embedded systems, Build Process, Toolchain, Compiler, Object Files

Code Link: Complete Bash Script Github Link


A toolchain is a set of tools that compiles source code into executables that can run on your target device. A toolchain normally consists of a compiler, a linker, and run-time libraries. [1]

In this tutorial we will learn how to build GCC toolchain for a target processor in general and ARM Cortex-M processor in particular. Though the process is straight forward, in order to fine tune the toolchain a good background knowledge of GCC is highly desirable. This is one of the reason that manually compiling toolchain should be the last option to consider unless you have strong background knowledge of GCC or a target CPU/Processor for which no vendor is supplying GCC toolchain (If you wanna stick to it).

The beauity of GCC toolchain is that it’s absolutely free with support for wide number of target architectures including x86, ARM v4/v5/v6/v7, and many others. GCC has quite large support community. Another recently evolved (and evolving with pretty much good phase) toolchain is called Clang Compiler (LLVM project). Clang is a viable alternative to the GCC toolchain. One of the major difference between LLVM and GNU-based toolchains is the licensing; LLVM has a BSD license, while GNU has the GPL.

There are many vendors who provide pre-build GCC toolchain binaries for ARM processor. Two of the well known are ARM itself (AND who will know ARM Processor better than ARM itself) and Mentor Graphics Sourcery CodeBench. Links to both binary distribution are given bellow.


1. Toolchain naming Convention:

Before we move forward with this tutorial first let’s understand ARM toolchain naming convention which indicate for WHAT purpose and for WHICH Environment the GCC toolchain will Build Binaries! GCC follows UNIX like naming convention which is pretty much loosely followed. The general naming convention is:
arch [-vendor] [-os] – abi

arch: “arch” refers to target architecture e.g. ARM, MIPS etc.
vendor: “vendor” refers to toolchain supplier name e.g. apple
os: “os” refers to the target operating system. This indicate two thing.. 1. Which Libraries are linked to the toolchain, 2. System call conventions (see C-Library bellow).
abi: The “abi” specify which Application Binary Interface (ABI) convention is used by toolchain. Being ABI compliant means that binaries generated by different toolchains can inter-operate.

Note: Not all target toolchain follows the arch [-vendor] [-os] – abi naming convention e.g. arm-cortex_a8-linux-gnueabi, arm-elf etc. also there is currently no official list of the target names that are supported. Few commonly used target toolchain names for ARM are as given bellow.

arm-none-eabi: This toolchain targets the ARM architecture, has no vendor, does not target an operating system (i.e. targets a “bare metal” system), and complies with the ARM EABI. In this tutorial we will build this target Toolchain. arm-none-linux-gnueabi: This toolchain targets the ARM architecture, has no vendor, creates binaries that run on the Linux operating system (follows Linux System Calls Convention), and uses the GNU EABI.

arm-elf-eabi: This is aimed at any operating system capable to run elf and support EABI. The OS part of C-Library is left unmodified.

arm-elf: This is a generic ARM target, without OS-specific modifications, that generates object files in the ELF file format.

2. Building Block – GCC Toolchain:

A complete toolchain consists of the following major components shown in Figure-1.

Figure-1: Toolchain Components

We will not go deep into the details of each component as we already have separate tutorials for it. A surface touch will be given in the relevant section. The main goal of this tutorial is to learn how to build a complete Toolchain from all the above components freely provided by GNU.

3. Build Process:

Now that we know the major components of toolchain and the main reasons behind cross compiling GCC toolchain for a target processor, let’s jump into cross compilation process. Following are the required main components that covers Toolchain requirements.

  1. Binutils   ↦   (GNU Linker , Assembler etc.)
  2. GCC      ↦    (The C/C++ Compiler)
  3. newlib   ↦    (C-Library)
  4. GDB      ↦    (GNU Debugger)

For this tutorial we will be using the following versions of these components.

  1. BINUTILS_VERSION = binutils-2.28.tar.gz
  2. GCC_VERSION = gcc-6.4.0.tar.gz
  3. NEWLIB_VERSION = newlib-2.5.0.tar.gz
  4. GDB_VERSION = gdb-8.0.tar.gz

3.1 Prerequisites:

In order to proceed with this tutorial, you need a System with atleast 1GB free space and 2GB RAM with Linux/UNIX OS or windows with Cygwin installed. The second approach i.e. Windows with Cygwin is comparatively less desirable due to Cygwin package manager.

3.2 Procedure:

1. Building GCC toolchain requires few dependencies which need to be resolved. Let’s install these dependencies one by one (assuming you are using Debian distribution).

sudo apt-get update

sudo apt-get install gcc

sudo apt-get install make

sudo apt-get install libmpc-dev

sudo apt-get install libmpfr-dev

sudo apt-get install libgmp3-dev

sudo apt-get install autoconf

sudo apt-get install automake

sudo apt-get install textinfo

#optional - only needed if multilib support is required
sudo apt-get install gcc-multilib

2. Next create a separate directory ~/toolchain where all the toolchain building blocks/components will be downloaded from their FTP servers.

#creating separate directory for source codes 
mkdir -p ~/toolchain

3. Next step is to create a separate directory and sub-directories where all toolchain components will be build. This step is particularly important and tricky. Both “Binutils” and “gcc” are designed to be built from a separate/different directory. Especially gcc, will not build if you try to do it from its own source directory and may replace its own files which will cause a permanent failure in build process with current source. The same is true for “newlib”.

cd ~/toolchain

#creating separate directories for make build

mkdir -p ./build/binutils-build

mkdir -p ./build/gcc-build

mkdir -p ./build/newlib-build

mkdir -p ./build/gdb-build

Overall directory structure will look like:

Figure-2: Toolchain build directory structure

4. Now that we have directories setup appropriately, let’s download and extract all 4-components.

cd ~/toolchain

wget https://ftp.gnu.org/gnu/binutils/binutils-2.28.tar.gz

wget ftp.gnu.org/gnu/gcc/gcc-6.4.0/gcc-6.4.0.tar.gz

wget ftp://sourceware.org/pub/newlib/newlib-2.5.0.tar.gz

wget ftp.gnu.org/gnu/gdb/gdb-8.0.tar.gz

tar xzf binutils-2.28.tar.gz

tar xzf gcc-6.4.0.tar.gz

tar xzf newlib-2.5.0.tar.gz

tar xzf gdb-8.0.tar.gz

5. Now that we have the sources codes we need; its time to start compiling each component. First of all we will build “binutils”. Following are the bash commands.

cd ~/toolchain/build/binutils-build

../../binutils-2.28/configure --target=arm-none-eabi --prefix=/home/linuxlite/arm-none-eabi --with-cpu=cortex-m4  \
--with-no-thumb-interwork --with-mode=thumb

make all install 2>&1 | tee ./binutils-build-logs.log

Following are the configure script switches details:

  • –target: This switch sets the target processor for which the toolchain is being build, the build environment and the type of executable as already explained in Toolchain naming convention.
  • –prefix: switch sets the location where the binaries will be installed after successful compilation. In our case its /home/linuxlite/arm-none-eabi.
    Note: This switch must never be supplied relative paths e.g. ~/arm-none-eabi. The complete/absolute path is mandatory.
  • –with-cpu: This option specifies the name of the target processor for which GCC should tune the performance of the toolchain. In our case its ARM Cortex-m4. Other options for ARM can be find in [3].
  • –with-mode: Select an ISA for target processor. ARM Cortex-m (Our Target Processor) family support only thumb ISA so thumb is selected. ARM ISA can be selected via “marm”
  • –with-no-thumb-interwork: ARM pre-v5 architectures processors used both ARM and thumb instructions and the processor can switch between ARM instruction mode and thumb instruction mode on run-time. As Cortex-m family only supports thumb instruction set and no switching is required so we will disable ISA switching with “–with-no-thumb-interwork” switch. The facility can be enabled using “-mthumb-interwork” switch.

Once the compilation is successful, the directory /home/linuxlite/arm-none-eabi/bin (in our case) will look similar to Figure-3. Compilation will take a while depending on your system specifications – Believe me you will love that compilation screen ☺.

Figure-3: Prefix directory after binutils successful compilation

After successful compilation/installation of binutils, it is required to add the binaries path to the PATH variable as all the subsequent steps require these binaries.

export PATH="$PATH:/home/linuxlite/arm-none-eabi/bin"

Note: Remember again not to add relative path e.g. ~/arm-none-eabi. The complete path is mandatory.

6. Now that we have successfully compiled binutils, its time to cross-compile gcc source code for ARM. Following are the bash commands.

cd ~/toolchain/build/gcc-build

../../gcc-6.4.0/configure --target=arm-none-eabi --prefix=/home/linuxlite/arm-none-eabi --with-cpu=cortex-m4 \
--enable-languages=c,c++ --without-headers --with-newlib --with-no-thumb-interwork --with-mode=thumb

make all-gcc install-gcc 2>&1 | tee ./gcc-build-withoutnewlib-logs.log

Almost all the configure script switches are explained earlier, the remaining switches details are as under:

  • –enable-languages: The target languages support to be added to the toolchain.
  • –without-headers: Disables GCC from using the target’s Libc when cross compiling.
  • –with-newlib: Specifies that “newlib” is being used as the target C library.

Once the compilation is successful few new entries will be added to the Prefix/bin directory i.e. /home/linuxlite/arm-none-eabi/bin – Figure-4.

Figure-4: Prefix directory after gcc successful compilation

By now our cross compiler for ARM is partially build/complete – its functional except the C-Library support needs to be added to the newly build compiler. For that we need to compile the C-Library first. For this tutorial we will be using Cygnus C-Library i.e. newlib which is a C standard library implementation intended for use on embedded devices.

7. Following are the bash commands to build newlib.

cd ~/toolchain/build/newlib-build

../../newlib-2.5.0/configure --target=arm-none-eabi --prefix=/home/linuxlite/arm-none-eabi \
--disable-newlib-supplied-syscalls

make all install 2>&1 | tee ./newlib-build-logs.log

–disable-newlib-supplied-syscalls: This switch prevents newlib to add default system calls stubs being compiled with newlib i.e. by default newlib will add some debug friendly stubs for printf like functions. By using this switch we can disable deafult printf like functions code and redirect it to desired medium. As we are compiling for bare-metal code so no system-call is required.

Now that C-Libraries (newlib) is cross-compiled successfully; it’s time to add its support to the earlier build gcc compiler to complete it.

cd ~/toolchain/build/gcc-build

../../gcc-6.4.0/configure --target=arm-none-eabi --prefix=/home/linuxlite/arm-none-eabi --with-cpu=cortex-m4 \
--enable-languages=c,c++ --with-newlib --with-no-thumb-interwork --with-mode=thumb

make all-gcc install-gcc 2>&1 | tee ./gcc-build-withnewlib-logs.log

Congratulation your toolchain is now fully functional with only one exception i.e. it doesn’t have Debugging support. So let’s add that support as well to the our newly born baby toolchain.

8. The GNU gdb has support for ARM so let’s cross-compile it and add it to our earlier build executable toolchain binaries. Following are the bash commands to cross compile gdb for ARM.

cd ~/toolchain/build/gdb-build

../../gdb-8.0/configure --target=arm-none-eabi --prefix=/home/linuxlite/arm-none-eabi

make all install 2>&1 | tee ./gdb-build-logs.log

If everything went well, the Prefix/bin directory i.e. /home/linuxlite/arm-none-eabi/bin will look similar to Figure-5.

Figure-5: Prefix directory after complete toolchain successful compilation

Congratulation you have successfully completed all the steps to cross-compile GCC toolchain for ARM Cortex-M Processors.

For motivation, a sample screen record is given in the following video ☺.

References:

[1] – Mastering Embedded Linux Programming by Chris Simmonds

[2] – LLVM Project

[2] – GCC ARM Options



26 thoughts on “Cross-Compiling GCC toolchain for ARM Cortex-M Processors”

Leave a Reply

Your email address will not be published. Required fields are marked *