Part of the Fathom-OS Project Log.
Chapter 5 builds the cross-toolchain that everything else gets compiled with. This is five packages built in a strict order: Binutils Pass 1, GCC Pass 1, Linux API Headers, Glibc, and Libstdc++. Each one depends on the previous being built correctly. Because the packages are sequential steps building one thing rather than standalone milestones, the whole chapter is covered in a single post.
References:
- LFS 13.0-systemd - 5.1 Introduction
- LFS 13.0-systemd - 5.2 Binutils Pass 1
- LFS 13.0-systemd - 5.3 GCC Pass 1
- LFS 13.0-systemd - 5.4 Linux API Headers
- LFS 13.0-systemd - 5.5 Glibc
- LFS 13.0-systemd - 5.6 Libstdc++
Before You Start: The Pre-Flight Check
This trips people up, so it goes first. Every package in Chapters 5 and 6 must be built as the lfs user with the correct environment loaded. If you SSH into the build host the way I do, you land as your normal user, not lfs. The environment variables will be blank or wrong until you switch.
Switch to the lfs user with a login shell:
su - lfs
The - is required. It loads .bash_profile which sets up the clean build environment. Then confirm all four values before doing anything:
echo $LFS && echo $LFS_TGT && echo $MAKEFLAGS && umask
You want to see:
/lfs
x86_64-lfs-linux-gnu
-j10
0022
If any of those are blank, stop and fix the environment before building. The book stresses checking $LFS one last time in the General Compilation Instructions for exactly this reason. Run this check at the start of every session.
The Universal Build Workflow
The General Compilation Instructions define a workflow that applies to every package in this chapter:
- Change to
$LFS/sources - Extract the tarball with
tar, nevercp -Rwhich destroys timestamps - Change into the extracted directory
- Build and install per the package instructions
- Change back to
$LFS/sources - Delete the extracted source directory
Each package page in the book starts at step 4, assuming you already did the extract and enter from the general instructions. That is why the per-package commands begin at mkdir -v build rather than the tar extraction.
5.2 Binutils Pass 1
Binutils is built first because both Glibc and GCC test the linker and assembler to decide which of their own features to enable. The book has you wrap this one package in a time command to establish the 1 SBU baseline that every other build time in the book is measured against.
cd $LFS/sources
tar -xf binutils-2.46.0.tar.xz
cd binutils-2.46.0
mkdir -v build
cd build
Configure, compile, and install, timed:
time { ../configure --prefix=$LFS/tools --with-sysroot=$LFS --target=$LFS_TGT --disable-nls --enable-gprofng=no --disable-werror --enable-new-dtags --enable-default-hash-style=gnu && make && make install; }
On this build host the baseline came in at 44 seconds real time. That is 1 SBU for the rest of the build.
Clean up:
cd $LFS/sources
rm -rf binutils-2.46.0
5.3 GCC Pass 1
GCC needs GMP, MPFR, and MPC. The book has you unpack these three into the GCC source tree and rename them so the GCC build picks them up automatically.
cd $LFS/sources
tar -xf gcc-15.2.0.tar.xz
cd gcc-15.2.0
tar -xf ../mpfr-4.2.2.tar.xz
mv -v mpfr-4.2.2 mpfr
tar -xf ../gmp-6.3.0.tar.xz
mv -v gmp-6.3.0 gmp
tar -xf ../mpc-1.3.1.tar.gz
mv -v mpc-1.3.1 mpc
On x86_64, set the default 64-bit library directory name to lib:
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
Build directory, configure, compile, install:
mkdir -v build
cd build
../configure \
--target=$LFS_TGT \
--prefix=$LFS/tools \
--with-glibc-version=2.43 \
--with-sysroot=$LFS \
--with-newlib \
--without-headers \
--enable-default-pie \
--enable-default-ssp \
--disable-nls \
--disable-shared \
--disable-multilib \
--disable-threads \
--disable-libatomic \
--disable-libgomp \
--disable-libquadmath \
--disable-libssp \
--disable-libvtv \
--disable-libstdcxx \
--enable-languages=c,c++
make
make install
There is a required post-install step that is easy to miss. GCC installs a partial limits.h header. The full version is needed later, so the book has you build it now:
cd ..
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
`dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include/limits.h
Confirm it was created:
ls -l `dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include/limits.h
Clean up:
cd $LFS/sources
rm -rf gcc-15.2.0
5.4 Linux API Headers
This exposes the kernel’s API for Glibc to use. There is no configure or build directory here, just header extraction. Quick at less than 0.1 SBU.
cd $LFS/sources
tar -xf linux-6.18.10.tar.xz
cd linux-6.18.10
make mrproper
make headers
find usr/include -type f ! -name '*.h' -delete
cp -rv usr/include $LFS/usr
Confirm the headers landed:
ls $LFS/usr/include
The book lists asm, asm-generic, drm, linux, misc, mtd, rdma, scsi, sound, video, and xen. Kernel 6.18.10 also installs a few newer directories (cxl, fwctl, regulator). That is expected with a newer kernel, not a problem.
Clean up:
cd $LFS/sources
rm -rf linux-6.18.10
5.5 Glibc
This is the main C library and the most involved package in the chapter. It also has a critical set of sanity checks at the end that confirm the whole toolchain is wired together correctly.
Extract and create the loader symlinks for x86_64:
cd $LFS/sources
tar -xf glibc-2.43.tar.xz
cd glibc-2.43
case $(uname -m) in
i?86) ln -sfv ld-linux.so.2 $LFS/lib/ld-lsb.so.3
;;
x86_64) ln -sfv ../lib/ld-linux-x86-64.so.2 $LFS/lib64
ln -sfv ../lib/ld-linux-x86-64.so.2 $LFS/lib64/ld-lsb-x86-64.so.3
;;
esac
The book notes this ln command is correct even though the syntax looks unusual. Apply the FHS patch:
patch -Np1 -i ../glibc-fhs-1.patch
Build directory, configparms, configure:
mkdir -v build
cd build
echo "rootsbindir=/usr/sbin" > configparms
../configure \
--prefix=/usr \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--disable-nscd \
libc_cv_slibdir=/usr/lib \
--enable-kernel=5.4
A warning about msgfmt being missing may appear during configure. The book says this is harmless.
Compile:
make
The Install Safety Check
Before installing, the book includes a Warning worth taking seriously. The install uses DESTDIR=$LFS to contain itself to the LFS partition. If you were root with an empty $LFS, the command would expand to install glibc straight into your host system and break it. Confirm you are safe first:
whoami
echo $LFS
whoami must return lfs and echo $LFS must return /lfs. Only then install:
make DESTDIR=$LFS install
Fix a hardcoded path in the ldd script:
sed '/RTLDLIST=/s@/usr@@g' -i $LFS/usr/bin/ldd
Toolchain Sanity Checks
This is the most important verification in Chapter 5. Six checks confirm the cross-compiler, linker, and libc all work together. Generate the test log:
echo 'int main(){}' | $LFS_TGT-gcc -x c - -v -Wl,--verbose &> dummy.log
Check 1, the program interpreter. The path must not contain /lfs:
readelf -l a.out | grep ': /lib'
Expected: [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
Check 2, the start files:
grep -E -o "$LFS/lib.*/S?crt[1in].*succeeded" dummy.log
Expected: three lines for Scrt1.o, crti.o, crtn.o ending in succeeded.
Check 3, the header search directories:
grep -B3 "^ $LFS/usr/include" dummy.log
Expected: the search block ending in /lfs/usr/include.
Check 4, the linker search paths. Every path must begin with =:
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
Check 5, the correct libc:
grep "/lib.*/libc.so.6 " dummy.log
Expected: attempt to open /lfs/usr/lib/libc.so.6 succeeded
Check 6, the dynamic linker:
grep found dummy.log
Expected: found ld-linux-x86-64.so.2 at /lfs/usr/lib/ld-linux-x86-64.so.2
The book’s examples show /mnt/lfs because that is its example mount point. This build uses /lfs, so the paths show /lfs instead. That difference is expected and correct.
Clean up test files and the source:
rm -v a.out dummy.log
cd $LFS/sources
rm -rf glibc-2.43
5.6 Libstdc++
The last package in the chapter. Libstdc++ is part of the GCC sources, so the GCC tarball is unpacked again. Its installation was deferred from GCC Pass 1 because it depends on Glibc, which is now in place.
cd $LFS/sources
tar -xf gcc-15.2.0.tar.xz
cd gcc-15.2.0
mkdir -v build
cd build
Configure points at ../libstdc++-v3/configure, not the top-level configure:
../libstdc++-v3/configure \
--host=$LFS_TGT \
--build=$(../config.guess) \
--prefix=/usr \
--disable-multilib \
--disable-nls \
--disable-libstdcxx-pch \
--with-gxx-include-dir=/tools/$LFS_TGT/include/c++/15.2.0
make
make DESTDIR=$LFS install
Remove the libtool archive files, which are harmful for cross-compilation:
rm -v $LFS/usr/lib/lib{stdc++{,exp,fs},supc++}.la
Clean up:
cd $LFS/sources
rm -rf gcc-15.2.0
Snapshot
With the cross-toolchain complete this is a good point to take a VM snapshot before moving on. A clean rollback point here means that if anything goes wrong in Chapter 6, the toolchain does not have to be rebuilt from scratch.
What Is Next
The cross-toolchain is built and verified. Chapter 6 uses it to cross-compile the temporary tools needed before entering the chroot environment. The book notes that if any package in Chapter 6, especially Binutils Pass 2 or GCC Pass 2, fails to build, it points to a problem in the Binutils, GCC, or Glibc work just completed. The six sanity checks passing is a strong sign that will not happen.
More updates to follow as each build phase completes.
Previous: Chapter 4: Final Preparations | Next: Chapter 6: Cross Compiling Temporary Tools
All session notes and build logs are committed to the private repository at github.com/QuietWireDev/fathom-os.





