EDIT : do not accidentally press ctrl-c. It exists menu-config immediately.

Important links:

These are brief instructions for creating your own GCC based tool-chain for a Cortex-M4 microcontroller, heavily based on this post. I tried a few precompiled ones which I found on the Internet, but always wondered how to make one which would be configured specifically for my micro, not for “ARM” in general. Tool-chains generated by the following method was tested by me on ST STM32F407 and Texas Instruments TIVA-C TM4C123 (i.e. one tool-chain for these two µC since they both include the same CPU). My setup as I write this:

  • Host operating system : Ubuntu 14.04 - 16.10
  • Kernel : 3.13.0-24-generic - 4.8.0-30-generic
  • Few GB of free space on HD.

Making a toolchain is hard, therefore wise people over the net developed tools to simplify the process. Few years ago, when I attempted to build a GCC tool-chain I struggled with lack of information, complexity of the process, and variety of recipes, which all seemed were extremely complex, and in some point in the process I was struck with problem I couldn’t solve. Then I found crosstool-NG - it may seem funny, but all this stuff was new to me, and I was looking for the best way possible to finish the task, some “canonical” way of building a cross-compiler, and for me, crosstool-NG is exactly this. Lets grab the newest version from its website and follow the installation instructions (this step will build only the crosstool-NG itself, read the EDIT note below before doing this):

mkdir my-toolchain
cd my-toolchain

# Pay attention which version is the newest. As of writing this, the newest was
# 1.19.0, but at the "header-file" 
# incorrectly indicated the 1.18.0 version

tar jxvf crosstool-ng-1.19.0.tar.bz2 
cd crosstool-ng-1.19.0/

# Resolve some dependencies. EDIT Ubuntu 15.04 wants libtool-bin as well.
sudo apt-get install bison flex gperf texinfo gawk libtool libtool-bin automake libncurses5-dev libexpat-dev help2man

# Provide a prefix to some destination which PATH points to.
./configure --prefix=/home/iwasz/local/
make install

EDIT (2016-12) It seems, that the newest official release available today is 1.22, which is more than year old. The older ct-ng is, the older GCC, and libraries it provides, which may cause a problems on newer systems. For example making a cross-gcc version 5.3 using ct-ng version 1.22 on Ubuntu 16.10 (which uses GCC 6.2) resulted in compilation error during GCC stage. Thus I think, that the best option is to start with development ct-ng from github repo. Instructions are here but basically you only need to:

git clone
cd crosstool-ng
./configure --prefix=/home/iwasz/local/
make install

Now we perform some setup. All features of our future tool-chain will be set during this step:

# cd back, so we are in "my-toolchain" directory again.
cd ..
mkdir staging
cd staging
ct-ng  menuconfig

The last command brings the following menu-config tool:


Paths and misc options

  • Try features marked as EXPERIMENTAL : Y
  • Prefix : ${HOME}/local/share/${CT_TARGET} . Provide a destination folder that suits your needs, give descriptive name if you plan to host more than one crosscompilers.
  • Number of parallel jobs : 8 (depends on host capabilities of course).
  • Uncheck “Render the toolchain read-only” (I find it annoying to have read only directory in my stuff, it’s problematic to delete it later, you have to chmod etc).
  • Check “Debug Crosstool-NG”, “Save intermediate steps”, and “gzip saved states” as described here.


Target options

  • Target Architecture : arm
  • (cortexm4) Suffix to the arch-part (breaks the build!). EDIT : tried with this turned on in 1.21.0, and works.
  • Use the MMU : N

The following 3 options can be somewhat tricky because when you set “ Emit assembly for CPU”, the two other (being “Architecture level” and “ Tune for CPU”) will disappear. So either you set the first mentioned one or two other.

  • Architecture level (correspods to -march flag). For Cortex-M4 use armv7e-m+fp (armv7-m for older compilers, probably before GCC-4.9). Pay attention that every architecture type has its own additional parameters which can be obtained from the man page. In this case I used “+fp” which stands for “the single-precision VFPv4 floating-point instructions”, just to be sure that hard-fp assembly instructions are emmited. EDIT : armv6-m for Cortex-M0. You can always check it in the “programming manual” of every STM32 part in chapter entitled like “About the STM32 Cortex-M0 processor and core peripherals”
  • Tune for CPU (-mtune) : use cortex-m4 or wathever you are using. Check the man page.
  • Emit assembly for CPU (-mcpu). Use this instead of the two above if you preffer, but remember, that GCC will then try to automatically figure out what architecture and tune flags to use. The values are the same as for -mtune, so we can use cortex-m4 for Cortex-M4 (full list of available options can be found in GCC manual somewhere near -mcpu phrase). Or here.
  • Use specific FPU : fpv4-sp-d16. Cortex-M4 can have FPU, but not necessarily (with FPU it is called Cortex-M4F, and Cortex-M4 without). But the fact is I found this option somewhere over the net, and I am a little bit confused on the topic of FPU. For STM32H7 you would use fpv5-d16 for double precision and fpv5-sp-d16 for single precision.
  • Floating point : hardware (FPU). EDIT : M0 have no FPU.
  • Default instruction set mode (thumb).


Toolchain options.

  • Add some cool Toolchain ID string.
  • Set “Tuple’s vendor string” to none.


Operating System.

  • Set Target OS to bare-metal:


Binary utilities

  • Binary format: ELF
  • binutils version (2.22) - latest which is not marked as EXPERIMENTAL. EDIT just recently I went with 2.24 (EXPERIMENTAL), and everything seems to be OK.

binary utilities

C compiler

  • Show Linaro versions : Y
  • gcc version (linaro-4.8-2013.06-1)
  • C++ : Y

C compiler


  • C library (newlib)
  • newlib version (2.0.0 (EXPERIMENTAL)) - the newest, and works OK.
  • Disable the syscalls supplied with newlib : Y - I provide my own syscalls in every program. BTW I had some problems when his option was checked (crt0 missing?)

C library config


  • gdb : Y

Debugging options

Then descend into “GDB” menu and check and choose the newest from linaro, and set Enable python scripting to N (caused build problems for me) EDIT : qtcreator requires python support in GDB:

Gdb config

Exit menu-config (few times ESC, and save when prompted) and finally build the toolchain:

ct-ng build
tail -f build.log # in another console (not necessary if debug options were set)

The build process takes some time (30-60 minutes), and if in some point for some reason the build fail, first place you check is the build.log file in staging directory (therefore I pasted this tail -f command earlier, but of course it does not matter how you display the file). For example, in my case, the crosstool-NG decided to fail with this:

... kilobytes, megabytes of logs ....
[ALL  ]    checking whether to use python... yes
[ALL  ]    checking for python... /usr/bin/python
[ALL  ]    checking for python2.7... no
[ERROR]    configure: error: python is missing or unusable
[ERROR]    make[2]: *** [configure-gdb] Error 1
[ALL  ]    make[2]: Leaving directory `/home/iwasz/Documents/my-toolchain/staging/.build/arm-unknown-eabi/build/build-gdb-cross'
[ERROR]    make[1]: *** [all] Error 2
[ALL  ]    make[1]: Leaving directory `/home/iwasz/Documents/my-toolchain/staging/.build/arm-unknown-eabi/build/build-gdb-cross'
[ERROR]  >>
[ERROR]  >>  Build failed in step 'Installing cross-gdb'
[ERROR]  >>        called in step '(top-level)'
[ERROR]  >>
[ERROR]  >>  Error happened in: CT_DoExecLog[scripts/functions@257]
[ERROR]  >>        called from: do_debug_gdb_build[scripts/build/debug/]
[ERROR]  >>        called from: do_debug[scripts/build/]
[ERROR]  >>        called from: main[scripts/]
[ERROR]  >>
[ERROR]  >>  For more info on this error, look at the file: 'build.log'
[ERROR]  >>  There is a list of known issues, some with workarounds, in:
[ERROR]  >>      '/home/iwasz/local/share/doc/crosstool-ng/ct-ng.1.19.0/B - Known issues.txt'
[ERROR]  (elapsed: 58:52.70)

I didn’t thought long on this one (apt-get install libpython2.7-dev maybe???), but disabled the python support for GDB (I modified the instructions accordingly, so hopefully you haven’t had the same error). But in case you had, you should resolve the error (maybe change the configuration using menuconfig, or resolve the problem in other ways, depending on the cause) and rerun ct-ng, or refer to this stack-overflow thread for more info on speeding up the process after build has failed.

Edit Feb 2015 : I recently made cross-compiler x86_64 -> i686 to be able to make 32bit binaries on 64bit box. Statically linked binaries made with it crashed with message:

FATAL: kernel too old

Following suggestions found here, I found that indeed, my binaries were (output of file command):

ELF 32-bit LSB  executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.15.4, not stripped

But my uname -a is:

Linux xxx 3.13.0-44-generic #73-Ubuntu SMP Tue Dec 16 00:22:43 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

The solution is to instruct crosstool-ng so it compile glibc/eglibc with older kernel support. Invoke ct-ng menuconfig and:

  1. Go into “C-library”
  2. Go into “Minimum supported kernel version (Specific kernel version) —>”
  3. Check “(X) Specific kernel version”
  4. “ESC ESC” and make sure that : “(2.6.9) Minimum kernel version to support”

EDIT (mar 2016). For the second time I encountered an error like this:

[ALL  ]    /usr/bin/install: cannot stat '.../.build/src/newlib-linaro-2.2.0-2015.01/libgloss/arm/linux.specs': No such file or directory

According to this, and especially this , the error is caused by some bug in newlib itself. User ‘ bhundven sugested that suffix ‘hf’ to ‘eabi’ (making it ‘eabihf’) is causing problems, so I turned off ‘Target options —> append ‘hf’ to the tuple (EXPERIMENTAL)’, and it helped.

EDIT (Jul 2018). Toolchain for Cortex-M0 was successfully tested on BlueNRG-1 and BlueNRG-2 IC’s, and works just fine.

Special remarks for LPC812 (March 2020)

This µC has so tiny amount of RAM and flash, that normal version of newlib is to heavy for it and compiler made using above instructions would not be able to produce a simple hello-world program that would fit in 16k of flash.

A glimpse into one of CMakeLists.txt provided by NXP as an example, shows that they use nano.specs, which instruct GCC to use libc_nano.a and libg_nano.a (when -g) instead of their full counterparts. I have been unsuccessful assembling a toolchain which would include libraries with those exact names (I mean with nano suffixes) but I finally found a way to compile the lighter libraries that would fit on the LPC812 instead of the usual ones. So even if they have usual names like libc.a, they are smaller and lighter, just like libc_nano.a would be. Here are the options:

New lib nano configuration options