[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[patches] Cross-building instructions

Here's a first draft of instructions for cross-building a GCC/EGLIBC
pair.  Criticism welcome, and actively solicited.

                        Cross-Compiling EGLIBC
                  Jim Blandy <jimb@xxxxxxxxxxxxxxxx>


Most GNU tools have a simple build procedure: you run their
'configure' script, and then you run 'make'.  Unfortunately, the
process of cross-compiling the GNU C library is quite a bit more

1) Build a cross-compiler, with certain facilities disabled.

2) Configure the C library using the compiler you built in step 1).
   Build a few of the C run-time object files, but not the rest of the
   library.  Install the library's header files and the run-time
   object files, and create a dummy libc.so.

3) Build a second cross-compiler, using the header files and object
   files you installed in step 2.

4) Configure, build, and install a fresh C library, using the compiler
   built in step 3.

5) Build a third cross-compiler, based on the C library built in step 4.

The reason for this complexity is that, although GCC and the GNU C
library are distributed separately, they are not actually independent
of each other: GCC requires the C library's headers and some object
files to compile its own libraries, while the C library depends on
GCC's libraries.  EGLIBC includes features and bug fixes to the stock
GNU C library that simplify this process, but the fundamental
interdependency stands.

In this document, we explain how to cross-compile an EGLIBC/GCC pair
from source.  Our intended audience is developers who are already
familiar with the GNU toolchain and comfortable working with
cross-development tools.  While we do present a worked example to
accompany the explanation, for clarity's sake we do not cover many of
the options available to cross-toolchain users.


EGLIBC requires recent versions of the GNU binutils and GCC.  The web
page <http://www.eglibc.org/prerequisites> documents the current
requirements, and lists patches needed for certain target
architectures.  In the example build process shown here we use
binutils 2.17 and GDB 4.1, which were current as we wrote this.

First, let's set some variables, to simplify later commands.  We'll
build EGLIBC and GCC for a PowerPC target, known to the Linux kernel
as 'powerpc', and we'll do the build on an Intel Linux box:

    $ build=i686-pc-linux-gnu
    $ host=$build
    $ target=powerpc-none-linux-gnu
    $ linux_arch=powerpc

We're using the aforementioned versions of Binutils and GCC, and Linux

    $ binutilsv=binutils-2.17
    $ gccv=gcc-4.1.1
    $ linuxv=linux-2.6.20

And we're carrying out the entire process under '~/cross-build', which
contains unpacked source trees:

    $ top=$HOME/cross-build/ppc
    $ src=$top/src
    $ ls $src
    binutils-2.17  gcc-4.1.1  libc  linux-2.6.20


Configuring and building binutils for the target is straightforward.
However, since we're going to install it in several places, we leave
that step for later:

    $ mkdir -p "$top/binutils/build"
    $ cd $top/binutils/build
    $ $src/$binutilsv/configure --target=$target --prefix=$top/bad-binutils
    $ make

The First GCC

For our work, we need a cross-compiler targeting a PowerPC Linux
system.  However, that configuration includes the shared library
'libgcc_s.so', which is compiled against the EGLIBC headers (which we
haven't installed yet) and linked against 'libc.so' (which we haven't
built yet).

Fortunately, there are configuration options for GCC which tell it not
to build 'libgcc_s.so'.  The '--without-headers' option is supposed to
take care of this, but its implementation is incomplete, so you must
also configure with the '--with-newlib' option.  While '--with-newlib'
appears to mean "Use the Newlib C library", its effect is to tell the
GCC build machinery, "Don't assume there is a C library available."

We also need to disable some of the libraries that would normally be
built along with GCC, and specify that only the C compiler is needed.

We'll build and install this first compiler in its own directory,
'$top/gcc1'.  First, we install binutils there:

    $ cd $top/binutils/build
    $ make install prefix=$top/gcc1

Then, we configure, make, and install:

    $ mkdir -p $top/gcc1/build
    $ cd $top/gcc1/build
    $ $src/$gccv/configure \
    >     --target=$target \
    >     --prefix=$top/gcc1 \
    >     --disable-shared --disable-threads --disable-libssp \
    >     --disable-libgomp --disable-libmudflap \
    >     --without-headers --with-newlib \
    >     --enable-languages=c
    $ PATH=$top/gcc1/bin:$PATH make
    $ PATH=$top/gcc1/bin:$PATH make install

Linux Kernel Headers

Before we can configure EGLIBC, we also need Linux kernel headers in
place.  Fortunately, the Linux makefiles have a target that installs
them for us.  Since the process does modify the source tree a bit, we
make a copy first:

    $ mkdir -p "$top/linux"
    $ cp -r $src/$linuxv $top/linux/build
    $ cd $top/linux/build

Now we're ready to install the headers into the tree we'll be using
for the final toolchain, '$top/phase2':

    $ phase2=$top/phase2
    $ PATH=$top/gcc1/bin:$PATH \
    > make headers_install \
    >      ARCH=$linux_arch CROSS_COMPILE=$target- \
    >      INSTALL_HDR_PATH=$phase2/$target

EGLIBC Headers and Preliminary Objects

Using the cross-compiler we've just built, we can now configure EGLIBC
well enough to install the headers and build the object files the full
cross-compiler will need:

    $ mkdir -p "$top/eglibc-headers"
    $ cd "$top/eglibc-headers"
    $ gcc1bin=$top/gcc1/bin
    $ BUILD_CC=gcc \
    > CC=$gcc1bin/$target-gcc \
    > CXX=$gcc1bin/$target-g++ \
    > AR=$gcc1bin/$target-ar \
    > RANLIB=$gcc1bin/$target-ranlib \
    > $src/libc/configure \
    >     --with-headers=$phase2/$target/include \
    >     --prefix=$phase2/$target \
    >     --build=$build \
    >     --host=$target \
    >     --disable-profile --without-gd --without-cvs --enable-add-ons

Here, the '--with-headers' option tells EGLIBC where the Linux headers
have been installed, and the '--prefix' option tells EGLIBC to install
its own headers in the cross-target 'include' directory of our
'phase2' tree.

We can now use the 'install-headers' makefile target to install the
headers, and the 'install-bootstrap-headers' variable to request
special handling for certain tricky header files:

    $ make install-headers install-bootstrap-headers=yes

Next, there are a few object files needed to link shared libraries,
which we build and install by hand:

    $ mkdir -p $phase2/$target/lib
    $ make csu/subdir_lib
    $ cp csu/crt1.o csu/crti.o csu/crtn.o $phase2/$target/lib

Finally, 'libgcc_s.so' requires a 'libc.so' to link against.  However,
since we will never actually execute its code, it doesn't matter what
it contains.  So, treating /dev/null as a C source file, we produce a
dummy 'libc.so' in one step:

    $ $gcc1bin/$target-gcc -nostdlib -nostartfiles -shared -x c /dev/null \
    >                      -o $phase2/$target/lib/libc.so

The Second GCC

With the EGLIBC headers installed, along with the selected object
files, we can now build a GCC that is capable of compiling EGLIBC.
First, we re-install binutils in our 'phase2' directory:

    $ cd $top/binutils/build
    $ make install prefix=$phase2

Then, we configure, build, and install the second GCC, again building
only the C compiler, and avoiding libraries we won't use:

    $ mkdir -p $top/gcc2
    $ cd $top/gcc2
    $ $src/$gccv/configure \
    >     --target=$target \
    >     --prefix=$phase2 \
    >     --disable-libssp \
    >     --disable-libgomp --disable-libmudflap \
    >     --enable-languages=c
    $ PATH=$phase2/bin:$PATH make
    $ PATH=$phase2/bin:$PATH make install

EGLIBC, Complete

With the second compiler built and installed, we're now ready for the
full EGLIBC build:

    $ mkdir -p "$top/eglibc"
    $ cd "$top/eglibc"
    $ bin=$phase2/bin
    $ BUILD_CC=gcc \
    > CC=$bin/$target-gcc \
    > CXX=$bin/$target-g++ \
    > AR=$bin/$target-ar \
    > RANLIB=$bin/$target-ranlib \
    > $src/libc/configure \
    >     --prefix=$phase2/$target \
    >     --with-headers=$phase2/$target/include \
    >     --build=$build \
    >     --host=$target \
    >     --disable-profile --without-gd --without-cvs --enable-add-ons
    $ PATH=$bin:$PATH make
    $ PATH=$bin:$PATH make install

At this point, we have a complete EGLIBC installation in
'$phase2/$target', with header files, library files, and C runtime
startup files in place.

The Third GCC

Finally, we recompile GCC against this full installation, enabling
whatever languages and libraries we would like to use:

    $ mkdir -p $top/gcc3
    $ cd $top/gcc3
    $ $src/$gccv/configure \
    >     --target=$target \
    >     --prefix=$phase2 \
    >     --disable-libssp --disable-libgomp --disable-libmudflap \
    >     --enable-languages=c,c++
    $ PATH=$phase2/bin:$PATH make
    $ PATH=$phase2/bin:$PATH make install

Trying Things Out

At this point, '$phase2/bin' contains a cross toolchain ready to use
the EGLIBC installation in '$phase2/$target':

    $ cat hello.c
    main (int argc, char **argv)
      puts ("Hello, world!");
      return 0;
    $ $phase2/bin/$target-gcc hello.c -o hello

We can use 'readelf' to verify that this is indeed an executable for
our target, and using our dynamic linker:

    $ $phase2/bin/$target-readelf -hl hello
    ELF Header:
      Type:                              EXEC (Executable file)
      Machine:                           PowerPC

    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      PHDR           0x000034 0x10000034 0x10000034 0x00100 0x00100 R E 0x4
      INTERP         0x000134 0x10000134 0x10000134 0x0000d 0x0000d R   0x1
          [Requesting program interpreter: /lib/ld.so.1]
      LOAD           0x000000 0x10000000 0x10000000 0x008f0 0x008f0 R E 0x10000

From the presence of the '.6' suffix in the shared object name, we
can see that the 'libgcc_s.so' depends on the 'libc.so' we built from
EGLIBC, not our dummy:

    $ $phase2/bin/$target-readelf -d $phase2/$target/lib/libgcc_s.so.1
    Dynamic section at offset 0x1083c contains 24 entries:
      Tag        Type                         Name/Value
     0x00000001 (NEEDED)                     Shared library: [libc.so.6]
     0x00000001 (NEEDED)                     Shared library: [ld.so.1]
     0x0000000e (SONAME)                     Library soname: [libgcc_s.so.1]

And on an appropriate machine:

    $ $phase2/$target/lib/ld.so.1 --library-path $phase2/$target/lib \
    > ./hello
    Hello, world!