ARM GCC toolchain build

I recently needed an ARM toolchain for a Luminary Cortex M3 microcontroller, and found that much of the information out there was aggravatingly vague and fragmentary, or simply out of date. So, to save others some trouble, here’s the process I used to put together an ARM toolchain using GCC 4.5.0, applicable to both Mac OS X and Linux.

First, some notes about my setup…I like to put my entire toolchain in a subdirectory of my home directory…for example, ~/arm-eabi-none/, with an entry like this in a startup file like .zshrc:

    export PATH=~/arm-none-eabi/bin:$PATH

This allows quick and easy swapping of toolchains by just renaming directories. If you’re building a new toolchain, you can just rename the other to keep it around while you test the new one. So, to start with, add a line like the above to ~/.zshrc, ~/.bashrc, or whatever is appropriate for the shell you use. Mac OS X and most Linux distros use bash by default, I prefer zsh. The following will append that line to .zshrc, creating the file if it doesn’t already exist (modify as needed for the shell you use):

    echo "export PATH=~/arm-none-eabi/bin:\$PATH" >>.zshrc

Now, either run “export PATH=~/arm-none-eabi/bin:$PATH” command or start a fresh terminal so your path is set up before continuing, and create some directories in your home directory:

    cd ~
    mkdir arm-eabi-none arm_build
    cd arm_build

To build a GCC toolchain, you need GMP, MPFR, and MPC. These may be already installed on your system, if not, you’ll need to install them. If you use a package management system, that may simplify things. You may also want to install them on your system, or build them specifically for your GCC toolchain. I chose the last approach, statically linking them into the toolchain to avoid disturbing any existing installs and to reduce dependencies on the system.

While we’re at it, lets get some other stuff we’ll need. You’ll also need binutils, gcc-core and optionally g++, newlib, gdb, and openocd.
GMP can be obtained from http://gmplib.org/, MPFR from http://www.mpfr.org/, and MPC from http://www.multiprecision.org/. The versions I used are gmp-5.0.1, mpfr-2.4.2, and mpc-0.8.1. Download and decompress these:

    wget ftp://ftp.gmplib.org/pub/gmp-5.0.1/gmp-5.0.1.tar.bz2 http://www.mpfr.org/mpfr-current/mpfr-2.4.2.tar.bz2 http://www.multiprecision.org/mpc/download/mpc-0.8.1.tar.gz
    tar -xjf gmp-5.0.1.tar.bz2
    tar -xjf mpfr-2.4.2.tar.bz2
    tar -xzf mpc-0.8.1.tar.gz

And the GCC toolchain…pick an appropriate mirror from http://www.gnu.org/prep/ftp.html and do the same for binutils, gcc-core, g++ if you’re using C++, and gdb. I used gcc-core-4.5.0, gcc-g++-4.5.0, binutils-2.20, and gdb-7.1:

    wget http://ftp.wayne.edu/pub/gnu/binutils/binutils-2.20.tar.bz2 http://ftp.wayne.edu/pub/gnu/gcc/gcc-4.5.0/gcc-core-4.5.0.tar.bz2  http://ftp.wayne.edu/pub/gnu/gcc/gcc-4.5.0/gcc-g++-4.5.0.tar.bz2 http://ftp.wayne.edu/pub/gnu/gdb/gdb-7.1.tar.bz2
    tar -xjf binutils-2.20.tar.bz2
    tar -xjf gcc-core-4.5.0.tar.bz2
    tar -xjf gcc-g++-4.5.0.tar.bz2
    tar -xjf gdb-7.1.tar.bz2

(if you’re wondering where g++ went, it untarred into the gcc-4.5.0 directory)
And newlib, from ftp://sources.redhat.com/pub/newlib/index.html. I used newlib-1.18.0:

    wget ftp://sources.redhat.com/pub/newlib/newlib-1.18.0.tar.gz
    tar -xzf newlib-1.18.0.tar.gz

Now, to build GMP, MPFR, and MPC. Again, I’m building temporary copies and linking them statically, you can skip this step if you want to use existing installed versions, or you can install them system-wide instead.

    mkdir gmp-5.0.1/build; cd gmp-5.0.1/build
    ../configure --prefix=$HOME/arm_build/gmp --disable-shared --enable-static
    make; make install
    cd ../../
    mkdir mpfr-2.4.2/build; cd mpfr-2.4.2/build
    ../configure --prefix=$HOME/arm_build/mpfr --with-gmp=$HOME/arm_build/gmp --disable-shared --enable-static
    make; make install
    cd ../../
    mkdir mpc-0.8.1/build; cd mpc-0.8.1/build
    ../configure --prefix=$HOME/arm_build/mpc --with-gmp=$HOME/arm_build/gmp --with-mpfr=$HOME/arm_build/mpfr --disable-shared --enable-static
    make; make install
    cd ../../

There’s some “make check” targets you should run as well to make sure things are working, though I omitted them from the above.

Finally, to the GCC toolchain. First, you need to build binutils, but the version downloaded above is slightly broken. Open binutils-2.20/gas/as.h and change line 241,

    #define know(p)			/* know() checks are no-op.ed  */

to:

    #define know(p)	do {} while(0)		/* know() checks are no-op.ed  */

When compiling this on my 64-bit Mac Pro, I also had to make some changes to binutils-2.20/binutils/strings.c to disable use of some deprecated data types and functions. This was not necessary on the 32 bit OS X and Linux systems I’ve built this on, and I don’t know if it’s needed on 64 bit Linux systems. There’s probably a cleaner way to do it, but I just commented out the relevant branches of the #ifdefs from lines 83 to 96:

    //#ifdef HAVE_FOPEN64
    //typedef off64_t file_off;
    //#define file_open(s,m) fopen64(s, m)
    //#else
    typedef off_t file_off;
    #define file_open(s,m) fopen(s, m)
    //#endif
    //#ifdef HAVE_STAT64
    //typedef struct stat64 statbuf;
    //#define file_stat(f,s) stat64(f, s)
    //#else
    typedef struct stat statbuf;
    #define file_stat(f,s) stat(f, s)
    //#endif

Now, go on and build binutils:

    mkdir binutils-2.20/build; cd binutils-2.20/build
    ../configure --prefix=$HOME/arm-none-eabi --target=arm-none-eabi --enable-interwork --enable-multilib --disable-nls --disable-shared --disable-threads --with-gcc --with-gnu-as --with-gnu-ld
    make; make install
    cd ../../

Building newlib for ARM requires an ARM GCC compiler, but building a full ARM GCC compiler requires newlib. You need to build an intermediate version of GCC using the –without-headers option to let it know it can’t rely on libc headers existing. Also, c++ support is unnecessary for this. Note the different make targets, “make all-gcc” and “make install-gcc”!

    mkdir gcc-4.5.0/build; cd gcc-4.5.0/build
    ../configure --prefix=$HOME/arm-none-eabi --target=arm-none-eabi --enable-interwork --enable-multilib --enable-languages=c --with-newlib --disable-nls --disable-shared --disable-threads --with-gnu-as --with-gnu-ld --with-gmp=$HOME/arm_build/gmp --with-mpfr=$HOME/arm_build/mpfr --with-mpc=$HOME/arm_build/mpc --without-headers
    make all-gcc; make install-gcc
    cd ../../

Next, build newlib:

    mkdir newlib-1.18.0/build; cd newlib-1.18.0/build
    ../configure --prefix=$HOME/arm-none-eabi --target=arm-none-eabi --enable-interwork --enable-multilib --with-gnu-as --with-gnu-ld --disable-nls
    make; make install
    cd ../../

Now that newlib’s built, you can build the full GCC:

    cd gcc-4.5.0
    rm -rf build
    mkdir build; cd build
    ../configure --prefix=$HOME/arm-none-eabi --target=arm-none-eabi --enable-interwork --enable-multilib --enable-languages=c,c++ --with-newlib --disable-nls --disable-shared --disable-threads --with-gnu-as --with-gnu-ld --with-gmp=$HOME/arm_build/gmp --with-mpfr=$HOME/arm_build/mpfr --with-mpc=$HOME/arm_build/mpc
    make; make install
    cd ../../

Now, that’s enough to allow you to compile programs, but you will need a debugger as well. Compile GDB:

    mkdir gdb-7.1/build; cd gdb-7.1/build
    ../configure --prefix=$HOME/arm-none-eabi --target=arm-none-eabi --disable-nls --with-gmp=$HOME/arm_build/gmp --with-mpfr=$HOME/arm_build/mpfr --with-mpc=$HOME/arm_build/mpc --with-libexpat
    make; make install
    cd ../../

Note that libexpat must be specified to get full support for things like memory maps sent to GDB from OpenOCD. Make sure it took…GDB will happily continue the configure and build process without XML support without telling you if it is unable to find or use the library, only giving you a “warning: Can not parse XML memory map; XML support was disabled at compile time” message at run time. You should get the following somewhere in your build output when you do make:

    checking for libexpat... yes
    checking how to link with libexpat... -lexpat
    checking for XML_StopParser... yes

OpenOCD:
I keep OpenOCD separate from my GCC toolchain, again installing it to ~/openocd so I can easily swap versions. You can get the latest release from http://developer.berlios.de/projects/openocd, or check it out of the Git repository with:

    git clone git://openocd.git.sourceforge.net/gitroot/openocd/openocd

The release has the advantage of reliability, but I used the latest version in the repository.

Now you’ll want to configure OpenOCD to use the JTAG device you’re using. If you’re using Mac OS X and one of the common FTDI based JTAG interfaces, you’ll want to install libftdi (http://freshmeat.net/projects/libftdi/) first, and use –enable-ft2232_libftdi. I have not had luck getting the FTD2XX direct drivers to work, OpenOCD will run for a bit and then crash. Run “configure –help” to get a list of other supported JTAG devices. I also had to install libusb, which can be obtained from http://www.libusb.org/.

    cd openocd
    ./bootstrap
    mkdir build; cd build
    ../configure --enable-maintainer-mode --prefix=$HOME/openocd --enable-ft2232_libftdi
    make; make install

You will also need to add OpenOCD to your path:

    echo "export PATH=~/openocd/bin:\$PATH" >>~/.zshrc

4 comments

  1. Andrew Capon says:

    Hi,

    Thanks for this post,

    I am new to all this ARM stuff and am trying to use this build of gcc with a mBed and basic CMIS setup on OS X. The bin that is produced will not run on the mBed.

    The same project built with gcc from sourcery gcc lite built on a pc works fine, so I think it must be something to do with the gcc I have built on the mac.

    I do not have the expertise to work out why this may be and wondered if you may have some pointers for me.

    Thanks for any help

    Andy

    • cjh says:

      I’ve not done anything with mbed, and the given information doesn’t give me any ideas about why it would have problems…what are the specific GCC versions involved? Can you step through it with a debugger and see where it’s failing?

  2. Edgar says:

    Hi,

    Thanks for this excellent information.

    I just wanted to add that you can get a working OpenOCD from Mac Ports.
    So far I have been able to used ARM-USB-JTAG from Olimex, FTDI, and Jlink.
    Just be aware that Mac Ports installs everything on /opt/local.

    Cheers,

    Edgar

    • cjh says:

      MacPorts is certainly a faster and more sure way to get going if you can find a port you need. I simply dislike being at the mercy of the port maintainer’s updates, or having to wrestle the port management system into submission to get it to install my own version.

Leave a Reply

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