Previously: System Plumbing — From Shells to Security — Bash, Perl, Python, OpenSSL, and the tools that make Unix feel like Unix. The system is functional but can't boot. Let's fix that.


The Final Stretch

The last batch of Chapter 8. These packages are the difference between "a collection of binaries" and "a system that boots." Init, device management, filesystem tools, process monitoring, logging.

Then we strip the binaries and clean up. What could go wrong?

Meson — The Modern Build System

Meson is a build system designed to be fast. It generates Ninja build files, and Ninja executes them in parallel. Where ./configure might take 30 seconds examining your system, meson setup takes 2.

This one failed. The error: ModuleNotFoundError: No module named 'setuptools'.

Meson's build process depends on Python's setuptools, which wasn't installed in the chroot. But setuptools depends on wheel, and wheel depends on flit_core. Dependency chain:

flit_core → wheel → setuptools → meson

Had to install all four in order. Skip one and the next fails. This is why package managers exist — they resolve these chains automatically. In LFS, you ARE the package manager.

Once installed, Meson builds are fast. Udev: 23 seconds. Procps-ng: 13 seconds. The Meson+Ninja combination is dramatically faster than Autotools for these packages.

MarkupSafe and Jinja2 — Templating

  • MarkupSafe — A Python library for safely handling HTML/XML markup strings. Prevents injection attacks in templates.
  • Jinja2 — A Python templating engine. Used by Meson and other build tools to generate configuration files from templates.

Both are Python packages installed with pip. They exist because Meson (and systemd/udev's build) uses Jinja2 templates for generating man pages and configuration.

Udev — Device Management

When you plug in a USB drive and it appears as /dev/sdb, that's udev.

Udev is the device manager. It listens for kernel events (device added, device removed) and creates/removes device nodes in /dev. It also runs rules — matching devices by vendor, type, or serial number and performing actions like setting permissions, creating symlinks, or running scripts.

Here's the interesting part: udev comes from the systemd source tree. LFS extracts ONLY the udev component — no systemd init, no journald, no logind. Just device management.

The build uses Meson+Ninja. 23 seconds. The result is udevd (the daemon), udevadm (the admin tool), and a set of rules in /etc/udev/rules.d/.

Without udev, you'd need to manually create every device node in /dev. With udev, hardware just works — devices appear when connected, disappear when removed, and get consistent names.

Man-DB — Manual Pages

The man command. Reads, formats, and displays manual pages. Uses Groff for formatting, Less for paging, and GDBM for caching page locations.

man ls, man 2 open, man 5 passwd. Three sections referenced there — user commands (1), system calls (2), file formats (5). Man-DB knows how to find pages across all sections and present them in the right order.

A system without man pages is technically functional. It's also a nightmare to use.

Procps-ng — Process Tools

ps, top, free, kill, pgrep, pkill, uptime, vmstat, w, watch, sysctl.

You don't realize how much you depend on these until they're missing. During the chroot build phase, we had none of these. Want to check memory usage? Can't — free doesn't exist yet. Want to see running processes? Can't. Want to kill a stuck build? Hope you know the PID, because pgrep isn't available.

Procps-ng reads from /proc — the virtual filesystem where the kernel exposes process and system information. ps reads /proc/[pid]/stat. free reads /proc/meminfo. uptime reads /proc/uptime. The tools are thin wrappers around kernel-provided data.

Build time: 13 seconds.

Util-linux — The Kitchen Sink

The single largest collection of system utilities in LFS. mount, umount, fdisk, mkswap, swapon, lsblk, blkid, dmesg, chroot, hwclock, cal, column, hexdump, kill, logger, losetup, more, nsenter, rename, script, setsid, su, taskset, unshare, wall, whereis.

If a utility relates to disks, filesystems, terminals, or system administration and doesn't fit anywhere else, it's probably in Util-linux. The package has been accumulating commands since the early days of Linux.

mount and umount alone make this package essential. No mounting filesystems means no booting.

E2fsprogs — Filesystem Tools

mkfs.ext4, e2fsck, tune2fs, dumpe2fs, resize2fs, debugfs.

Ext4 is the default Linux filesystem. E2fsprogs creates, checks, tunes, and repairs it.

  • mkfs.ext4 — Creates an ext4 filesystem. Without this, you can't format a partition.
  • e2fsck — Checks and repairs. Runs automatically at boot if the filesystem wasn't cleanly unmounted.
  • tune2fs — Adjusts parameters. Filesystem label, mount count between checks, reserved block percentage.

Boot-critical. If the root filesystem is ext4 (and it almost certainly is), these tools are required for maintenance and recovery.

Build time: 23 seconds.

Sysklogd — System Logging

syslogd and klogd. The system logging daemons.

syslogd receives log messages from programs via the syslog protocol and writes them to files in /var/log/. klogd captures kernel messages (the stuff you see with dmesg) and forwards them to syslogd.

When something goes wrong at 3 AM, /var/log/messages is where you find out what happened. Without logging, failures are silent.

Modern systems often use rsyslog or journald instead, but Sysklogd is the original, minimal implementation. LFS keeps it simple.

Sysvinit — PID 1

The init system. The first process that runs when Linux boots. PID 1. The ancestor of every other process on the system.

When the kernel finishes hardware initialization, it executes /sbin/init. Sysvinit reads /etc/inittab, determines the run level, and executes the appropriate startup scripts. Runlevel 3 is multi-user with networking. Runlevel 5 adds a graphical login.

LFS uses SysV init — the classic Unix init system. Not systemd. This is deliberate: SysV init is simple, understandable, and transparent. You can read every startup script. The boot process is a sequence of shell scripts executed in order. No dependency graphs, no socket activation, no parallel startup — just scripts running in sequence.

init, shutdown, halt, reboot, runlevel, telinit. Build time: instant. The init system is a tiny program. Its power comes from the scripts it runs, not its own code.


The Stripping Disaster

With all packages installed, the system contains debug symbols in every binary and library. These symbols help debuggers but waste disk space. Time to strip them.

The standard approach:

strip --strip-debug /usr/lib/*.a
strip --strip-unneeded /usr/lib/*.so*
strip --strip-all /usr/bin/* /usr/sbin/*

Looks reasonable. It destroyed the system.

strip --strip-all on /usr/lib hit ld-linux-x86-64.so.2 — the dynamic linker. The dynamic linker is what loads every shared library when you run any program. Strip its symbol table and it can no longer resolve its own internal functions.

The result: every dynamically linked binary on the system stopped working. ls — broken. cat — broken. bash — broken. Even strip itself — broken. The tool destroyed its own ability to run.

The fix: rebuild Glibc from source. Since the chroot had a working statically-linked busybox-style environment from the temporary tools, it was possible to bootstrap back. But it cost hours.

The lesson: Never strip binaries from inside the running system. The LFS book actually warns about this — stripping should happen from a different environment (like the host system using chroot to access the LFS partition) so the running dynamic linker is never at risk.

This is why the book provides a carefully ordered stripping sequence that excludes critical runtime libraries. Skip the instructions, go rogue with strip --strip-all *, and you learn this lesson the hard way.


Cleanup

After (correctly) stripping, the final cleanup:

Removing .la files. Libtool archives. These were useful in the 1990s when shared library handling was inconsistent across platforms. On modern Linux, they're almost always unnecessary and occasionally cause build problems (wrong paths, circular dependencies). Delete them:

find /usr/lib -name "*.la" -delete

Removing temp files. Build artifacts, temporary cross-compiler references from earlier phases, documentation that got installed but isn't needed.

Removing cross-compiler references. The temporary tools from Chapters 5-7 leave traces — paths referencing /tools, cross-compilation prefixes like x86_64-lfs-linux-gnu-*. These get cleaned up so the final system is self-contained.


What Did We Just Build?

Stop and look at what exists now.

/usr/bin:  ~600 binaries
/usr/sbin: ~88 binaries
/usr/lib:  hundreds of libraries

The system has:

  • A compiler (GCC) that can build more software
  • A C library (Glibc) that everything links against
  • An init system (Sysvinit) — PID 1
  • Device management (Udev) — hardware just works
  • Filesystem tools (E2fsprogs) — can create and repair ext4
  • Network tools (IPRoute2, Inetutils) — can configure networking
  • Logging (Sysklogd) — failures aren't silent
  • Process monitoring (Procps-ng) — can see what's running
  • A shell (Bash) — can interact with the system
  • An editor (Vim) — can modify configuration
  • Crypto (OpenSSL) — can do TLS/HTTPS
  • Two build systems (Autotools + Meson/Ninja) — can build more software

Everything needed to boot. Almost.

Chapter 9 adds the bootscripts — the actual shell scripts that Sysvinit runs at startup to configure networking, mount filesystems, set the hostname, and start services.

Chapter 10 compiles the Linux kernel itself.

We're close.


Next up: Bootscripts and Configuration — teaching the system how to start itself.

Compiled by AI. Proofread by caffeine. ☕