I do a lot with embedded systems. Each new platform needs a compiler. Normally gcc is used for this, being a pretty good and very free compiler; unfortunately, building it for a particular target is annoyingly and needlessly hard.

(Rant follows: stupidly hard. The installation instructions are difficult to find and lousy, there are usually bugs that cause it to fail to build out of the box, and there's no assistance available.)

Here's a brief walk through rebuilding gcc to target an embedded platform with an ARM processor.

Firstly, you need a copy of both GNU binutils (which contains an assembler, linker, etc) and GNU gcc itself. You can get them here.

GNU binutils download site

I recommend 2.19.1, which is known to work.

GNU gcc download site

I recommend 4.3.3, which is known to work. Get the gcc-core version, which just contains the C compiler, and not things like Fortran or Ada.

(Important note: as of writing this article, the automatic mirror selector that I link to above would occasionally send me to a compromised web site. Idiots. If your web browser complains about this, hit BACK and try again --- you'll get a proper mirror.)

The next thing you need to do is to decide on the target you want to build for. gcc uses, of course, a needlessly complex naming scheme for targets that distinguishes between all the various slightly different ABIs. For embedded systems, this is all irrelevant. All you need to know is that the target name is usually processor-format. Our platform is going to be arm-elf. The following processors are available in gcc 4.3.3:

alpha arc arm avr bfin cris crx fr30 frv h8300 i386 ia64 iq2000 m32c m32r m68hc11 m68k mcore mips mmix mn10300 mt pa pdp11 rs6000 s390 score sh sparc spu stormy16 v850 vax xtensa

format is nearly always going to be elf, although occasionally you might want coff.

Now the prerequisites. Apart from the usual toolchain and development sets for your platform,

Right, now you're ready to build them. First, decompress them. (Change the version numbers in the following examples to match whatever you downloaded.)

$ cd /tmp
$ mkdir build
$ cd build
$ tar xjf binutils-2.19.1.tar.bz2
$ tar xjf gcc-core-4.3.3.tar.bz2

Now here's the cunning trick without which you are lost: both binutils and gcc are designed to be built from a different directory. gcc, in particular, will not build if you try to do it from its own directory --- and if you try it will subtly poison its own source and fail to build ever again. The documentation does not mention this.

First, binutils:

$ mkdir binutils-obj
$ cd binutils-obj
$ ../binutils-2.19.1/configure --target=arm-elf --prefix=/usr/local
[output]
$ make CFLAGS="-Os -w"
[output]
$ sudo make install
$ cd ..

Note the argument to --prefix --- this indicates where you want to install everything. /usr/local is common. If you don't have sudo privileges you'll need to install into your home directory; $HOME is perfectly acceptable, but be aware that gcc tends to spew stuff all over the place.

What's the CFLAGS setting for? Well, the binutils authors decided to use the compiler option that causes warnings to be treated as errors. For development, this is a good idea, because it forces people to fix warnings. For release code, it's a really bad idea, because different compilers emit different warnings. Newer versions of gcc have added more warnings, and so binutils won't compile out of the box on it. Changing CFLAGS like this works around the problem.

Hopefully now binutils should be compiled and installed. Let's move on to gcc.

$ mkdir gcc-obj
$ cd gcc-obj
$ ../gcc-4.3.3/configure --target=arm-elf --prefix=/usr/local --enable-multilib --disable-libssp
[output]
$ make CFLAGS="-Os -w"
[output]
$ make install
$ cd ..

Pretty much the same procedure as for binutils, and deceptively simple-looking...

--enable-multilib tells gcc to build versions of its run-time library for all different configurations of the target. In particular, for ARM this means you'll get a libgcc for both ARM and Thumb. For embedded use, this is what you want.

--disable-libssp disables the stack-smashing protection library. This is because it doesn't build.

And now, if all that worked... you should be ready to go. Test it:

$ cat > test.c
int foo(void) { return 42; }
^D
$ arm-elf-gcc -Os -S test.c
$ cat test.s
	.file	"test.c"
	.text
	.align	2
	.global	foo
	.type	foo, %function
foo:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	mov	r0, #42
	bx	lr
	.size	foo, .-foo
	.ident	"GCC: (GNU) 4.3.3"

And if it didn't work... well, sorry. Probably you've hit one of the known fails-to-build bugs; try the error message in Google and with luck you'll get a workaround. The above recipe works for me, most of the time, and so I'm sticking with it. Why the gcc team don't clean up their act and produce something that works a bit more reliably I don't know.