Team LiB   Previous Section   Next Section

4.2 GNU Cross-Platform Development Toolchain

The toolchain we need to put together to cross-develop applications for any target includes the binary utilities, such as ld, gas, and ar, the C compiler, gcc, and the C library, glibc. The rest of the discussion in the later chapters relies on the cross-platform development toolchain we will put together here.

You can download the components of the GNU toolchain from the FSF's FTP site at ftp://ftp.gnu.org/gnu/ or any of its mirrors. The binutils package is in the binutils directory, the gcc package is in the gcc directory, and the glibc package is in the glibc directory along with glibc-linuxthreads. If you are using a glibc version older than 2.2, you will also need to download the glibc-crypt package, also from the glibc directory. This part of the library used to be distributed separately, because U.S. cryptography export laws made it illegal to download this package to a computer outside the U.S. from the FSF's site, or any other U.S. site, for that matter. Since Version 2.2, however, glibc-crypt has been integrated as part of the main glibc package and there is no need to download this package separately anymore.[2] Following the project directory layout suggested earlier, download the packages into the ${PRJROOT}/build-tools directory.

[2] The following email from the glibc developer mailing list covers the folding of glibc-crypt into the main glibc package and conformance to U.S. export laws: http://sources.redhat.com/ml/libc-alpha/2000-02/msg00104.html. This email, and the ensuing thread, refer to the "BXA" abbreviation. This is the Bureau of Industry and Security of the U.S. Department of Commerce (http://www.bxa.doc.gov/). It is known as the BXA, because it was formerly the Bureau of Export Administration.

Note that all the targets discussed in Chapter 3 are supported by the GNU toolchain.

4.2.1 GNU Toolchain Basics

Configuring and building an appropriate GNU toolchain is a complex and delicate operation that requires a good understanding of the dependencies between the different software packages and their respective roles. This knowledge is required, because the GNU toolchain components are developed and released independently from one another.

4.2.1.1 Component versions

The first step in building the toolchain is selecting the component versions we will use. This involves selecting a binutils version, a gcc version, and a glibc version. Because these packages are maintained and released independently from one another, not all versions of one package will build properly when combined with different versions of the other packages. You can try using the latest versions of each package, but this combination is not guaranteed to work either.

To select the appropriate versions, you have to test a combination tailored to your host and target. Of course, you may find it easier to ask around and see whether someone somewhere tested a certain combination of versions for that setup and reports that her combination works correctly. You may also have to try such combinations for your setup on your own if you do not find a known functional version combination. In that case, start with the most recent stable versions of each package and replace them one by one with older ones if they fail to build.

In some cases, the version with the highest version number may not have had the time to be tested enough to be considered "stable." At the time glibc 2.3 was released, for example, it may have been a better choice to keep using glibc 2.2.5 until 2.3.1 became available.

At the time of this writing, for instance, the latest version of binutils is 2.13.2.1, the latest version of gcc is 3.2.1, and the latest version of glibc is 2.3.1. Most often, binutils will build successfully and you will not need to change it. Hence, let us assume that gcc 3.2.1 fails to build although all the appropriate configuration flags have been provided. In that case, I would revert to gcc 3.2. If that failed, I would try 3.1.1 and so on. It is the same thing with glibc. Version 2.3.1 of glibc may fail to build. In that case, I would revert to 2.3 and later to 2.2.5, if necessary.

You must understand, however, that you cannot go back like this indefinitely, because the most recent package versions expect the other packages to provide certain capabilities. You may, therefore, have to go back to older versions of packages that you successfully built if the other packages down the line fail to build. Using the above versions, for example, if I had to go back to glibc 2.1.3, it might be appropriate to change back to gcc 2.95.3 and binutils 2.10 although the most recent gcc and most recent binutils may have compiled perfectly.

You may also need to apply patches to some versions to get them to properly compile for your target. The web sites and mailing lists provided for each processor architecture in Chapter 3 are the best place to find such patches and package versions suggestions. Another place to look for patches is in the Debian source packages. Each package contains the patches required for all the architectures supported by that package.

Table 4-2 provides a list of known functional version combinations. For each host/target combination, known compatible versions are provided for binutils, gcc, and glibc. The last column indicates whether the tools require patching.

Table 4-2. Known functional package version combinations

Host

Target

Kernel

binutils

gcc

glibc

Patches

i386

PPC

 

2.10.1

2.95.3

2.2.1

No

PPC

i386

 

2.10.1

2.95.3

2.2.3

No

PPC

i386

 

2.13.2.1

3.2.1

2.3.1

No

i386

ARM

2.4.1-rmk1

2.10.1

2.95.3

2.1.3

Yes[3]

PPC

ARM

 

2.10.1

2.95.3

2.2.3

Yes[3]

i386

MIPS

 

2.8.1

egcs-1.1.2

2.0.6

Yes[4]

i386

SuperH

 

2.11.2

3.0.1

2.2.4

Yes[5]

Sparc (Solaris)

PPC

2.4.0

2.10.1

2.95.2

2.1.3

No

[3] See "The -Dinhibit_libc hack" subsection in the "Building the Toolchain" section of "The GNU toolchain" chapter in AlephOne's "Guide to ARMLinux for Developers" (http://www.aleph1.co.uk/armlinux/book/book1.html) for further information on the modifications to be made to gcc to make it build successfully.

[4] See Ralf Bächle's MIPS HOWTO (http://howto.linux-mips.org/ ) for further information on the patches to apply.

[5] See Bill Gatliff's "Running Linux on the Sega Dreamcast" (http://www.linuxdevices.com/articles/AT7466555948.html ) for further information on the patches to apply.

Some of the combinations presented were on the Net as part of cross-platform development toolchain setups. I have kept the kernel version when the original explanation provided one. The kernel version, however, does not really matter for the build of the toolchain. Any recent kernel—Version 2.2.x or 2.4.x—known to work for your target can be used for the toolchain build. I strongly recommend using the actual kernel you will be using in your target, however, to avoid any future conflicts. I will discuss kernel selection in Chapter 5.

Although it is not specifically mentioned in the table, there is one glibc add-on that we will need for the toolchain: glibc-linuxthreads. The package's versions closely follow glibc's numbering scheme. Hence, the linuxthreads version matching glibc 2.2.3 is linuxthreads Version 2.2.3. Although I recommend getting the linuxthreads package, you should be able to build glibc without it. Note that glibc 2.1.x, for instance, does not build properly without linuxthreads. If you are using glibc 2.1.x, remember that you will also need to download the glibc-crypt add-on if you intend to use DES encryption.

By no means is Table 4-2 complete. There are many other combinations that will work just as well. Feel free to try newer versions than the ones presented. Use the same technique discussed earlier by starting with the latest versions and decrementing versions as needed. At worst, you will have to revert to setups presented above.

Whenever you discover a new version combination that compiles successfully, make sure you test the resulting toolchain to ensure that it is indeed functional. Some version combinations may compile successfully and still fail when used. Version 2.2.3 of glibc, for example, builds successfully for a PPC target on an x86 host using gcc 2.95.3. The resulting library is, nevertheless, broken and will cause a core dump when used on the target. In that particular setup, we can obtain a functional C library by reverting to glibc 2.2.1.

There are also cases where a version combination was found to work properly on certain processors within a processor family while failing to work on other processors of the same family. Versions of glibc earlier than 2.2, for example, worked fine for most PPC processors except those that were part of the MPC8xx series. The problem was that glibc assumed 32-byte cache lines for all PPC processors, while the processors in the MPC8xx series have 16-byte cache lines. Version 2.2 fixed this problem by assuming 16-byte cache lines for all PPC processors.

The following sections describe the building of the GNU toolchain for a PPC host and an i386 target using binutils 2.10.1, gcc 2.95.3, and glibc 2.2.3. This was the second entry in Table 4-2.

4.2.1.2 Build requirements

To build a cross-platform development toolchain, you will need a functional native toolchain. Most mainstream distributions provide this toolchain as part of their packages. If it was not installed on your workstation or if you chose not to install it to save space, you will need to install it at this point using the procedure appropriate to your distribution. With a Red Hat distribution, for instance, you will need to install the appropriate RPM packages.

You will also need a valid set of kernel headers for your host. These headers must usually be located in the /usr/include/linux, /usr/include/asm, and /usr/include/asm-generic directories, and should be the headers used to compile the native glibc installed on your system. In older distributions, and in some installations still, these directories are actually symbolic links to directories within the /usr/src/linux directory. In turn, this directory is itself a symbolic link to the actual kernel installed by your distribution. If your distribution uses the older setup, and you have updated your kernel or modified the content of the /usr/src/linux directory, you will need to make sure the /usr/src/linux symbolic link is set appropriately so that the symbolic links in /usr/include point to the kernel that was used to build your native glibc, and that was installed by your distribution. In recent distributions, however, the content of /usr/include/linux, /usr/include/asm, and /usr/include/asm-generic is independent of the content of /usr/src/linux, and no kernel update should result in such problems.

4.2.1.3 Build overview

With the appropriate tools in place, let us take a look at the procedure used to build the toolchain. These are the five main steps:

  1. Kernel headers setup

  2. Binary utilities setup

  3. Bootstrap compiler setup

  4. C library setup

  5. Full compiler setup

The first thing that you probably noticed, looking at these steps, is that the compiler seems to be built twice. This is normal and required, because some languages supported by gcc, such as C++, require glibc support. Hence, a first compiler is built with support for C only, and a full compiler is built once the C library is available.

Although I placed the kernel headers setup as the first step, the headers will not be used until the C library setup. Hence, you could alter the steps and set up the kernel headers right before the C library setup. Given the workspace directory layout we are using, however, you will find the original ordering of the steps given above to be more appropriate.

Obviously, each step involves many iterations of its own. Nonetheless, the steps remain similar in many ways. Most toolchain build steps involve carrying out the following actions:

  1. Unpack the package.

  2. Configure the package for cross-platform development.

  3. Build the package.

  4. Install the package.

Some toolchain builds differ slightly from this sequence. The kernel headers setup, for instance, does not require that we build the kernel or install it. Actually, we will save much of the discussions about configuring, building, and installing the kernel for Chapter 5. Also, since the compiler will have already been unpacked for the bootstrap compiler's setup, the full compiler setup does not require unpacking the gcc package again.

4.2.1.4 Workspace setup

According to the workspace directory layout I suggested earlier, the toolchain will be built in the ${PRJROOT}/build-tools directory, while the components built will be installed in the ${PRJROOT}/tools directory. To this end, we need to define some additional environment variables. They ease the build process and are based on the environment variables already defined. Using the same example project as before, here is the new develdaq script with the new variables:

export PROJECT=daq-module
export PRJROOT=/home/karim/control-project/${PROJECT}
export TARGET=i386-linux
export PREFIX=${PRJROOT}/tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=${PREFIX}/bin:${PATH}
cd $PRJROOT

The TARGET variable defines the type of target for which your toolchain will be built. Table 4-3 provides some of the other possible values for TARGET. Notice that the target definition does not depend on the type of host. A target is defined by its own hardware and the operating system used on it, which is Linux in this case. Also, note that only TARGET needs to be modified in case we change targets. Of course, if we had already compiled the complete GNU toolchain for a different target, we would need to rebuild it after changing the value of TARGET. For a more complete list of TARGET values, look at the manual included in the glibc sources.

Table 4-3. Example values for TARGET

Actual target

Value of TARGET

PowerPC

powerpc-linux

ARM

arm-linux

MIPS (big endian)

mips-linux

MIPS (little endian)

mipsel-linux

SuperH 4

sh4-linux

The PREFIX variable provides the component configuration scripts with a pointer to the directory where we would like the target utilities to be installed. Conversely, TARGET_PREFIX is used for the installation of target-dependent header files and libraries. To have access to the newly installed utilities, we also need to modify the PATH variable to point to the directory where the binaries will be installed.

Some people prefer to set PREFIX to /usr/local. This results in the tools and libraries being installed within the /usr/local directory where they can be accessed by any user. I find this approach not to be useful for most situations, however, because even projects using the same target architecture may require different toolchain configurations.

If you need to set up a toolchain for an entire development team, instead of sharing tools and libraries via the /usr/local directory, I suggest that a developer build the toolchain within an entry shared by all project members in the /home directory, as I said earlier. In a case in which no entry in the /home directory is shared among group members, a developer may build the toolchain within an entry in her workstation's /opt directory and then share her resulting ${PRJROOT}/tools directory with her colleagues. This may be done using any of the traditional sharing mechanisms available, such as NFS, or using a tar-gzipped archive available on an FTP server. Each developer using the package will have to place it in a filesystem hierarchy identical to the one used to build the toolchain for the tools to operate adequately. In a case in which the toolchain was built within the /opt directory, this means placing the toolchain in the /opt directory.

If you choose to set PREFIX to /usr/local, you will also have to issue the commands shown below while being logged-in as root, with all the risks this entails. You could also set the permission bits of the /usr/local directory to allow yourself or your user group to issue the commands without requiring root privileges.

Notice that TARGET_PREFIX is set to ${PREFIX}/${TARGET}, which is a target-dependent directory. If you set PREFIX to /usr/local, successive installations of development toolchains for different targets will result in the libraries and header files of the latest installation being placed in different directories from the libraries and header files of previous toolchain installations.

Regardless of the value you give to PREFIX, setting TARGET_PREFIX to ${PREFIX}/${TARGET} is the configuration the GNU toolchain utilities expect to find during their configuration and installation. Hence, I strongly suggest that you use this value for TARGET_PREFIX. The following explanations may require changes if you modify TARGET_PREFIX's value.

Again, you can remove the cd command from the script if you would prefer not to move directly to the project directory.

4.2.1.5 Preparing the build-tools directory

At this point, you should have the different packages required for building the toolchain in the build-tools directory. As with other packages, a new directory will be created when you extract the files from the package archive. This new directory will contain the complete source code necessary to build the packages and all appropriate Makefiles. Although it is possible to build the package within this source directory, I highly recommend that you build each package in a directory separate from its source directory, as is suggested in the FSF's installation manuals.

Building a package in a directory different from the one holding its source may seem awkward if you are used to simply typing configure; make; make install, but I will shortly explain how this is done. First, though, we need to create the directories that will hold the packages being built. Create one directory for each toolchain component. Four directories are therefore needed: one for the binutils, one for the bootstrap C compiler, one for the C library, and one for the complete compiler. We can use the following commands to create the necessary entries:

$ cd ${PRJROOT}/build-tools
$ mkdir build-binutils build-boot-gcc build-glibc build-gcc

We can now look at the content of the build-tools directory with the packages and the build directories (the last line in this example is truncated to fit the page):

$ ls -l
total 35151
-rw-r--r--    1 karim    karim     7284401 Apr  4 17:33 binutils-2.10.1.tar.gz
drwxrwxr-x    2 karim    karim        1024 Apr  4 17:33 build-binutils
drwxrwxr-x    2 karim    karim        1024 Apr  4 17:33 build-boot-gcc
drwxrwxr-x    2 karim    karim        1024 Apr  4 17:33 build-gcc
drwxrwxr-x    2 karim    karim        1024 Apr  4 17:33 build-glibc
-rw-r--r--    1 karim    karim    12911721 Apr  4 17:33 gcc-2.95.3.tar.gz
-rw-r--r--    1 karim    karim    15431745 Apr  4 17:33 glibc-2.2.3.tar.gz
-rw-r--r--    1 karim    karim      215313 Apr  4 17:33 glibc-linuxthreads-2.2.3.t

Everything is now almost ready for building the actual toolchain.

4.2.1.6 Resources

Before proceeding to the actual building of the toolchain, let us look at some resources you may find useful in case you run into problems during the build process.

First and foremost, each package comes with its own documentation. Although you will find the binutils package to be the leanest in terms of installation documentation, it is also the least likely to cause any problems. The gcc and glibc packages, however, are amply documented. Within the gcc package, you will find an FAQ file and an install directory containing instructions on how to configure and install gcc. This includes an extensive explanation of the build configuration options. Similarly, the glibc package contains an FAQ and INSTALL files. The INSTALL file covers the build configuration options and the installation process, and provides recommendations for compilation tool versions.

In addition, you may want to try using a general search engine such as Google to look for reports by other developers who may have already encountered and solved problems similar to yours. Often, using a general search engine will be the most effective way to solve a GNU toolchain build problem.

On the matter of cross-compiling, there are two CrossGCC FAQs available: the Scott Howard FAQ and the Bill Gatliff FAQ. The Scott Howard CrossGCC FAQ is available at http://www.sthoward.com/CrossGCC/. This FAQ is rather outdated, however. The Bill Gatliff CrossGCC FAQ is available at http://crossgcc.billgatliff.com/.

Though the Scott Howard FAQ is outdated, and though it isn't limited to Linux and attempts to provide general explanations for all the platforms the GNU toolchain can be built for, it does provide pieces of information that can be hard to find otherwise. It covers, for instance, what is known as Canadian Crosses,[6] a technique for building cross-platform development tools for development on another platform. An example of this would be building cross-platform development tools for an ARM target and an i386 host on a PPC workstation.

[6] In reference to the fact that Canada had three national parties at the time a name was needed for this procedure.

As with the Scott Howard FAQ, the Bill Gatliff FAQ is not limited to Linux. In addition to the FAQ, Bill Gatliff actively maintains a CrossGCC Wiki site, which provides information on a variety of cross-platform development issues, including tutorials, links to relevant articles, and explanations about GNU toolchain internals. Since this is a Wiki site, you can register to modify and contribute to the site yourself. The Wiki site is accessed through the same URL as the Bill Gatliff FAQ.

Both FAQs provide scripts to automate the building of the toolchain. Similar scripts are also available from many other sites. You may be interested in taking a look at these scripts, but I will not rely on any scripts for my future explanations as I would rather you fully understand all the steps involved.

Finally, there is a crosgcc mailing list hosted by Red Hat at http://sources.redhat.com/ml/crossgcc/. You will find this mailing list quite useful if you ever get stuck, because many on this list have a great deal of experience with the process of building cross-platform development toolchains. Often, just searching or browsing the archive will help you locate immediate answers to your questions.

4.2.1.7 A word on prebuilt cross-platform toolchains

A lot of prebuilt cross-platform toolchains are available either online or commercially. Since I do not know the actual process by which each was built, I cannot offer any advice regarding those packages. You may still choose to use such packages out of convenience instead of carrying out the procedure explained here. In that case, make sure you have documentation as to how these packages were configured and built. Most importantly, make sure you know what package versions were used, what patches were applied, if any, and where to get the patches that were applied in case you need them.

4.2.2 Kernel Headers Setup

As I said earlier, the setup of the kernel headers is the first step in building the toolchain. In this case, we are using kernel Version 2.4.18, but we could have used any other version appropriate for our target. We will discuss kernel selection further in Chapter 5.

Having selected a kernel, the first thing you need to do is download a copy of that kernel into the directory in which you have chosen to store kernels. In the case of the workspace hierarchy I suggested earlier, this would be in ${PRJROOT}/kernel. You can obtain all the Linux kernels from the main kernel repository at http://www.kernel.org/ or any other mirror site, such as the national mirrors.[7] There are other sites that provide kernels more adapted to certain targets, and I will cover these in Chapter 5.

[7] In some countries, there are local national mirrors, which may be preferable for you to use instead of the main U.S. site. These mirrors' URLs are usually in the http://www.COUNTRY.kernel.org/ form. http://www.it.kernel.org/ and http://www.cz.kernel.org/ are two such mirrors.

For some time now, each version of the kernel has been available both as a tar-gzipped file (with the .tar.gz extension) and as a tar-bzip2'd file (with the .tar.bz2 extension). Both contain the same kernel, except that tar-bzip2'd files are smaller and require less download time than tar-gzipped files.

With the kernel now in your kernel directory, you can extract it using the appropriate command. In our case, we use one the following commands, depending on the file we downloaded:

$ tar xvzf linux-2.4.18.tar.gz

or:

$ tar xvjf linux-2.4.18.tar.bz2

Some older versions of tar do not support the j option and you may need to use bzip2 -d or bunzip2 to decompress the archive before using tar.

For all kernels up to 2.4.18, the tar command creates a directory called linux that contains the extracted files from the archive. Starting with 2.4.19, however, the kernel extracts immediately into a directory that has the version number appended to its name. Hence, Linux 2.4.19 extracts directly into the linux-2.4.19 directory. This avoids accidently overwriting an older kernel with a new one. If you are using a kernel that is older than 2.4.19, I recommend that you rename the directory right away to avoid any accidental overwriting:

$ mv linux linux-2.4.18

Overwriting a kernel version with another because the directory of the previous version wasn't renamed is a common and often costly mistake, so it is really important that you rename the directory as soon as you extract the kernel from the archive, if need be.

With the kernel now extracted, we proceed to configuring it:

$ cd linux-2.4.18
$ make ARCH=i386 CROSS_COMPILE=i386-linux- menuconfig

This will display a menu in your console where you will be able to select your kernel's configuration. Instead of menuconfig, you can specify config or xconfig. The former requires that you provide an answer for every possible configuration option one by one at the command line. The latter provides an X Window dialog, which is often considered the most intuitive way to configure the kernel. Beware of xconfig, however, as it may fail to set some configuration options and forget to generate some headers required by the procedure I am describing. The use of config may also result in some headers not being created. You can check whether the kernel configuration has successfully created the appropriate headers by verifying whether the include/linux/version.h file exists in the kernel sources after you finish the configuration process. If it is absent, the instructions outlined below will fail at the first instance where kernel headers are required; usually during the compilation of glibc.

As you probably noticed, the values of ARCH and CROSS_COMPILE depend on your target's architecture type. Had this been a PPC target and an i386 host, we would have used ARCH=ppc and CROSS_COMPILE=powerpc-linux-. (The trailing hyphen in the CROSS_COMPILE=powerpc-linux- variable is not an accident.) Strictly speaking, it is not necessary to set CROSS_COMPILE for all kernel make targets. The various configuration targets I just covered don't usually need it, for example. In fact, it is only necessary when code is actually being cross-compiled as a result of the kernel Makefile rules. Nevertheless, I will continue to specify it for all kernel make targets throughout this book, even when it isn't essential, to highlight its importance. You are free to set it only when needed in your actual day-to-day work.

I will cover the intricacies of kernel configuration in Chapter 5. If you are not familiar with kernel configuration, you may want to have a peek at that chapter first. The most important configuration options we need to set at this time are the processor and system type. Although it is preferable to fully configure the kernel before proceeding, just setting the processor and system type is usually enough to generate the appropriate headers for the toolchain build.

With the kernel now configured, exit the menu by selecting the Exit item with your right arrow. The configuration utility then asks you if you want to save the configuration and, upon confirmation, proceeds to write the kernel configuration and creates the appropriate files and links.

We can now create the include directory required for the toolchain and copy the kernel headers to it:

$ mkdir -p ${TARGET_PREFIX}/include
$ cp -r include/linux/ ${TARGET_PREFIX}/include
$ cp -r include/asm-i386/ ${TARGET_PREFIX}/include/asm
$ cp -r include/asm-generic/ ${TARGET_PREFIX}/include

Keep in mind that we are using a PPC host and an i386 target. Hence, the asm-i386 directory in the path above is the directory containing the target-specific headers, not the host-specific ones. If this were a PPC target, for example, we would have to replace asm-i386 with asm-ppc.

Note that you will not need to rebuild the toolchain every time you reconfigure the kernel. The toolchain needs one valid set of headers for your target, which is provided by the procedure given earlier. You may later choose to reconfigure your kernel or use another one entirely without impacting your toolchain, unless you change the processor or system type.

4.2.3 Binutils Setup

The binutils package includes the utilities most often used to manipulate binary object files. The two most important utilities within the package are the GNU assembler, as, and the linker, ld. Table 4-4 contains the complete list of utilities found in the binutils package.

Table 4-4. Utilities found in the binutils package

Utility

Use

as

The GNU assembler

ld

The GNU linker

gasp

The GNU assembler pre-processor

ar

Creates and manipulates archive content

nm

Lists the symbols in an object file

objcopy

Copies and translates object files

objdump

Displays information about the content of object files

ranlib

Generates an index to the content of an archive

readelf

Displays information about an ELF format object file

size

Lists the sizes of sections within an object file

strings

Prints the strings of printable characters in object files

strip

Strips symbols from object files

c++filt

Converts low-level mangled assembly labels resulting from overloaded c++ functions into their user-level names

addr2line

Converts addresses into line numbers within original source files

Note that although as supports many processor architectures, it does not necessarily recognize the same syntax as the other assemblers available for a given architecture. The syntax recognized by as is actually a machine-independent syntax inspired by BSD 4.2 assembly language.

The first step in setting up the binutils package is to extract its source code from the archive we downloaded earlier:

$ cd ${PRJROOT}/build-tools
$ tar xvzf binutils-2.10.1.tar.gz

This will create a directory called binutils-2.10.1 with the package's content. We can now move to the build directory for the second part of the build process, the configuration of the package for cross-platform development:

$ cd build-binutils 
$ ../binutils-2.10.1/configure --target=$TARGET --prefix=${PREFIX} 
Configuring for a powerpc-unknown-linux-gnu host.
Created "Makefile" in /home/karim/control-project/daq-module/build- ...
Configuring intl...
creating cache ../config.cache
checking for a BSD compatible install... /usr/bin/install -c
checking how to run the C preprocessor... gcc -E
checking whether make sets ${MAKE}... yes
checking for gcc... gcc
checking whether the C compiler (gcc -g -O2 -W -Wall ) works... yes
checking whether the C compiler (gcc -g -O2 -W -Wall ) is a cross-c ...
checking whether we are using GNU C... yes
checking whether gcc accepts -g... yes
checking for ranlib... ranlib
checking for POSIXized ISC... no
checking for ANSI C header files... yes
           ... 

What I've shown is only part of the output from the configure script. It will actually continue printing similar messages on the console until it has prepared each utility in the package for compilation. This may take a minute or two to complete, but it is a relatively short operation.

During its run, configure checks for the availability of certain resources on the host and creates appropriate Makefiles for each tool in the package. Since the command is not being issued in the directory containing the binutils source code, the result of the configure command will be found in the directory where it was issued, the build-binutils directory.

We control the creation of the Makefiles by passing the appropriate options to configure. The - -target option enables us to specify the target for which the binutils are being built. Since we had already specified the name of the target in the TARGET environment variable, we provide this variable as is. The - -prefix option enables us to provide the configuration script with the directory within which it should install files and directories. The directory for - -prefix is the same as the one we specified earlier in the PREFIX environment variables.

With the Makefiles now ready, we can build the actual utilities:

$ make

The actual build of the binutils may take anywhere between 10 and 30 minutes, depending on your hardware. Using a 400 MHz PowerBook host, it takes at most 15 minutes to build the binutils for the i386 target used here. You may see some warnings during the build but they can be ignored, unless you're one of the binutils developers.

With the package now built, we can install the binutils:

$ make install

The binutils have now been installed inside the directory pointed to by PREFIX. You can check to see that they have been installed properly by listing the appropriate directory:

$ ls ${PREFIX}/bin
i386-linux-addr2line  i386-linux-ld       i386-linux-readelf
i386-linux-ar         i386-linux-nm       i386-linux-size
i386-linux-as         i386-linux-objcopy  i386-linux-strings
i386-linux-c++filt    i386-linux-objdump  i386-linux-strip
i386-linux-gasp       i386-linux-ranlib

Notice that the name of each utility is prepended by the value of TARGET we set earlier. Had the target been a powerpc-linux, for instance, the names of the utilities would have been prepended with powerpc-linux-. When building an application for a target, we can therefore use the appropriate tools by prepending the name of the target type.

A copy of some of the utilities without the prepended target name will also be installed in the ${PREFIX}/${TARGET}/bin directory. Since this directory will later be used to install target binaries by the C library build process, we will need to move the host binaries to a more appropriate directory. For now, we will leave them as is and address this issue later.

4.2.4 Bootstrap Compiler Setup

In contrast to the binutils package, the gcc package contains only one utility, the GNU compiler, along with support components such as runtime libraries. At this stage, we will build the bootstrap compiler, which will support only the C language. Later, once the C library has been compiled, we will recompile gcc with full C++ support.

Again, we start by extracting the gcc package from the archive we downloaded earlier:

$ cd ${PRJROOT}/build-tools
$ tar xvzf gcc-2.95.3.tar.gz

This will create a directory called gcc-2.95.3 with the package's content. We can now proceed to the configuration of the build in the directory we had prepared for the bootstrap compiler:

$ cd build-boot-gcc
$ ../gcc-2.95.3/configure --target=$TARGET --prefix=${PREFIX} \
> --without-headers --with-newlib --enable-languages=c

This will print output similar to that printed by the binutils configuration utility we discussed earlier. Here too, configure checks for the availability of resources and builds appropriate Makefiles.

The - -target and - -prefix options given to configure have the same purpose as with binutils, to specify the target and the installation directory, respectively. In addition, we use options that are required for building a bootstrap compiler.

Since this is a cross-compiler and there are no system header files for the target yet—they will be available once glibc is built—we need to use the - -without-headers option. We also need to use the - -with-newlib option to tell the configuration utility not to use glibc, since it has not yet been compiled for the target. This option, however, does not force us to use newlib as the C library for the target. It is just there to enable gcc to properly compile, and we will be free to choose any C library at a later time.

The - -enable-languages option tells the configuration script which programming languages we expect the resulting compiler to support. Since this is a bootstrap compiler, we need only include support for C.

Depending on your particular setup, you may want to use additional options for your target. For a complete list of the options recognized by configure, see the installation documentation provided with the gcc package.

With the Makefiles ready, we can now build the compiler:

$ make all-gcc

The compile time for the bootstrap compiler is comparable to that of the binutils. Here, too, you may see warnings during the compilation, and you can safely ignore them.

With the compilation complete, we can now install gcc:

$ make install-gcc

The bootstrap compiler is now installed alongside the binutils, and you can see it by relisting the content of ${PREFIX}/bin. The name of the compiler, like the utilities, is prepended with the name of the target and is called i386-linux-gcc in our example.

4.2.5 C Library Setup

The glibc package is made up of a number of libraries and is the most delicate and lengthy package build in our cross-platform development toolchain. It is an extremely important software component on which most, if not all, applications available or being developed for your target will rely. Note that although the glibc package is often called the C library—a confusion maintained within GNU's own documentation—glibc actually generates many libraries, one of which is the actual C library, libc. We will discuss the complete list of libraries generated by glibc in Chapter 6. Until then, I will continue to use "C library" and "glibc" interchangeably.

Using gcc 3.2 and Above

The instructions provided in the previous section will fail to work with gcc 3.2 and the subsequent 3.2.1 release existing at the time of this writing, because the - -without-headers configuration option is broken and has not yet been fixed. To solve the problem, we must install appropriate glibc headers before attempting to compile the bootstrap compiler. This sidebar provides the commands used to install the headers, but does not explain the various command options in detail, since they are already covered by the previous and next sections. Here, we are using binutils 2.13.2.1, gcc 3.2.1, and glibc 2.3.1. Note that a native gcc 3.2 must be available on the host for the following procedure to work.

First, we must extract the glibc package and its add-ons, as we would do when setting up the C library:

$ cd ${PRJROOT}/build-tools
$ tar xvzf glibc-2.3.1.tar.gz
$ tar -xvzf glibc-linuxthreads-2.3.1.tar.gz --directory=glibc-2.3.1

Next, we must configure glibc and install its headers:

$ mkdir build-glibc-headers
$ cd build-glibc-headers
$ ../glibc-2.3.1/configure --host=$TARGET --prefix="/usr" \
> --enable-add-ons --with-headers=${TARGET_PREFIX}/include
$ make cross-compiling=yes install_root=${TARGET_PREFIX} \
> prefix="" install-headers

Because we are not setting CC to point to an existing cross-compiler, we must set the cross-compiling variable to yes so that the glibc build scripts do not attempt to build parts of the library natively. Installing the headers is achieved by using the install-headers Makefile target.

Next, we create a dummy stubs.h file required for gcc to build (a version of this file will be generated properly during the installation of the cross-compiled glibc):

$ mkdir -p ${TARGET_PREFIX}/include/gnu
$ touch ${TARGET_PREFIX}/include/gnu/stubs.h

Finally, we can build the bootstrap gcc compiler:

$ cd ${PRJROOT}/build-tools/build-boot-gcc
$ ../gcc-3.2.1/configure --target=$TARGET --prefix=${PREFIX} \
> --disable-shared --with-headers=${TARGET_PREFIX}/include \
> --with-newlib --enable-languages=c 
$ make all-gcc

In addition to the options we used in the previous section, we are also using the - -disable-shared configuration option to avoid the build scripts from trying to create the shared gcc library. If this option is not used, gcc 3.2 fails to build.

Once the bootstrap compiler is installed, the steps for building and installing the rest of the GNU toolchain are the same as those described in this chapter.

As with the previous packages, we start by extracting the C library from the archive we downloaded earlier:

$ cd ${PRJROOT}/build-tools
$ tar xvzf glibc-2.2.3.tar.gz

This will create a directory called glibc-2.2.3 with the package's content. In addition to extracting the C library, we extract the linuxthreads package in the glibc directory for the reasons stated earlier in the chapter:

$ tar -xvzf glibc-linuxthreads-2.2.3.tar.gz --directory=glibc-2.2.3

We can now proceed to preparing the build of the C library in the build-glibc directory:

$ cd build-glibc
$ CC=i386-linux-gcc ../glibc-2.2.3/configure --host=$TARGET \
> --prefix="/usr" --enable-add-ons \
> --with-headers=${TARGET_PREFIX}/include

Notice that this configuration command is somewhat different from the previous ones. First, we precede the call to configure with CC=i386-linux-gcc. The effect of this command is to set the CC environment variable to i386-linux-gcc. Therefore, the compiler used to build the C library will be the bootstrap cross-compiler we have just built. Also, we now use the - -host option instead of the - -target option, since the library runs on our target and not on our build system.[8] In other words, the host from the point of view of the library is our target, contrary to the tools we built earlier, which all run on our build system.

[8] Practically speaking, the build system is our development host.

Although we still use the - -prefix option, its purpose here is to indicate to the configuration script the location of the library components once on the target's root filesystem. This location is then hardcoded into the glibc components during their compilation and used at runtime. As is explained in the INSTALL file in the glibc source directory, Linux systems expect to have some glibc components installed in /lib and others in /usr/lib. By setting - -prefix to /usr, the configuration script recognizes this setup and the relevant directory paths are properly hardcoded in the glibc components. As a result, the dynamic linker, for example, will expect to find shared libraries in /lib, which is the appropriate location for these libraries in any Linux system, as we shall see in Chapter 6. We will not, however, let the build script install the libraries into the build system's /usr directory. Rather, as we shall see later in this section, we will override the install directory when issuing the make install command.

We also instruct the configuration script to use the add-on we downloaded with the - -enable-add-ons option. Since we are using linuxthreads only, we could have given the exact list of add-ons we want to be configured by using the - -enable-add-ons=linuxthreads option. If you are using glibc 2.1.x and had applied the glibc-crypt add-on, you would need to use the - -enable-add-ons=linuxthreads,crypt option instead. The full command I provided earlier, which doesn't include the full list of add-ons, will work fine nonetheless with most glibc versions.

Finally, we tell the configuration script where to find the kernel headers we set up earlier using the - -with-headers option. If this option was omitted, the headers found through /usr/include would be used to build glibc and these would be inappropriate, since they are the build system's headers, not the target's.

During the actual build of the library, three sets of libraries are built: a shared set, a static set, and a static set with profiling information. If you do not intend to use the profiling version of the library, you may instruct the configuration script not to include it as part of the build process by using the - -disable-profile option. The same applies to the shared set, which can be disabled using the - -disable-shared option. If you do not intend to have many applications on your target and plan to link all your applications statically, you may want to use this option. Be careful, however, as your target may eventually need the shared library. You can safely leave its build enabled and still link your applications statically. At least then you will be able to change your mind about how to link your application without having to rebuild the C library.

Another option that has an effect on static versus dynamic linking is - -enable-static-nss. This option generates libraries which enable the static linking of the Name Service Switch (NSS) components. In brief, the NSS part of glibc allows some of the library components to be customizable to the local configuration. This involves the use of the /etc/nsswitch.conf file to specify which /lib/libnss_NSS_SERVICE library is loaded at runtime. Because this service is specifically designed to load libraries dynamically, it doesn't allow true static linking unless it is forced to. Hence, if you plan to statically link applications that use NSS, add the - -enable-static-nss option to the configuration script's command line. The web servers discussed in Chapter 10, for example, use NSS and will either not function properly on the target or will simply fail to build if you instruct the linker to statically link them against a glibc that doesn't allow static NSS linking. Look at the glibc manual for a complete discussion of NSS.

If you are compiling glibc for a target that lacks an FPU, you may also want to use the - -without-fp option to build FPU emulation into the C library. In some cases, you may also need to add the -msoft-float option to the C flags used to build the library. In the case of the PPC, at least, the C flags are appropriately set (since glibc 2.3) whenever - -without-fp is used to configure glibc.

If you have chosen not to download the linuxthreads package, or the crypt package if you were using glibc 2.1.x, you may try to compile the C library by removing the - -enable-add-ons option and adding the - -disable-sanity-checks option. Otherwise, the configuration script will complain about the missing linuxthreads. Note, however, that although glibc may build successfully without linuxthreads, it is possible that the full compiler's build will fail when including C++ support later.

With the configuration script done, we can now compile glibc:

$ make

The C library is a very large package and its compilation may take several hours, depending on your hardware. On the PowerBook system mentioned earlier, the build takes approximately an hour. Regardless of your platform, this is a good time to relax, grab a snack, or get some fresh air. One thing you may want to avoid is compiling the C library in the background while trying to use your computer for other purposes in the meantime. As I said earlier, the compilation of some of the C library's components uses up a lot of memory, and if the compiler fails because of the lack of available memory, you may have to restart the build of the library from scratch using make clean followed by make. Some parts of the build may not be restarted gracefully if you just retype make.

Once the C library is built, we can now install it:

$ make install_root=${TARGET_PREFIX} prefix="" install

In contrast to the installation of the other packages, the installation of the C library will take some time. It won't take as much time as the compilation, but it may take between 5 and 10 minutes, again depending on your hardware.

Notice that the installation command differs from the conventional make install. We set the install_root variable to specify the directory where we want the library's components to be installed. This ensures that the library and its headers are installed in the target-dependent directory we had assigned to TARGET_PREFIX earlier, not in the build system's /usr directory. Also, since the use of the - -prefix option sets the prefix variable's value and since the value of prefix is appended to install_root's value to provide the final installation directory of the library's components, we reset the value of prefix so that all glibc components are installed directly in the ${TARGET_PREFIX} directory. Hence, the glibc components that would have been installed in ${TARGET_PREFIX}/usr/lib are installed in ${TARGET_PREFIX}/lib instead.

If you are building tools for a target that is of the same architecture as your host (compiling for a PPC target on a PPC host, for instance), you may want to set the cross-compiling variable to yes as part of the make install command. Because the library's configure script will have detected that the architectures are identical during the build configuration, the Makefile assumes that you are not cross-compiling and the installation of the C library fails as a result of the Makefile using a different set of rules.

There is one last step we must carry out to finalize glibc's installation: the configuration of the libc.so file. This file is used during the linking of applications to the C library and is actually a link script. It contains references to the various libraries needed for the real linking. The installation carried out by our make install above assumes that the library is being installed on a root filesystem and hence uses absolute pathnames in the libc.so link script to reference the libraries. Since we have installed the C library in a nonstandard directory, we must modify the link script so that the linker will use the appropriate libraries. Along with the other components of the C library, the link script has been installed in the ${TARGET_PREFIX}/lib directory.

In its original form, libc.so looks like this:

/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a )

This is actually quite similar, if not identical, to the libc.so that has already been installed by your distribution for your native C library in /usr/lib/. Since you may need your target's default script sometime, I suggest you make a copy before modifying it:

$ cd ${TARGET_PREFIX}/lib
$ cp ./libc.so ./libc.so.orig

You can now edit the file and remove all absolute path references. In essence, you will need to remove /lib/ from all the library filenames. The new libc.so now looks like this:

/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
GROUP ( libc.so.6 libc_nonshared.a )

By removing the references to an absolute path, we are now forcing the linker to use the libraries found within the same directory as the libc.so script, which are the appropriate ones for your target, instead of the native ones found on your host.

4.2.6 Full Compiler Setup

We are now ready to install the full compiler for your target with both C and C++ support. Since we had already extracted the compiler from its archive in Section 4.2.4, we will not need to repeat this step. Overall, the build of the full compiler is much simpler than the build of the bootstrap compiler.

From the build-tools/build-gcc directory enter:

$ cd ${PRJROOT}/build-tools/build-gcc
$ ../gcc-2.95.3/configure --target=$TARGET --prefix=${PREFIX} \
> --enable-languages=c,c++

The options we use here have the same meaning as when building the bootstrap compiler. Notice, however, that there are fewer options and that we now add support for C++ in addition to C. If you had set TARGET_PREFIX to something other than ${PREFIX}/${TARGET} as we did earlier, you will need to use the - -with-headers and - -with-libs options to tell the configuration script where to find the headers and libraries installed by glibc.

With the full compiler properly configured, we can now build it:

$ make all

This build will take slightly longer than the build of the bootstrap compiler. Again, you may see warnings that you can ignore. Notice that we didn't use all-gcc as with the bootstrap compiler, but rather all. This will result in the build of all the rest of the components included with the gcc package, including the C++ runtime libraries.

If you didn't properly configure the libc.so link script file as previously explained, the build will fail during the compilation of the runtime libraries. Also, if you didn't install the linuxthreads package during the C library setup, the compilation may fail under some versions of gcc. Version 2.95.3 of gcc, for instance, will fail to build without linuxthreads.

With the full compiler now built, we can install it:

$ make install

This will install the full compiler over the bootstrap compiler we had previously installed. Notice that we didn't use install-gcc as we had done earlier for the bootstrap compiler, but rather install. Again, this is because we are now installing both gcc and its support components.

4.2.7 Finalizing the Toolchain Setup

The full cross-platform development toolchain is now set up and almost ready to be used. I have only a couple of final observations left.

First, let's take a look at what has been installed in the tools directory and how we will be using it in the future. Table 4-5 provides the list of first-level subdirectories found in the tools directory.

Table 4-5. Contents of the ${PRJROOT}/tools directory

Directory

Content

bin

The cross-development utilities.

i386-linux

Target-specific files.

include

Headers for cross-development tools.

info

The gcc info files.

lib

Libraries for cross-development tools.

man

The manual pages for cross-development tools.

share

The files shared among cross-development tools and libraries. This directory is empty.

The two most important directories are bin and i386-linux. The first contains all the tools within the cross-development toolchain that we will use on the host to develop applications for the target. The second contains all the software components to be used on the target. Mainly, it contains the header files and runtime libraries for the target. Table 4-6 provides a list of the first-level subdirectories found in i386-linux.

Table 4-6. Contents of the ${PRJROOT}/tools/i386-linux directory

Directory

Content

bin

glibc-related target binaries and scripts.

etc

Files that should be placed in the target's /etc directory. Only contains the rpc file.

include

The headers used to build applications for the target.

info

The glibc info files.

lib

The target's /lib directory.

libexec

Binary helpers. This directory only contains pt_chown, which you will not need for most targets.

sbin

The target's /sbin directory.

share

Subdirectories and files related to internationalization.

sys-include

Would have been used by the gcc configuration script to copy the target's headers had glibc not installed the main target headers in the include directory.

Within the i386-linux directory, the two most important directories are include and lib. The first contains the header files that will be used to build any application for the target. The second contains the runtime libraries for the target.

Notice that this last directory contains a lot of large libraries. By itself, the directory weighs in at around 80 MB. Most embedded systems do not have this quantity of storage available. As we will see in Section 4.3, there are other libraries that can be used instead of glibc. Also, we will see in Chapter 6 ways to minimize the number and size of the libraries you choose to use.

As I said earlier, a copy of some of the host utilities without the prepended target name have been installed in the ${PREFIX}/${TARGET}/bin directory. Since this directory now contains target binaries installed by the C library build process, I highly suggest that you move the host binaries out of this directory and into another directory more appropriate for host binaries. The utilities affected by this are as, ar, gcc, ld, nm, ranlib, and strip. You can verify that these are indeed host binaries using the file command:

$ cd ${PREFIX}/${TARGET}/bin
$ file as ar gcc ld nm ranlib strip
as:     ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1...
ar:     ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1...
gcc:    ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1...
ld:     ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1...
nm:     ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1...
ranlib: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1...
strip:  ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1...

We must choose an appropriate directory in which to put these binaries and create symbolic links to the relocated binaries, because some GNU utilities, including gcc, expect to find some of the other GNU utilities in ${PREFIX}/${TARGET}/bin and will use the host's utilities if they can't find the target's binaries there. Naturally, this will result in failed compilations, since the wrong system's tools are used. The compiler has a default search path it uses to look for binaries. We can view this path using one of the compiler's own options (some lines wrap; your shell will take care of line wrapping):

$ i386-linux-gcc -print-search-dirs

install: /home/karim/control-project/daq-module/tools/lib/gcc-lib/i386-linux/2.95.3/
programs: /home/karim/control-project/daq-module/tools/lib/gcc-lib/i386-linux/2.95.3/
:/home/karim/control-project/daq-module/tools/lib/gcc-lib/i386-linux/:/usr/lib/gcc/
i386-linux/2.95.3/:/usr/lib/gcc/i386-linux/:/home/karim/control-project/daq-module/
tools/i386-linux/bin/i386-linux/2.95.3/:/home/karim/control-project/daq-module/tools/
i386-linux/bin/
libraries: /home/karim/control-project/daq-module/tools/lib/gcc-lib/i386-linux/2.95.
3/:/usr/lib/gcc/i386-linux/2.95.3/:/home/karim/control-project/daq-module/tools/i386-
linux/lib/i386-linux/2.95.3/:/home/karim/control-project/daq-module/tools/i386-linux/
lib/

The first entry on the programs line, ${PREFIX}/lib/gcc-lib/i386-linux/2.95.3, is a directory containing gcc libraries and utilities. By placing the binaries in this directory, you can make the cross-compiler use them instead of the native tools:

$ mv as ar gcc ld nm ranlib strip \
> ${PREFIX}/lib/gcc-lib/i386-linux/2.95.3

Meanwhile, the native toolchain will continue to operate normally. We can also create symbolic links to the relocated binaries just in case an application still looks for the utilities only in ${PREFIX}/${TARGET}/bin. Most applications will not look exclusively in this directory, however, and you can almost always skip this step. One case requiring these symbolic links is when you need to recompile components of the GNU cross-platform development toolchain for your target. Nonetheless, because these are symbolic links to host binaries instead of the host binaries themselves, it is easier to tell them apart from the target binaries in case you need to copy the content of the ${PREFIX}/${TARGET}/bin directory to your target's root filesystem. The following script makes the links:

$ for file in as ar gcc ld nm ranlib strip
> do
> ln -s ${PREFIX}/lib/gcc-lib/i386-linux/2.95.3/$file .
> done

Regardless of the type of host or the gcc version you use, a directory similar to ${PREFIX}/lib/gcc-lib/i386-linux/2.95.3 will be created during the building of the cross-platform development toolchain. As you can see, the directory path is made up of the target type and the gcc version. Your particular directory should be located in ${PREFIX}/lib/gcc-lib/${TARGET}/GCC_VERSION, where GCC_VERSION is the version of gcc you are using in your cross-platform development toolchain.

Finally, to save disk space, you may choose to get rid of the content of the ${PRJROOT}/build-tools directory once you have completed the installation of the toolchain components. This may be very tempting, as the build directory now occupies around 600 MB of disk space. I advise you to think this through carefully, nonetheless, and not rush to use the rm -rf command. An unforeseen problem may require that you delve into this directory again at a future time. If you insist upon reclaiming the space occupied by the build directory, a compromise may be to wait a month or two and see if you ever need to come back to it.

4.2.8 Using the Toolchain

You now have a fully functional cross-development toolchain, which you can use very much as you would a native GNU toolchain, save for the additional target name prepended to every command you are used to. Instead of invoking gcc and objdump for your target, you will need to invoke i386-linux-gcc and i386-linux-objdump.

The following is a Makefile for the control daemon on the DAQ module that provides a good example of the cross-development toolchain's use:

# Tool names
CROSS_COMPILE = ${TARGET}-
AS            = $(CROSS_COMPILE)as
AR            = $(CROSS_COMPILE)ar
CC            = $(CROSS_COMPILE)gcc
CPP           = $(CC) -E
LD            = $(CROSS_COMPILE)ld
NM            = $(CROSS_COMPILE)nm
OBJCOPY       = $(CROSS_COMPILE)objcopy
OBJDUMP       = $(CROSS_COMPILE)objdump
RANLIB        = $(CROSS_COMPILE)ranlib
READELF       = $(CROSS_COMPILE)readelf
SIZE          = $(CROSS_COMPILE)size
STRINGS       = $(CROSS_COMPILE)strings
STRIP         = $(CROSS_COMPILE)strip

export AS AR CC CPP LD NM OBJCOPY OBJDUMP RANLIB READELF SIZE STRINGS \
         STRIP

# Build settings
CFLAGS        = -O2 -Wall
HEADER_OPS    =
LDFLAGS       =

# Installation variables
EXEC_NAME     = command-daemon
INSTALL       = install
INSTALL_DIR   = ${PRJROOT}/rootfs/bin

# Files needed for the build
OBJS          = daemon.o

# Make rules
all: daemon

.c.o:
        $(CC) $(CFLAGS) $(HEADER_OPS) -c $<

daemon: ${OBJS}
        $(CC) -o $(EXEC_NAME) ${OBJS} $(LDFLAGS)

install: daemon
        test -d $(INSTALL_DIR) || $(INSTALL) -d -m 755 $(INSTALL_DIR)
        $(INSTALL) -m 755 $(EXEC_NAME) $(INSTALL_DIR)

clean:
        rm -f *.o $(EXEC_NAME) core

distclean:
        rm -f *~
        rm -f *.o $(EXEC_NAME) core

The first part of the Makefile specifies the names of the toolchain utilities we are using to build the program. The name of every utility is prepended with the target's name. Hence, the value of CC will be i386-linux-gcc, the cross-compiler we built earlier. In addition to defining the name of the utilities, we also export these values so that subsequent Makefiles called by this Makefile will use the same names. Such a build architecture is quite common in large projects with one main directory containing many subdirectories.

The second part of the Makefile defines the build settings. CFLAGS provides the flags to be used during the build of any C file.

As we saw in the previous section, the compiler is already using the correct path to the target's libraries. The linker flags variable, LDFLAGS, is therefore empty. If the compiler wasn't pointing to the correct libraries or was using the host's libraries (which shouldn't happen if you followed the instructions I provided above), we would have to tell the compiler which libraries to use by setting the link flags as follows:

LDFLAGS       = -nostdlib -L${TARGET_PREFIX}/lib

If you wish to link your application statically, you need to add the -static option to LDFLAGS. This generates an executable that does not rely on any shared library. But given that the standard GNU C library is rather large, this will result in a very large binary. A simple program that uses printf( ) to print "Hello World!", for example, is less than 12 KB in size when linked dynamically and around 350 KB when linked statically and stripped.

The variables in the installation section indicate what, where, and how to install the resulting binary. In this case, the binary is being installed in the /bin directory of the target's root filesystem.

In the case of the control daemon, we currently only have one file to build. Hence, the program's compilation only requires this single file. If, however, you had used the -nostdlib option in LDFLAGS, which you should not normally need to do, you would also need to change the section describing the files required for the build and the rule for generating the binary:

STARTUP_FILES = ${TARGET_PREFIX}/lib/crt1.o \
                ${TARGET_PREFIX}/lib/crti.o \
                ${PREFIX}/lib/gcc-lib/${TARGET}/2.95.3/crtbegin.o
END_FILES     = ${PREFIX}/lib/gcc-lib/${TARGET}/2.95.3/crtend.o \
                ${TARGET_PREFIX}/lib/crtn.o
LIBS          = -lc
OBJS          = daemon.o
LINKED_FILES  = ${STARTUP_FILES} ${OBJS} ${LIBS} ${END_FILES}
...
daemon: ${OBJS}
        $(CC) -o $(EXEC_NAME) ${LINKED_FILES} $(LDFLAGS)

Here, we add five object files to the one we are generating from our own C file, crt1.o, crti.o, crtbegin.o, crtend.o, and crtn.o. These are special startup, initialization, constructor, destructor, and finalization files, respectively, which are usually automatically linked to your applications. It is through these files that your application's main( ) function is called, for example. Since we told the compiler not to use standard linking in this case, we need to explicitly mention the files. If you do not explicitly mention them while having disabled standard linking, the linker will complain about the missing _start symbol and fail. The order in which the object files are provided to the compiler is important because the GNU linker, which is automatically invoked by the compiler to link the object files, is a one-pass linker.

The make rules themselves are very much the same ones you would find in a standard, native Makefile. I added the install rule to automate the install process. You may choose not to have such a rule, but to copy the executable manually to the proper directory.

With the Makefile and the source file in your local directory, all you need to do is type make to build your program for your target. If you want to build your program for native execution on your host to test your application, for example, you could use the following command line:

$ make CROSS_COMPILE=""
    Team LiB   Previous Section   Next Section