Fathom-OS Build Series: Chapter 6 - Cross Compiling Temporary Tools

Part of the Fathom-OS Project Log.


Chapter 6 cross-compiles a set of basic utilities using the cross-toolchain built in Chapter 5. These tools install into their final locations in the LFS filesystem but cannot be used yet. Basic tasks still rely on the host’s tools through this whole chapter. They only become usable in Chapter 7 after entering the chroot environment, but every package here has to be built first. This is 17 packages built in sequence, so the whole chapter is covered in one post.

References:


Before You Start: The Pre-Flight Check

Same as Chapter 5, every package in this chapter must be built as the lfs user with the correct environment. If you SSH in, you land as your normal user, not lfs. Switch with a login shell and verify before doing anything:

su - lfs
echo $LFS && echo $LFS_TGT && echo $MAKEFLAGS && umask

You want /lfs, x86_64-lfs-linux-gnu, -j10, 0022. The book repeats the warning that improper LFS setting combined with building as root can render the machine unusable, so this check is not optional.


A Note on Book Versions

Partway through this chapter I noticed the online book showing a newer package version than what I had downloaded. For example, the development version of the book listed Coreutils-9.11 while my verified source from Chapter 3 was 9.10.

This is not a problem and the fix is simple: follow the stable book that matches your release, not the development book. The stable 13.0-systemd book at linuxfromscratch.org/lfs/view/13.0-systemd/ is frozen at the exact package versions you downloaded and verified. The development book runs ahead as maintainers test newer point releases for the next version. Always build the versions you downloaded and verified, since the whole package set was tested together as a known-good combination. Mixing in a newer point release breaks that guarantee.

Lesson: bookmark the stable book matching your sources, not the development tree.


The Universal Build Workflow

Same pattern as Chapter 5 for every package: change to $LFS/sources, extract with tar, enter the directory, build and install, then return and delete the source. The key difference in this chapter is the configure flags now use the cross-compilation pattern, --host=$LFS_TGT and --build=$(config.guess), which tells the build system to use the cross-compiler to produce binaries for the LFS machine. Installs use DESTDIR=$LFS to land in the LFS filesystem.


The Packages

The routine utility builds are listed below with their key commands. Each followed the standard extract, configure, make, make DESTDIR=$LFS install, then remove-source workflow. Build times are small (most 0.1 SBU) unless noted.

6.2 M4

./configure --prefix=/usr --host=$LFS_TGT --build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install

6.3 Ncurses

Builds a tic program on the host first, then the main package. Creates a libncurses.so symlink and edits curses.h to use the wide-character data structures.

mkdir build
pushd build
  ../configure --prefix=$LFS/tools AWK=gawk
  make -C include
  make -C progs tic
  install progs/tic $LFS/tools/bin
popd
./configure --prefix=/usr --host=$LFS_TGT --build=$(./config.guess) \
            --mandir=/usr/share/man --with-manpage-format=normal \
            --with-shared --without-normal --with-cxx-shared \
            --without-debug --without-ada --disable-stripping AWK=gawk
make
make DESTDIR=$LFS install
ln -sv libncursesw.so $LFS/usr/lib/libncurses.so
sed -e 's/^#if.*XOPEN.*$/#if 1/' -i $LFS/usr/include/curses.h

6.4 Bash

Uses --without-bash-malloc to avoid a known segfault issue, and creates the sh symlink.

./configure --prefix=/usr --build=$(sh support/config.guess) \
            --host=$LFS_TGT --without-bash-malloc
make
make DESTDIR=$LFS install
ln -sv bash $LFS/bin/sh

6.5 Coreutils

Enables hostname (needed by the Perl test suite later) and moves chroot to its final location since some programs hardcode the path.

./configure --prefix=/usr --host=$LFS_TGT --build=$(build-aux/config.guess) \
            --enable-install-program=hostname \
            --enable-no-install-program=kill,uptime
make
make DESTDIR=$LFS install
mv -v $LFS/usr/bin/chroot              $LFS/usr/sbin
mkdir -pv $LFS/usr/share/man/man8
mv -v $LFS/usr/share/man/man1/chroot.1 $LFS/usr/share/man/man8/chroot.8
sed -i 's/"1"/"8"/'                    $LFS/usr/share/man/man8/chroot.8

6.6 Diffutils

Supplies gl_cv_func_strcasecmp_works=y directly because the check cannot run during cross-compilation.

./configure --prefix=/usr --host=$LFS_TGT \
            gl_cv_func_strcasecmp_works=y \
            --build=$(./build-aux/config.guess)
make
make DESTDIR=$LFS install

6.7 File

Builds a temporary file command on the host first (it must match the version being built), then compiles pointing at it with FILE_COMPILE. Removes the libtool archive.

mkdir build
pushd build
  ../configure --disable-bzlib --disable-libseccomp \
               --disable-xzlib --disable-zlib
  make
popd
./configure --prefix=/usr --host=$LFS_TGT --build=$(./config.guess)
make FILE_COMPILE=$(pwd)/build/src/file
make DESTDIR=$LFS install
rm -v $LFS/usr/lib/libmagic.la

6.8 Findutils

./configure --prefix=/usr --localstatedir=/var/lib/locate \
            --host=$LFS_TGT --build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install

6.9 Gawk

Removes some unneeded files first.

sed -i 's/extras//' Makefile.in
./configure --prefix=/usr --host=$LFS_TGT --build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install

6.10 Grep

./configure --prefix=/usr --host=$LFS_TGT --build=$(./build-aux/config.guess)
make
make DESTDIR=$LFS install

6.11 Gzip

./configure --prefix=/usr --host=$LFS_TGT
make
make DESTDIR=$LFS install

6.12 Make

./configure --prefix=/usr --host=$LFS_TGT --build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install

6.13 Patch

./configure --prefix=/usr --host=$LFS_TGT --build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install

6.14 Sed

./configure --prefix=/usr --host=$LFS_TGT --build=$(./build-aux/config.guess)
make
make DESTDIR=$LFS install

6.15 Tar

./configure --prefix=/usr --host=$LFS_TGT --build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install

6.16 Xz

Removes the libtool archive.

./configure --prefix=/usr --host=$LFS_TGT --build=$(build-aux/config.guess) \
            --disable-static --docdir=/usr/share/doc/xz-5.8.2
make
make DESTDIR=$LFS install
rm -v $LFS/usr/lib/liblzma.la

6.17 Binutils Pass 2

A major toolchain build at 0.4 SBU. Applies a libtool workaround first, then builds in a separate directory and removes libtool archives and static libraries at the end.

sed '6031s/$add_dir//' -i ltmain.sh
mkdir -v build
cd build
../configure --prefix=/usr --build=$(../config.guess) --host=$LFS_TGT \
             --disable-nls --enable-shared --enable-gprofng=no \
             --disable-werror --enable-64-bit-bfd --enable-new-dtags \
             --enable-default-hash-style=gnu
make
make DESTDIR=$LFS install
rm -v $LFS/usr/lib/lib{bfd,ctf,ctf-nobfd,opcodes,sframe}.{a,la}


6.18 GCC Pass 2

The final and largest build of the chapter at 4.5 SBU, needing 6 GB of disk. Unpacks GMP, MPFR, MPC again, applies the x86_64 lib fix and a POSIX threads override, then builds.

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
case $(uname -m) in
  x86_64)
    sed -e '/m64=/s/lib64/lib/' -i.orig gcc/config/i386/t-linux64
  ;;
esac
sed '/thread_header =/s/@.*@/gthr-posix.h/' \
    -i libgcc/Makefile.in libstdc++-v3/include/Makefile.in
mkdir -v build
cd build
../configure --build=$(../config.guess) --host=$LFS_TGT --target=$LFS_TGT \
             --prefix=/usr --with-build-sysroot=$LFS \
             --enable-default-pie --enable-default-ssp --disable-nls \
             --disable-multilib --disable-libatomic --disable-libgomp \
             --disable-libquadmath --disable-libsanitizer --disable-libssp \
             --disable-libvtv --enable-languages=c,c++ \
             LDFLAGS_FOR_TARGET=-L$PWD/$LFS_TGT/libgcc
make
make DESTDIR=$LFS install
ln -sv gcc $LFS/usr/bin/cc

Issue Caught: Stale Build Directory

When I ran mkdir -v build for GCC Pass 2, it failed with cannot create directory 'build': File exists. A build directory was already present inside the freshly extracted GCC source, full of configured files with timestamps from an earlier session.

The most likely cause is that the GCC source from the Chapter 5 Libstdc++ step was not fully removed, so tonight’s tar extraction unpacked over a leftover tree that still had its old build directory. I cannot say for certain the earlier cleanup was skipped, but the timestamps line up with that session.

The fix is important: a stale, already-configured build directory will corrupt the new build. Do not configure into it. Check the contents first, then clear and recreate it clean:

cd ..
rm -rf build
mkdir -v build
cd build

Lesson for anyone following along: before configuring any package that builds in a build directory, confirm that directory is empty. If mkdir build ever reports the directory already exists, stop and clear it rather than building into whatever was left behind.


Snapshot

With the temporary tools cross-compiled, this is a good point to snapshot before entering the chroot in Chapter 7. A clean rollback point here means the whole temporary toolchain does not have to be rebuilt if something goes wrong later.


What Is Next

The temporary tools are built and installed in the LFS filesystem but cannot be used yet. Chapter 7 enters the chroot environment, where these tools finally become usable and are used to build the additional temporary tools needed before the core system build in Chapter 8.


More updates to follow as each build phase completes.

Previous: Chapter 5: Compiling a Cross-Toolchain | Next: Chapter 7: Entering Chroot and Building Additional Temporary Tools


All session notes and build logs are committed to the private repository at github.com/QuietWireDev/fathom-os.