The Bootstrap Process: Pass One and Pass Two

Updated

October 30, 2026

The Bootstrap Process: Pass One and Pass Two

Bootstrapping a new distribution presents a circular dependency problem: - To build the C library (glibc), you need a C compiler (gcc). - To build a functional C compiler (gcc), you need the C library headers and startup objects (libc.so, crt1.o).

                    The Circular Bootstrap Paradox
                     +---------------------------+
                     |  GCC needs libc headers   |
                     +-------------+-------------+
                                   |
                                   v
                     +---------------------------+
                     |  libc needs GCC compiler  |
                     +---------------------------+

To break this loop without poisoning the target distribution with host binaries, distro builders employ a multi-pass bootstrap process.

Pass 1: The Bare-Metal Cross-Compiler

In Pass 1, we compile a stripped-down, static cross-compiler that runs on the host system:

  1. Install Linux API Headers: Extract kernel headers directly into $SYSROOT/usr/include.

    $ make mrproper
    $ make headers_install INSTALL_HDR_PATH=$SYSROOT/usr
  2. Build Cross-Binutils (Pass 1): Configured with --target=$TARGET and --with-sysroot=$SYSROOT.

    $ ../binutils/configure \
        --prefix=$TOOLS \
        --with-sysroot=$SYSROOT \
        --target=$TARGET \
        --disable-nls \
        --disable-werror
    $ make && make install
  3. Build GCC (Pass 1 - Static / Inhibit libc): Configure GCC with --without-headers, --with-newlib, and --disable-shared. This builds a bare C-only compiler without thread support or dynamic linking capabilities:

    $ ../gcc/configure \
        --target=$TARGET \
        --prefix=$TOOLS \
        --with-glibc-version=2.39 \
        --with-sysroot=$SYSROOT \
        --without-headers \
        --disable-nls \
        --disable-shared \
        --disable-threads \
        --enable-languages=c
    $ make all-gcc all-target-libgcc && make install-gcc install-target-libgcc

The Intermediate Bridge: Compiling the Target C Library

Now that we possess $TOOLS/bin/$TARGET-gcc (a compiler capable of emitting target machine code without needing a pre-existing target libc), we compile glibc or musl:

$ CC="$TARGET-gcc" ../glibc/configure \
    --prefix=/usr \
    --host=$TARGET \
    --build=$(../glibc/scripts/config.guess) \
    --enable-kernel=4.19 \
    --with-headers=$SYSROOT/usr/include \
    libc_cv_slibdir=/usr/lib
$ make && make DESTDIR=$SYSROOT install

Pass 2: The Full Cross-Compiler and Native Chroot Tools

With the target C library now safely installed inside $SYSROOT/usr/lib and $SYSROOT/usr/include:

  1. Rebuild GCC (Pass 2): Build a full compiler targeting $TARGET with C++ support, thread support (pthreads), and shared library support enabled.

  2. Compile Core Utilities for Target: Using the Pass 2 cross-compiler, build Bash, Coreutils, Sed, Grep, Gawk, and Make, installing them into $SYSROOT.

  3. The Pivot (Entering Chroot): Mount host /dev, /proc, and /sys into $SYSROOT and switch into the target rootfs using chroot:

    # chroot $SYSROOT /usr/bin/bash

    From this point forward, every package is compiled natively from inside the clean environment. The host system’s tools are severed.