📖 LFS Series — Part 4 of 15 | Previously: Part 3: Preparing the Build

You've got your partition. You've got your sources. Now you need a compiler — but not the one sitting on your host system.

Your host's GCC is linked against your host's glibc, targeting your host's system. If you use it to build LFS packages directly, those packages inherit host dependencies like parasites. The result? A "from scratch" system that's secretly dependent on Ubuntu or Fedora or whatever you started with.

This is the chicken-and-egg problem of LFS: you need a compiler to build a system, but the compiler IS part of the system you're building.

The solution: build a cross-compiler. A GCC that runs on your host but produces binaries for x86_64-lfs-linux-gnu — your future system. It's the same technique used to compile code for embedded devices, Raspberry Pis, or any architecture that isn't the one you're sitting at.

The Build Order Is Not Optional

Five packages. Strict order. Each depends on the one before it:

  1. Binutils — assembler and linker. GCC needs these to exist before it can compile anything.
  2. GCC — the compiler itself. Needs binutils to assemble and link its output.
  3. Linux API Headers — defines the kernel-userspace interface. Glibc needs to know what syscalls exist.
  4. Glibc — the C library. Every C program links against it. GCC needs it to produce working binaries.
  5. Libstdc++ — C++ standard library. Built by GCC but listed separately because it needs glibc first.

Skip one, reorder one, and the whole chain breaks. This isn't a suggestion — it's a dependency graph.

Binutils Pass 1 (~2 min)

The quick one. You're building a cross-assembler (x86_64-lfs-linux-gnu-as) and cross-linker (x86_64-lfs-linux-gnu-ld).

The magic flag:

--target=x86_64-lfs-linux-gnu

That --target tells the build system: "The tools you're building should produce output for x86_64-lfs-linux-gnu, not for the host." Without it, you'd just rebuild your host's binutils — useless for LFS.

What if you skip this? GCC Pass 1 will fail immediately. It needs a linker that understands the target triplet.

Two minutes. Done. On to the big one.

GCC Pass 1 (~8 min)

This is where things get serious. GCC Pass 1 is the largest build in the cross-toolchain — 8 minutes on our VM (8 CPUs, 8GB RAM), 2.4MB of log output.

Before you even run configure, you need to unpack three arithmetic libraries into the GCC source tree:

tar -xf ../mpfr-4.2.1.tar.xz && mv mpfr-4.2.1 mpfr
tar -xf ../gmp-6.3.0.tar.xz && mv gmp-6.3.0 gmp
tar -xf ../mpc-1.3.1.tar.gz && mv mpc-1.3.1 mpc

GCC's configure line is enormous and every flag matters. The output confirms it found our target:

checking target system type... x86_64-lfs-linux-gnu

The limits.h Fixup — A Real Failure

Our build script failed here. The error:

/home/lfs/build-gcc1.sh: line 47: x86_64-lfs-linux-gnu-gcc: command not found

The problem: PATH wasn't set correctly. The freshly built cross-compiler wasn't in the search path when the script tried to run the limits.h fixup step.

Fix: ensure $LFS/tools/bin is in PATH, then run the limits.h fixup separately. GCC ships a limits.h that needs to be combined with the system's version. If you skip this, later builds will pick up incomplete header definitions and fail in confusing ways.

Lesson: when a build script fails partway through, don't just re-run it blindly. Read the error, fix the environment, and run the remaining steps manually.

Linux API Headers (~1 min)

No compilation here. You're installing sanitized kernel headers — the files that define the interface between userspace programs and the Linux kernel. Syscall numbers, ioctl constants, data structures.

make mrproper
make headers
cp -rv usr/include/* $LFS/usr/include

What if you skip this? Glibc won't know what syscalls exist. It literally cannot be built without kernel headers.

These headers don't need to match your running kernel exactly — they define an API that's stable across versions. But they do need to exist.

Glibc (~10 min)

The C library. The big one in terms of importance. Every C program on your LFS system will link against this. 17MB of make output, 10 minutes of build time.

The configure step is where you hold your breath. It needs to find your cross-compiler and be satisfied with it:

checking for x86_64-lfs-linux-gnu-gcc... x86_64-lfs-linux-gnu-gcc
checking whether the compiler supports GNU C... yes
checking version of ld... 2.45, ok
checking if x86_64-lfs-linux-gnu-gcc is sufficient to build libc... yes

That last line — "sufficient to build libc... yes" — is the green light. If it says no, go back and fix GCC Pass 1.

The Sanity Check

After glibc installs, LFS has you run a sanity check. Do not skip it. Compile a trivial program with the cross-compiler and verify it links against the NEW glibc, not the host's. If this check fails, every package you build afterward will be broken. Better to catch it now than debug mysterious segfaults 50 packages later.

What if you skip glibc? Nothing works. Period. Glibc is the C library. Without it, compiled programs have no way to talk to the kernel. No printf, no malloc, no open(). Nothing.

Libstdc++ (~3 min)

C++ standard library. It's technically part of GCC, but it's built separately because it needs glibc to exist first.

The configure output confirms cross-compilation mode:

checking whether we are cross compiling... yes

Our Mistake: Wrong Prefix

First attempt installed libstdc++ to /usr instead of the cross-tools directory. Result: it went onto the HOST system instead of the LFS target. Had to rebuild with the correct DESTDIR=/mnt/lfs.

The lesson is embarrassingly simple: read the configure flags carefully. When you're cross-compiling, the prefix and DESTDIR interact in ways that matter. Getting it wrong doesn't produce an error — it silently installs to the wrong place, and you don't notice until something breaks downstream.

Verification

Cross-toolchain complete. 25 minutes total. Let's make sure it works:

$ x86_64-lfs-linux-gnu-gcc --version
x86_64-lfs-linux-gnu-gcc (GCC) 15.2.0

That's a compiler that runs on your host but produces binaries for your LFS system. It doesn't depend on your host's libc. It uses the glibc you just built, the binutils you just built, the headers you just installed.

You now own your toolchain. Everything from here builds on this foundation.

What Just Happened

You solved the bootstrap problem. You used your host's compiler to build a cross-compiler, then used that cross-compiler to build a C library for your target. The cross-toolchain is self-consistent — it targets x86_64-lfs-linux-gnu and links against the glibc you built, not the host's.

Next up: Chapter 6 — using this cross-toolchain to build temporary tools. More packages, same idea: everything targets the new system.

The hard part wasn't the compilation. It was understanding WHY each piece exists and what breaks without it. Now you know.

Compiled by AI. Proofread by caffeine. ☕


📖 LFS Series Navigation
← Previous: Part 3: Preparing the Build
→ Next: Part 5: Temporary Tools