Fathom-OS Build Series: Chapter 8 Part 3 - System Libraries and the Final GCC

Part of the Fathom-OS Project Log.


This is Part 3 of Chapter 8, the core system build. Part 2 built the toolchain group through MPC. This part builds the system libraries the rest of the userland links against, Attr, Acl, Libcap, and Libxcrypt, then Shadow for password handling, and closes with the final permanent GCC and its full critical test suite. Six packages, two recovery stories worth reading before you hit the same walls.

References:


Resuming the Build: Getting Back to Root

Every Chapter 8 session starts the same way, become root on the host, remount the virtual kernel filesystems, re-enter the chroot. The snapshots are taken outside the chroot with everything unmounted, so this is the cross-session procedure that has to happen before any build work. This session the very first step tripped before any of that.

After SSHing in as the normal user, su - kept returning su: Authentication failure, no matter the password. The cause is the host itself. This build runs on Ubuntu 24.04, and Ubuntu ships with the root account locked by default. There is no root password to match, so su - can never succeed. This build has always used sudo for privileged commands, and the lfs user got its own password earlier in the process. Root never had one.

The fix is sudo -i, which opens a root shell using your own password instead of root’s:

sudo -i
echo $LFS

$LFS came back as /lfs, already set. From there, check the mount state before touching anything, since stacked mounts from a prior session have bitten this build before:

findmnt | grep /lfs

Only the /lfs root partition showed, clean. The single remount block and chroot entry then worked first try:

mount -v --bind /dev $LFS/dev
mount -vt devpts devpts -o gid=5,mode=0620 $LFS/dev/pts
mount -vt proc proc $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run
if [ -h $LFS/dev/shm ]; then
  install -v -d -m 1777 $LFS$(realpath /dev/shm)
else
  mount -vt tmpfs -o nosuid,nodev tmpfs $LFS/dev/shm
fi
chroot "$LFS" /usr/bin/env -i \
    HOME=/root TERM="$TERM" \
    PS1='(lfs chroot) \u:\w\$ ' \
    PATH=/usr/bin:/usr/sbin \
    MAKEFLAGS="-j$(nproc)" \
    TESTSUITEFLAGS="-j$(nproc)" \
    /bin/bash --login

Lesson: on a Ubuntu host, do not reach for su -. The root account is locked, no password works. Use sudo -i to get a root shell with your own password.


The System Libraries

These four libraries provide extended attributes, access control lists, POSIX capabilities, and modern password hashing. The rest of Chapter 8 leans on them.

8.25 Attr

Extended attribute support. The test suite needs a filesystem that supports extended attributes, which ext4 covers, so it runs clean.

./configure --prefix=/usr --disable-static --sysconfdir=/etc \
            --docdir=/usr/share/doc/attr-2.5.2
make
make check
make install

Result: TOTAL 2, PASS 2, no failures. Both the getfattr and attr tests passed on ext4.

8.26 Acl

Access control lists. One known test failure here, test/cp.test, because Coreutils does not have Acl support compiled in yet. It gets resolved when Coreutils is rebuilt later. Do not stop for it.

./configure --prefix=/usr --disable-static --docdir=/usr/share/doc/acl-2.3.2
make
make check
make install

Result: TOTAL 15, PASS 8, SKIP 4, XFAIL 2, FAIL 1. The single FAIL is the expected cp.test. The two XFAIL entries are the NFS tests, expected failures by design. Nothing unexpected.

8.27 Libcap

POSIX capabilities support. No configure step, the prefix and library directory are passed straight to make. The lib=lib option matters on x86_64, it forces the library into /usr/lib instead of /usr/lib64, matching the book’s no-lib64 convention. A sed first strips the static library install.

sed -i '/install -m.*STA/d' libcap/Makefile
make prefix=/usr lib=lib
make test
make prefix=/usr lib=lib install

The test output ran clean, cap_test PASS plus the psx and shared library checks all passing.

This package taught a verification lesson worth recording. Libcap installs its library to /usr/lib but its programs, capsh, getcap, getpcaps, and setcap, go to /usr/sbin, not /usr/bin. A verify check looking in the wrong directory makes a good install look like a failure. When confirming a package landed, check the directory the book’s install rules actually target:

ls -l /usr/sbin/{capsh,getcap,getpcaps,setcap}

All four present. One more note: rebuilding a package is harmless if you are ever unsure it installed. Installs overwrite in place rather than stacking, so a second make install just rewrites the same files.

8.28 Libxcrypt

Modern password hashing, the library Shadow uses for yescrypt and bcrypt. A glibc 2.43 compatibility sed first.

sed -i '/strchr/s/const//' lib/crypt-{sm3,gost}-yescrypt.c
./configure --prefix=/usr --enable-hashes=strong,glibc \
            --enable-obsolete-api=no --disable-static --disable-failure-tokens
make
make check
make install

Result: TOTAL 52, PASS 38, SKIP 14, no failures. The 14 skips are the obsolete API hash tests, skipped because the build sets --enable-obsolete-api=no.


8.29 Shadow

Password and account management. No test suite, but more configuration than the libraries before it, including setting the root password for the system being built. Several seds first, one group to drop the groups program that Coreutils provides a better version of, then one to switch password encryption to YESCRYPT, move the mailbox location to /var/mail, and strip /bin and /sbin from the default PATH.

sed -i 's/groups$(EXEEXT) //' src/Makefile.in
find man -name Makefile.in -exec sed -i 's/groups\.1 / /'   {} \;
find man -name Makefile.in -exec sed -i 's/getspnam\.3 / /' {} \;
find man -name Makefile.in -exec sed -i 's/passwd\.5 / /'   {} \;
sed -e 's:#ENCRYPT_METHOD DES:ENCRYPT_METHOD YESCRYPT:' \
    -e 's:/var/spool/mail:/var/mail:' \
    -e '/PATH=/{s@/sbin:@@;s@/bin:@@}' \
    -i etc/login.defs
touch /usr/bin/passwd
./configure --sysconfdir=/etc --disable-static --with-{b,yes}crypt \
            --without-libbsd --disable-logind --with-group-name-max-length=32
make
make exec_prefix=/usr install
make -C man install-man

The configure summary confirmed both bcrypt and yescrypt encryption enabled, plus ACL and extended attribute support linked in from the two libraries built earlier in this part. The touch /usr/bin/passwd before configure matters, the file’s location is hardcoded in some programs, and if it does not already exist the installer creates it in the wrong place.

Then the post-install configuration:

pwconv
grpconv
mkdir -p /etc/default
useradd -D --gid 999
passwd root

pwconv and grpconv enable shadowed passwords and shadowed group passwords. The useradd -D --gid 999 writes /etc/default/useradd with GROUP=999, matching the users group created back in Chapter 7. The root password set here is the password for the Fathom-OS system itself, separate from anything on the host. Keep it somewhere safe.


8.30 GCC

The final permanent compiler, and the largest package in the chapter at 45 SBU with tests and 6.6 GB of disk. A glibc 2.43 sed and the x86_64 library directory fix come first, then the build runs in a dedicated build directory.

sed -i 's/char [*]q/const &/' libgomp/affinity-fmt.c
case $(uname -m) in
  x86_64)
    sed -e '/m64=/s/lib64/lib/' -i.orig gcc/config/i386/t-linux64
  ;;
esac
mkdir -v build
cd build
../configure --prefix=/usr LD=ld --enable-languages=c,c++ \
             --enable-default-pie --enable-default-ssp --enable-host-pie \
             --disable-multilib --disable-bootstrap --disable-fixincludes \
             --with-system-zlib
make

The Critical Test Suite

The test suite is marked important. It takes a long time, runs as the unprivileged tester user, and does not stop on failures. Prep raises the stack limit, removes a known-failing plugin test, hands the build directory to tester, then runs:

ulimit -s -H unlimited
sed -e '/cpython/d' -i ../gcc/testsuite/gcc.dg/plugin/plugin.exp
chown -R tester .
su tester -c "PATH=$PATH make -k check"
../contrib/test_summary | grep -A7 Summ

The book lists the expected failures up front, and the results matched them exactly:

  • gcc: 9 unexpected failures, the 4 on pr90579.c plus the 5 on analyzer/strchr-1.c that the book documents.
  • libstdc++: 4 unexpected failures, the badnames, names, names_fortify, and experimental/names tests that the glibc 2.43 change is known to break.
  • g++, libatomic, libgomp, libitm: zero unexpected failures.

Thirteen unexpected failures total, every one of them a known, documented case. The book’s guidance is that unless results are vastly different from the published logs, it is safe to continue. This is well within that.

Install and Verify

Install, then fix the ownership of the header directory that tester touched during the test run:

make install
chown -v -R root:root /usr/lib/gcc/$(gcc -dumpmachine)/15.2.0/include{,-fixed}

Then a set of symlinks, the FHS cpp link, the cc man page, and the LTO plugin link for Binutils:

ln -svr /usr/bin/cpp /usr/lib
ln -sv gcc.1 /usr/share/man/man1/cc.1
ln -sfv ../../libexec/gcc/$(gcc -dumpmachine)/15.2.0/liblto_plugin.so /usr/lib/bfd-plugins/

The Toolchain Sanity Checks

This is the part the book stresses. With the final compiler in place, a set of checks confirm it links against the right files. Skipping these risks building the rest of the system on a broken toolchain. Compile a tiny test program with full verbosity, capture the log, then inspect it:

echo 'int main(){}' | cc -x c - -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'

Each check has an expected output, and all of them passed:

  • Program interpreter: /lib64/ld-linux-x86-64.so.2. Correct.
  • Start files: Scrt1.o, crti.o, and crtn.o all found under /usr/lib, all succeeded.
  • Header search: the four paths in order, ending at /usr/include.
  • Linker search: the eight SEARCH_DIR paths, ending at /usr/lib.
  • Libc: attempt to open /usr/lib/libc.so.6 succeeded.
  • Dynamic linker: found ld-linux-x86-64.so.2 at /usr/lib/ld-linux-x86-64.so.2.

Every check matched the book. The toolchain is correct. Clean up the test files and move a misplaced GDB autoload script to finish:

rm -v a.out dummy.log
mkdir -pv /usr/share/gdb/auto-load/usr/lib
mv -v /usr/lib/*gdb.py /usr/share/gdb/auto-load/usr/lib

Snapshot

With GCC done, the core libraries and the final compiler are all in place. This is the checkpoint before the long run of userland packages that follows. Exit the chroot, unmount the virtual filesystems one at a time with children before parents, verify clean with findmnt (only the /lfs root partition should remain), then snapshot.

exit
umount $LFS/dev/shm
umount $LFS/dev/pts
umount $LFS/{sys,proc,run,dev}
findmnt | grep /lfs

Only the /lfs root partition remained, clean. Snapshot taken.


What Is Next

Part 4 continues the long middle run of Chapter 8, the bulk of the userland. Ncurses comes next, then Sed, Psmisc, Gettext, Bison, and onward through the system utilities. That run is large, so it will span more than one post as the earlier parts did.


More updates to follow as each build phase completes.

Previous: Chapter 8 Part 2: The Toolchain | Next: Chapter 8 Part 4: Terminal Libraries and the Shell


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