Building GCC for Cortex
This tutorial explains how you can create a GCC+Newlib toolchain that can be used to compile programs for the Cortex (Thumb2) architecture, thus making it possible to use GCC to compile programs for the increasingly number of Cortex CPUs out there (Luminary Micro, ST, with new Cortex CPUs being announced by Atmel and other companies). I am writing this tutorial because I needed to work on a Cortex CPU for the eLua project and I couldn't find anywhere a complete set of instructions for building GCC for this architecture. You'll need such a toolchain if you want to compile eLua for Cortex-M3 CPUs.
DISCLAIMER: I'm by no means a specialist in the GCC/newlib/binutils compilation process. I'm sure that there are better ways to accomplish what I'm describing here, however I just wanted a quick and dirty way to build a toolchain, I have no intention in becoming too intimate with the build process. If you think that what I did is wrong, innacurate, or simply outrageously ugly, feel free to contact us and I'll make the necessary corrections. And of course, this tutorial comes without any guarantees whatsoever.
Prerequisites
To build your toolchain you'll need:
- a computer running Linux: I use Ubuntu 8.04, but any Linux will do as long as you know how to find the equivalent of "apt-get" for your distribution. I won't be going into details about this, google it and you'll sure find what you need. It is also assumed that the Linux system already has a "basic" native toolchain installed (gcc/make and related). This is true for Ubuntu after installation. Again, you might need to check your specific distribution.
- GNU binutils: get it from here. At the moment of writing this, the latest versions is 2.18, which for some weird reason refuses to compile on my system, so I'm using 2.17 instead. UPDATE: you MUST use the new binutils 2.19 distribution for the Cortex toolchain, since it fixes some assembler issues. You won't be able to compile eLua 0.5 or higher if you don't use binutils 2.19.
- GCC: since support for Cortex (Thumb2) was only introduced staring with version 4.3.0, you'll need to download version 4.3.0 or newer. As I'm writing this, the latest GCC version is 4.3.1, which I'll be using for this tutorial. Download it from here after choosing a suitable mirror.
- Newlib: as I'm writing this, the latest official Newlib version is 1.16.0. However, the CVS version contains some fixes for the Thumb2 architecture, some of them in very important functions (like setjmp/longjmp), so you'll need to fetch the sources from CVS (this will most likely change when a new official Newlib version is released). So go to http://sourceware.org/newlib/download.html and follow the instructions there in order to get the latest sources from CVS.
- Also, the tutorial assumes that you're using bash as your shell. If you use something else, you might need to adjust some shell-specific commands.
Also, you need some support programs/libraries in order to compile the toolchain. To install them:
$ sudo apt-get install flex bison libgmp3-dev libmpfr-dev autoconf texinfo build-essential |
---|
Next, decide where you want to install your toolchain. They generally go in /usr/local/, so I'm going to assume /usr/local/cross-cortex for this tutorial. To save yourself some typing, set this path into a shell variable:
$ export TOOLPATH=/usr/local/cross-cortex |
---|
Step 1: binutils
This is the easiest step: unpack, configure, build.
$ tar -xvjf binutils-2.19.tar.bz2 |
---|
$ cd binutils-2.19 |
$ mkdir build |
$ cd build |
$ ../configure --target=arm-elf --prefix=$TOOLPATH --enable-interwork --enable-multilib --with-gnu-as --with-gnu-ld --disable-nls |
$ make all |
$ sudo make install |
$ export PATH=${TOOLPATH}/bin:$PATH |
Now you have your ARM "binutils" (assembler, linker, disassembler ...) in your PATH. They are fully capable of handling Thumb2.
Step 2: basic GCC
In this step we build a "basic" GCC (that is, a GCC without any support libs, which we'll use in order to build all the libraries for our target). But first we need to make a slight modification in the configuration files. Out of the box, the GCC 4.3.1/newlib combo won't compile properly, giving a very weird "Link tests are not allowed after GCC_NO_EXECUTABLES" error. After a bit of googling, I found the solution for this:
$ tar -xvjf gcc-4.3.1.tar.bz2 |
---|
$ cd gcc-4.3.1/libstdc++-v3 |
$ joe configure.ac |
I'm using "joe" here as it's my favourite Linux text mode editor, you can use any other text editor. Now find the line which says "AC_LIBTOOL_DLOPEN" and comment it out by adding a "#" before it:
# AC_LIBTOOL_DLOPEN
Save the modified file and exit the text editor
$ autoconf |
---|
$ cd .. |
Great, now we know it will compile, so let's do it:
$ mkdir build |
---|
$ cd build |
$ ../configure --target=arm-elf --prefix=$TOOLPATH --enable-interwork --enable-multilib --enable-languages="c,c++" --with-newlib --without-headers --disable-shared --with-gnu-as --with-gnu-ld |
$ make all-gcc |
$ sudo make install-gcc |
On my system, the last line above (sudo make install-gcc) terminated with errors, because it was unable to find our newly compiled binutils. If this happens for any kind of "make install" command, this is a quick way to solve it:
$ sudo -s -H |
---|
# export PATH=/usr/local/cross-cortex/bin:$PATH |
# make install-gcc |
# exit |
Step 3: Newlib
Again, some modifications are in order before we start compiling. Because the CVS version of Newlib doesn't seem to have all the required support for Thumb2 yet, we need to tell Newlib to skip some of its libraries when compiling:
$ cd [directory where the newlib CVS is located] |
---|
$ joe configure.ac |
Find this fragment of code:
arm-*-elf* | strongarm-*-elf* | xscale-*-elf* | arm*-*-eabi* )
noconfigdirs="$noconfigdirs target-libffi target-qthreads"
libgloss_dir=arm
;;
And add "target-libgloss" to the "noconfigdirs" variable:
arm-*-elf* | strongarm-*-elf* | xscale-*-elf* | arm*-*-eabi* )
noconfigdirs="$noconfigdirs target-libffi target-qthreads target-libgloss"
libgloss_dir=arm
;;
Save the modified file and exit the text editor
$ autoconf
On one of the systems I ran the above sequence, it terminated with errors, complaining that autoconf 2.59 was not found. I don't know why that happens. 2.59 seems to be quite ancient, and the build ran equally well with 2.61 (the version of autoconf on the system that gave the error). If this happens to you, first execute autoconf --version to find the actual version of your autoconf, then do this:
$ joe config/override.m4
Look for this line:
[m4_define([_GCC_AUTOCONF_VERSION], [2.59])])
And replace [2.59] with your actual version ([2.61] in my case).
$ autoconf
Once again, now we're ready to actually compile Newlib. But we need to tell it to compile for Thumb2. As already specified, I'm not a specialist when it comes to Newlib's build system, so I chosed the quick, dirty and not so elegant solution of providing the compilation flags directly from the command line. Also, as I wanted my library to be as small as possible (as opposed to as fast as possible) and I only wanted to keep what's needed from it in the final executable, I added the "-ffunction-sections -fdata-sections" flags to allow the linker to perform dead code stripping:
$ mkdir build |
---|
$ cd build |
$ ../configure --target=arm-elf --prefix=$TOOLPATH --enable-interwork --disable-newlib-supplied-syscalls --with-gnu-ld --with-gnu-as --disable-shared |
$ make CFLAGS_FOR_TARGET="-ffunction-sections -fdata-sections -DPREFER_SIZE_OVER_SPEED -D__OPTIMIZE_SIZE__ -Os -fomit-frame-pointer -mcpu=cortex-m3 -mthumb -D__thumb2__ -D__BUFSIZ__=256" CCASFLAGS="-mcpu=cortex-m3 -mthumb -D__thumb2__" |
$ sudo make install |
Some notes about the flags used in the above sequence:
--disable-newlib-supplied-syscalls:
this deserves a page of its own, but I won't cover it here. For an explanation, see for example this page.-DPREFER_SIZE_OVER_SPEED -D__OPTIMIZE_SIZE__:
compile Newlib for size, not for speed (these are Newlib specific).-mcpu=cortex-m3 -mthumb:
this tells GCC that you want to compile for Cortex. Note that you need both flags.-D__thumb2__:
again, this is Newlib specific, and seems to be required when compiling Newlib for Cortex.-Os -fomit-frame-pointer:
tell GCC to optimize for size, not for speed.-D__BUFSIZ__=256:
again Newlib specific, this is the buffer size allocated by default for files opened via fopen(). The default is 1024, which I find too much for an eLua, so I'm using 256 here. Of course, you can change this value.
Step 4: full GCC
Finally, in the last step of our tutorial, we complete the GCC build. In this stage, a number of compiler support libraries are built (most notably libgcc.a). Fortunately this is simpler that the Newlib compilation step, as long as you remember that we want to build our compiler support libraries for the Cortex architecture:
$ cd gcc-4.3.1/build |
---|
$ make CFLAGS="-mcpu=cortex-m3 -mthumb" CXXFLAGS="-mcpu=cortex-m3 -mthumb" LIBCXXFLAGS="-mcpu=cortex-m3 -mthumb" all |
$ sudo make install |
All Done!
Phew! That was quite a disturbing tutorial, with all that confusing flags lurking in every single shell line :) But at this point you should have a fully functional Cortex GCC toolchain, which seems to be something very rare, so enjoy it with pride. If you need further clarification, or if the above instructions didn't work for you, feel free to contact us.