You can build any number of native and cross compilers from GCC, if you know how.
Introduction
There is great value these days in being able to compile code to run on multiple platforms. Software developers today might find themselves in one of the following situations:
- creating a program or library that must run on several different operating systems for example Linux, Windows, and Solaris
- developing software for an embedded device with a MIPS, PowerPC, ARM, or other CPU
The GNU Compiler Collection, or GCC [1], enables you to do all of this from the same host development system, for no more cost than a little time and elbow grease.
In this article I guide you through the steps of building and using GCC for cross-platform development, hosted under Linux. All the popular Linux distributions come standard with a "native" copy of GCC, which is needed to build the cross-compiler and other tools. (You have to start somewhere!) After showing how to build and install the tools, I present a small library with sample applications, and build them for native Linux, Win32, and embedded PowerPC EABI [2].
Getting the GCC Sources
The best way to become familiar with GCC is to build and install it yourself from the sources. Building a cross-development toolchain is just a special case of building GCC. Most often, people build GCC on the machine on which it will be run, either to supplement the vendor-supplied compiler, or to update to a newer version of GCC. In any case, self-hosted builds and cross-development builds are done from the same set of source files.
As a prerequisite to installing GCC, you must install Binutils (the GNU Binary Utilities), which includes the assembler, linker, archiver, and other utilities. You will also need appropriate header and library files to enable compilation for the target system. These files may be vendor supplied, but for this article I use the Newlib (see below) library headers for PowerPC, and the Mingw32 runtime for Win32.
Newlib is a small C library intended for embedded systems. There is a website for Newlib at:
http://sourceware.cygnus.com/newlib/Mingw32 is shorthand for "Minimalist GNU Win32." It is simply a set of headers and libraries that provide an interface between code built with GCC and the C runtime provided by Microsoft in Win32. You can find more information on Mingw32 on Mumit Khan's "GNU Win32 related projects" page:
http://www.xraylith.wisc.edu/~khan/software/gnu-win32/and Colin Peters' "GNU Compilers on Win32" page:
http://www.geocities.com/Tokyo/Towers/6162/gcc.htmlThere are numerous ways to get hold of the Binutils and GCC sources. FTP is my favorite, but there are two alternatives you might want to consider.
First, all versions of the GNU tools, including the bleeding-edge development versions, are available via public CVS over the Internet. CVS ("Concurrent Versions System") is a free, network-enabled, repository-based version control system, all packed into a single command-line executable. Red Hat Linux includes a copy of CVS. For help getting Binutils and GCC via CVS, see the following URLs:
http://sourceware.cygnus.com/binutils/ http://egcs.cygnus.com/cvs.htmlSecond, if your Linux distribution comes with source code, you might have a copy of the Binutils and GCC sources on CD. Red Hat includes the source for its entire distribution on the second CD in the box set, in the form of "SRPMS" (Source [to] Red Hat Package Manager fileS). Note that GCC might be named "egcs" (a name for an interim version of GCC), and some of the instructions for building GCC 2.95 might differ slightly. The Newlib library is not part of Linux, nor is Mingw32, so you will have to obtain them by other means, such as FTP.
Many GNU projects produce regular "stable" releases, which are believed to be relatively stable and well tested. There are also more frequent "snapshot" releases, which usually correspond to recent CVS versions, and which may contain experimental features and untested code. You may find that you need a snapshot release to get a certain feature or bug fix (until such feature makes it into a stable release), but for this article I've chosen to stick with stable releases.
The most recent stable releases of Binutils, Newlib, and GCC are always available via FTP. As of this writing they can found at these URLs:
ftp://sourceware.cygnus.com/pub/binutils/releases/binutils-2.9.1.tar.gz ftp://sourceware.cygnus.com/pub/newlib/newlib-1.8.1.tar.gz ftp://gcc.gnu.org/pub/gcc/releases/gcc-2.95.2/gcc-2.95.2.tar.gzDownload everything to a suitable directory, such as /usr/local/source, and extract with the following commands:
$ tar xvzf binutils-2.9.1.tar.gz $ tar xvzf newlib-1.8.1.tar.gz $ tar xvzf gcc-2.95.2.tar.gzYou will end up with three new directories containing the sources, binutils-2.9.1/, newlib-1.8.1/, and gcc-2.95.2/. Each of these "source" directories (referred to as <srcdir> later) will be left untouched during the build and install steps.
If you take a few minutes to browse under gcc-2.95.2/gcc, you will find several subdirectories containing the front-end code for the different languages supported by GCC in addition to C:
cp C++ objc Objective C java Java ch Chill f Fortran 77For the purposes of this article, I show how to build just the C and C++ front ends. The Java front end is quite new and still under development. (One interesting thing to note about the Java front end is that in addition to generating Java bytecode, it will eventually be able to generate machine code for the target CPU.) The Objective C front end is a bit finicky about cross-development configurations, so I don't bother with it here. Chill and Fortran 77 should work fine, but they are beyond the scope of this article.
Before you go any further, make a top-level installation directory to hold all of the cross-development tools:
$ mkdir /usr/local/crossdevFor Win32 support, you need the Mingw32 headers and libraries. Building Mingw32 from source would involve a kind of chicken-and-egg situation, so just download the following binary runtime files:
ftp://ftp.xraylith.wisc.edu/pub/khan/gnu-win32/mingw32/runtime/bin-crtdll-1999-11-18.tar.gz ftp://ftp.xraylith.wisc.edu/pub/khan/gnu-win32/mingw32/runtime/bin-msvcrt-1999-11-18.tar.gzUnpack these two files into the crossdev directory created earlier:
$ cd /usr/local/crossdev $ tar xvzf <path-to>/bin-crtdll-1999-11-18.tar.gz $ tar xvzf <path-to>/bin-msvcrt-1999-11-18.tar.gz $ ls -F /usr/local/crossdev i386-mingw32/ i386-mingw32msvc/Building the Toolchains
When building the GNU cross-development tools, there are three distinct directories you need to keep track of:
1) The top-level directory where the sources were unpacked. Call this <srcdir>.
2) The directory where the intermediate object files for the tools go. Call this <objdir>. You can (and should) specify a different <objdir> for each cross-development target platform.
3) The installation directory where the compiler, assembler, and other executables will reside. I used /usr/local/crossdev in the above example, but I sometimes refer to it as <installdir>.
Keeping these directories distinct makes it easy to build multiple cross development toolchains side-by-side, without polluting the main source directory, <srcdir>.
I now show how to build two separate cross-development toolchains, which I refer to by their respective target specifications, powerpc-eabi and i386-mingw32.
First, create the object directories (<objdir>) for both targets, for both Binutils and GCC (four total).
$ mkdir /usr/local/build $ mkdir /usr/local/build/binutils-powerpc-eabi $ mkdir /usr/local/build/gcc-powerpc-eabi $ mkdir /usr/local/build/binutils-i386-mingw32 $ mkdir /usr/local/build/gcc-i386-mingw32For each given target platform, you must run a configure script and supply a "target specification," which is a string that specifies the CPU type, object file format, and sometimes the operating system, for which your cross-development toolchain will generate object code. This process actually involves running a configure script for each <objdir> above. Running the script sets up a makefile, which you then use by invoking "make" and "make install" from the command line.
The following commands illustrate this process. You invoke the configure script with the target specification, the installation directory (prefix), and other options:
$ cd <objdir> $ <srcdir>/configure target=<targetspec> prefix=<installdir> $ make $ make installThe C/C++ cross-compiler generates assembly code that the cross-assembler (from Binutils) assembles to object code, so you must always build Binutils first.
Building the PowerPC Toolchain
I start by showing how to build the PowerPC toolchain. To prepare the Binutils targeting the PowerPC EABI, do the following:
$ cd /usr/local/build/binutils-powerpc-eabi $ /usr/local/source/binutils-2.9.1/configure \ target=powerpc-eabi prefix=/usr/local/crossdev $ make $ make installThis process should only take a few minutes on a reasonably fast PC. If all goes well, you should find the assembler and other binary utilities installed, as shown in Figure 1.
Notice that all of the tools for a particular platform are prefixed with the target specification ("powerpc-eabi" in this case). This allows multiple cross development toolchains to coexist in the same directory without naming conflicts. So far I have shown how to create the assembler (as), linker (ld), archiver (ar), and other assorted utilities. At this point you should append to your PATH environment variable to get access to these tools:
$ export PATH=${PATH}:/usr/local/crossdev/bin(I'm assuming use of the default Linux shell, bash; use the appropriate variable syntax for other shells such as tcsh.)
Before building GCC for the PowerPC target, the Newlib header files must be copied into the powerpc-eabi installation directory, so that GCC will be able to find things like <stdio.h>:
$ cd /usr/local/source/newlib-1.8.1/newlib/libc $ cp -rpv include /usr/local/crossdev/powerpc-eabiNow build the C/C++ cross-compiler. The configure script for GCC requires a few more arguments than the one for Binutils:
$ cd /usr/local/build/gcc-powerpc-eabi $ /usr/local/source/gcc-2.95.2/configure target=powerpc-eabi prefix=/usr/local/crossdev with-headers=/usr/local/crossdev/powerpc-eabi/include with-newlib enable-languages=c,c++ $ make $ make installGCC will take longer to build than binutils. To verify that the compiler (gcc) is installed and operational, ask it what its version is:
$ powerpc-eabi-gcc -v Reading specs from /usr/local/crossdev/lib/gcc-lib/ powerpc-eabi/2.95.2/specs gcc version 2.95.2 19991024 (release)Building the Win32 Toolchain
Now build Binutils and GCC again, but this time for the i386-mingw32 target. To build Binutils:
$ cd /usr/local/build/binutils-i386-mingw32 $ /usr/local/source/binutils-2.9.1/configure target=i386-mingw32 prefix=/usr/local/crossdev $ make $ make installBuild GCC for the Win32 toolchain as follows:
$ cd /usr/local/build/gcc-i386-mingw32 $ /usr/local/source/gcc-2.95.2/configure target=i386-mingw32 prefix=/usr/local/crossdev with-headers=/usr/local/crossdev/i386-mingw32/include enable-languages=c,c++ $ make $ make install(Note! Before building GCC for i386-mingw32, you must have the mingw32 runtime files installed in /usr/local/crossdev as described earlier.)
You should now have a complete set of i386-mingw32 tools in /usr/local/crossdev/bin along with the powerpc-eabi tools. Ask the new compiler what its version is to make sure:
$ i386-mingw32-gcc -v Reading specs from /usr/local/crossdev/lib/gcc-lib/ i386-mingw32/2.95.2/specs gcc version 2.95.2 19991024 (release)Compiling and Running with GCC
To illustrate the use of the cross-development toolchains, I present a trivial CRC(32) implementation. This implementation is in the form of a library that includes both a C interface and a C++ wrapper interface that calls the C layer underneath. The library can be fed data n bytes at a time, and the current running CRC value can be retrieved at any time. I also provide sample applications using the two interfaces. Listings 1 and 2 show files crc.c and crcplus.h, which contain the CRC implementation and C++ wrapper class respectively. Listings 3 and 4 show the sample C and C++ applications respectively.
The first thing to do is to verify that the CRC library works, by building the sample C application as a native Linux program:
$ gcc -c crc.c $ gcc -c crcplus.cpp $ ar -r libcrc.a crc.o crcplus.o $ gcc -c app1.c $ gcc -o app1 app1.o libcrc.a $ ./app1 CRC result=0xac933f5f(Note that the C++ wrapper is built into the library, but is not used by the C application.)
Also build and run the C++ version as a native Linux program:
$ gcc -c app2.cpp $ gcc -o app2 app2.o libcrc.a -lstdc++ $ ./app2 CRC result=0xac933f5fNow you can build the library and sample apps for the Mingw32 target, using the cross-development versions of the compiler and archiver:
$ i386-mingw32-gcc -c app1.c -o app1_mingw32.o $ i386-mingw32-gcc -c crc.c -o crc_mingw32.o $ i386-mingw32-gcc -c crcplus.cpp -o crcplus_mingw32.o $ i386-mingw32-ar -r libcrc_mingw32.a crc_mingw32.o \ crcplus_mingw32.o $ i386-mingw32-gcc -o app1_m32.exe app1_mingw32.o \ libcrc_mingw32.a $ i386-mingw32-gcc -c app2.cpp -o app2_mingw32.o $ i386-mingw32-gcc -o app2_m32.exe app2_mingw32.o \ libcrc_mingw32.a -lstdc++If you copy app1_m32.exe and app2_m32.exe to a Windows machine (via ftp or Samba) and run them, you should get the same result as under Linux:
C:\>app1_m32.exe CRC result=0xac933f5f C:\>app2_m32.exe CRC result=0xac933f5fThe library is built as follows for the PowerPC target:
$ powerpc-eabi-gcc -c crc.c -o crc_powerpc.o $ powerpc-eabi-gcc -c crcplus.cpp -o crcplus_powerpc.o $ powerpc-eabi-ar -r libcrc_powerpc.a crc_powerpc.o \ crcplus_powerpc.oAt this point, you need an embedded operating system, or at least a suitable library with I/O facilities if you want to produce any output.
I tested the PowerPC version by compiling app1.c, and linking it with the eCos [3] target libraries and libcrc_powerpc.a, with a version of the powerpc-eabi toolchain that comes with the eCos distribution. I ran the resulting code on a Motorola MPC821-based development board, and it worked as expected, sending "CRC result=0xac933f5f" to the debugger console.
Conclusion
In the embedded space, it is common for developers to deliver "black-box" object and library code to clients or partner companies. Chances are good that you can build a GNU cross-development toolchain that will be compatible with their target system. Find out what the target CPU is, and if they are using ELF or COFF for an object file format, then study the GCC documentation and see if you can figure out a compatible target specification for a cross compiler. On the desktop, it is possible to cross-compile programs for what is currently the most popular platform, Win32.
Thus, using Linux as your development platform, armed with the GCC sources, you have the capability and flexibility to support your entire enterprise.
There are a few caveats to keep in mind. First, you should always install C and C++ headers compatible with the target system in your <installdir>/<target-spec>/include directory. You want to make sure that the implementation of Standard C library objects and other structures is consistent so that when your code is linked with the main program code there are no surprises. Second, keep in mind that different C++ compilers intentionally differ in the way they perform name decoration (mangling). You might be able to cross-compile C++ code, but the rest of the code that it is linked with must follow the same conventions. This effectively means that for C++ object code you need to use GCC at link time, unless you export your API with a C interface.
Resources
I tried to give a decent mix of "what," "why," and "how" in this article. The CrossGCC FAQ supplies some additional "how," at http://www.objsw.com/CrossGCC/.
The GCC home page is http://egcs.cygnus.com/.
You may have noticed that about half the URLs in this article end in .cygnus.com. Cygnus Solutions does a great deal of development of the GNU tools, and they offer commercial support contracts. Their URL is http://www.cygnus.com/.
Notes
[1] GCC used to stand for GNU C Compiler, but it supports several languages now, hence the name change.
[2] The PowerPC "Embedded Application Binary Interface" is used by many PowerPC cross-development toolchains. You can grab the specification at http://www.mot.com/SPS/ADC/pps/download/8XX/ppceabi.pdf.
[3] eCos is a free, portable real-time OS developed primarily by Cygnus. Its website is http://sourceware.cygnus.com/ecos/.
Jamie Guinan runs Blue Button Solutions, Inc., providing system development and programming services specializing in Linux and other free software. When he's not in front of a computer, he enjoys spending time with his wife and cats, and someday kids. You can reach him at guinan@bluebutton.com or http://www.bluebutton.com.