Linux From Scratch
Intro
What’s up guys, my name is Tony, and today, I’m gonna give you a slow and painful guide on installing Linux from Scratch.
Chapter 1 - Introduction:
Install Gentoo ISO
For the iso image, we’re going to use the gentoo gui live cd, and drop into a tty immediatly, and ssh to it from another machine. This is because it will already have all of the required tools needed to compile all the software, and other than that it is relatively minimal.
Setup ISO, and enable SSH
Let’s boot into our Gentoo Live CD. I see this kde nonsense, lets just drop into a tty right on with Control Alt F2, and lets do a few things.
Let’s begin by creating a root password on this machine, and switching to root, and enabling ssh. Now we check the ip here with ip a, and then we can ssh to it from our other machine.
sudo passwd root
su
rc-service sshd start
ip a
ssh root@192.168.x.x
Testing Versions:
We need to prepare for this by testing the versions of our toolchain. Let’s run this script here on the lfs manual like so:
And here’s the output:
bash check-version.sh
OK: Coreutils 9.7 >= 8.1
OK: Bash 5.3.3 >= 3.2
OK: Binutils 2.45 >= 2.13.1
OK: Bison 3.8.2 >= 2.7
OK: Diffutils 3.12 >= 2.8.1
OK: Findutils 4.10.0 >= 4.2.31
OK: Gawk 5.3.2 >= 4.0.1
OK: GCC 14.3.1 >= 5.4
OK: GCC (C++) 14.3.1 >= 5.4
OK: Grep 3.12 >= 2.5.1a
OK: Gzip 1.14 >= 1.3.12
OK: M4 1.4.20 >= 1.4.10
OK: Make 4.4.1 >= 4.0
OK: Patch 2.8 >= 2.5.4
OK: Perl 5.40.2 >= 5.8.8
OK: Python 3.13.5 >= 3.4
OK: Sed 4.9 >= 4.1.5
OK: Tar 1.35 >= 1.22
OK: Texinfo 7.2 >= 5.0
OK: Xz 5.8.1 >= 5.0.0
OK: Linux Kernel 6.12.47 >= 5.4
OK: Linux Kernel supports UNIX 98 PTY
Aliases:
OK: awk is GNU
OK: yacc is Bison
OK: sh is Bash
Compiler check:
OK: g++ works
OK: nproc reports 12 logical cores are available
And we are ready to move on to the next step.
Chapter 2 - Preparing the Host System:
Create Partitions
This part might be familiar to those of you who have installed gentoo, arch, or even nix using the minimal iso. Let’s hop into cfdisk and create our partitions.
lsblk will tell us what our disk is named. Mine is /dev/vda Let’s run cfdisk on /dev/vda, your disk might be named something else, such as /dev/sda Let’s select gpt for the label type because we are on a UEFI system.
For the partitions, let’s create a 1G boot partition, a 4G swap partition, and the rest can be the root partition.
Lets write and quit here, and move on to creating the filesystems
vda 252:0 0 50G 0 disk
├─vda1 252:1 0 1G 0 part
├─vda2 252:2 0 4G 0 part
└─vda3 252:3 0 45G 0 part
Make Filesystems
Lets make those filesystems.
For the root partition: For the boot partition: And for that swap:
mkfs.ext4 /dev/vda3
mkfs.fat -F 32 /dev/vda1
mkswap /dev/vda2
vda 252:0 0 50G 0 disk
├─vda1 252:1 0 1G 0 part
├─vda2 252:2 0 4G 0 part
└─vda3 252:3 0 45G 0 part
livecd ~ # mkfs.ext4 /dev/vda3
mke2fs 1.47.3 (8-Jul-2025)
Discarding device blocks: done
Creating filesystem with 11795968 4k blocks and 2949120 inodes
Filesystem UUID: cf482c8d-d5e7-4cd5-82b9-1fe1e21156e4
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424
Allocating group tables: done
Writing inode tables: done
Creating journal (65536 blocks): done
Writing superblocks and filesystem accounting information: done
livecd ~ # mkfs.fat -F 32 /dev/vda1
mkfs.fat 4.2 (2021-01-31)
livecd ~ # mkswap /dev/vda2
Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
no label, UUID=9ad48deb-4292-4267-95a3-0b189a8d2acd
livecd ~ #
Let’s move on to mounting the filesystems.
Exporting $LFS and confirming Umask:
So this is where we deviate from the arch installs, and what have you. We need to export the $LFS variable here mainly for convenience, because we will be referencing the path of $LFS throughout this installation.
So lets run
export LFS=/mnt/lfs
And we need to ensure `umask` is set to `022` so that newly created files and directories are only writable by their owner, but are readable and searchable by anyone.
so lets run
umask 022
And we look good here, we can echo $LFS and we see umask is correct. Let’s create the filesystems.
Mounting Partitions
Let’s create the directory here for lfs and mount it. We can run
mount --mkdir /dev/vda3 $LFS
And for our root partition:
mount --mkdir /dev/vda1 $LFS/boot/efi
And lastly, for our swap:
swapon /dev/vda2
We can confirm this worked with the following commands:
livecd ~ # mount | grep vda
/dev/vda3 on /mnt/lfs type ext4 (rw,relatime)
/dev/vda1 on /mnt/lfs/boot/efi type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
livecd ~ # lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 252:0 0 50G 0 disk
├─vda1 252:1 0 1G 0 part /mnt/lfs/boot/efi
├─vda2 252:2 0 4G 0 part [SWAP]
└─vda3 252:3 0 45G 0 part /mnt/lfs
Following the book it suggests to chown the $LFS directory here:
chown root:root $LFS
chmod 755 $LFS
And confirm with ls -la $LFS
livecd ~ # ls -la $LFS
total 24
drwxr-xr-x 4 root root 4096 Oct 17 05:05 .
drwxr-xr-x 1 root root 60 Oct 17 05:03 ..
drwxr-xr-x 3 root root 4096 Oct 17 05:05 boot
drwx------ 2 root root 16384 Oct 17 04:48 lost+found
And it looks good to go! We are ready to move to Chapter 3. Sources.
Chapter 3 - Packages and Patches:
Sources:
We need to make a directory for the sources here, like so:
mkdir -v $LFS/sources
And we need to make it ‘sticky’, so that even if any user can have write permissions on a directory, only the owner can delete a file within the directory. We can achieve this with:
chmod -v a+wt $LFS/sources
Which will look like this:
livecd ~ # mkdir -v $LFS/sources
mkdir: created directory '/mnt/lfs/sources'
livecd ~ # chmod -v a+wt $LFS/sources
mode of '/mnt/lfs/sources' changed from 0755 (rwxr-xr-x) to 1777 (rwxrwxrwt)
livecd ~ #
Retrieve wget-list and md5sums
Let’s cd into the sources directory here and download the sources.
cd $LFS/sources
So we can grab all of the sources here from a wget list. let’s wget the wget list (inception) like so: right click this link and copy link as, and do:
Let’s also grab the md5sums so we can verify the download succeeded with no corrupted data like so:
wget https://www.linuxfromscratch.org/lfs/view/12.4/wget-list-sysv
wget https://www.linuxfromscratch.org/lfs/downloads/stable/md5sums
And our ouput should look like this:
livecd /mnt/lfs/sources # wget https://www.linuxfromscratch.org/lfs/view/12.4/wget-list-sysv
--2025-10-17 09:14:50-- https://www.linuxfromscratch.org/lfs/view/12.4/wget-list-sysv
Resolving www.linuxfromscratch.org... 208.118.68.85
Connecting to www.linuxfromscratch.org|208.118.68.85|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6107 (6.0K)
Saving to: ‘wget-list-sysv’
wget-list-sysv 100%[===========================================================>] 5.96K --.-KB/s in 0s
2025-10-17 09:14:50 (9.62 GB/s) - ‘wget-list-sysv’ saved [6107/6107]
Grab All Packages from wget-list
So we can see what this wget list is by running less on it, and we see its just a list of urls with each package we need. So let’s just download them here with this wget command:
And now for the fun part…
wget --input-file=wget-list-sysv --continue --directory-prefix=$LFS/sources
This will iterate through the wget list and grab all the files in it. I will see you in a minute.
Check md5sums:
Alright, looks like our wget command finished, so lets test the md5sums here, this will let us know if all of the packages are downloaded to, as well as confirm if any of them are corrupted or not.
md5sum -c md5sums | less
acl-2.3.2.tar.xz: OK
attr-2.5.2.tar.gz: OK
autoconf-2.72.tar.xz: OK
automake-1.18.1.tar.xz: OK
bash-5.3.tar.gz: OK
bc-7.0.3.tar.xz: OK
binutils-2.45.tar.xz: OK
bison-3.8.2.tar.xz: OK
bzip2-1.0.8.tar.gz: OK
coreutils-9.7.tar.xz: OK
dejagnu-1.6.3.tar.gz: OK
diffutils-3.12.tar.xz: OK
e2fsprogs-1.47.3.tar.gz: OK
elfutils-0.193.tar.bz2: OK
expat-2.7.1.tar.xz: OK
expect5.45.4.tar.gz: OK
file-5.46.tar.gz: OK
findutils-4.10.0.tar.xz: OK
flex-2.6.4.tar.gz: OK
flit_core-3.12.0.tar.gz: OK
gawk-5.3.2.tar.xz: OK
gcc-15.2.0.tar.xz: OK
gdbm-1.26.tar.gz: OK
gettext-0.26.tar.xz: OK
glibc-2.42.tar.xz: OK
gmp-6.3.0.tar.xz: OK
gperf-3.3.tar.gz: OK
grep-3.12.tar.xz: OK
groff-1.23.0.tar.gz: OK
grub-2.12.tar.xz: OK
gzip-1.14.tar.xz: OK
iana-etc-20250807.tar.gz: OK
As we see here, our md5sums are all verified, meaning we have all the necessary packages. The book recommends to change ownership of these files to root, but we are root so they are already owned by us. If you downloaded them as a non root user, just run this to be safe
chown root:root $LFS/sources/*
We’re ready to move onto Chapter 4.
Chapter 4 - Final Preparations:
Creating the LFS Filesystem
So we want to actually create the LFS filesystem here, so lets create some directories like so:
mkdir -pv $LFS/{etc,var} $LFS/usr/{bin,lib,sbin}
Expected output:
mkdir: created directory '/mnt/lfs/etc'
mkdir: created directory '/mnt/lfs/var'
mkdir: created directory '/mnt/lfs/usr'
mkdir: created directory '/mnt/lfs/usr/bin'
mkdir: created directory '/mnt/lfs/usr/lib'
mkdir: created directory '/mnt/lfs/usr/sbin'
And to create symlinks here:
for i in bin lib sbin; do
ln -sv usr/$i $LFS/$i
done
Expected output:
'/mnt/lfs/bin' -> 'usr/bin'
'/mnt/lfs/lib' -> 'usr/lib'
'/mnt/lfs/sbin' -> 'usr/sbin'
And for the lib64 directory in my case (because I am on x86_64 technology)
case $(uname -m) in
x86_64) mkdir -pv $LFS/lib64 ;;
esac
Expected output:
mkdir: created directory '/mnt/lfs/lib64'
And for the tools directory, we will use this to keep some of the temporary programs we will be compiling.
mkdir -pv $LFS/tools
Expected output:
mkdir: created directory '/mnt/lfs/tools'
And we’re ready to add the LFS user.
Adding LFS user:
We want an LFS user because running things as root could break our system, and if we do it as LFS, we dont risk nefariously affecting the host iso in any way.
So lets add the group and create the user like so:
groupadd lfs
useradd -s /bin/bash -g lfs -m -k /dev/null lfs
And add a passwd so we can log in to it:
passwd lfs
And we want lfs to have full access to all lfs directories so lets run:
chown -v lfs $LFS/{usr{,/*},var,etc,tools}
case $(uname -m) in
x86_64) chown -v lfs $LFS/lib64 ;;
esac
Expected output:
livecd /mnt/lfs/sources # chown -v lfs $LFS/{usr{,/*},var,etc,tools}
changed ownership of '/mnt/lfs/usr' from root to lfs
changed ownership of '/mnt/lfs/usr/bin' from root to lfs
changed ownership of '/mnt/lfs/usr/lib' from root to lfs
changed ownership of '/mnt/lfs/usr/sbin' from root to lfs
changed ownership of '/mnt/lfs/var' from root to lfs
changed ownership of '/mnt/lfs/etc' from root to lfs
changed ownership of '/mnt/lfs/tools' from root to lfs
livecd /mnt/lfs/sources # case $(uname -m) in
> x86_64) chown -v lfs $LFS/lib64 ;;
> esac
changed ownership of '/mnt/lfs/lib64' from root to lfs
And we’re ready to switch into our new user. We can ignore this warning here, since gentoo wont give us issues, but take a look at this warning if you are on a different host.
su - lfs
And now we’re LFS, so we are ready to setup the environment
Setup Environment for lfs user
Let’s create a bash profile like so:
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF
And as we see here, this will ensure that no unwanted and potentially hazardous environment variables from the host system leak into the build environment
And lets create the bashrc here like so:
cat > ~/.bashrc << "EOF"
set +h
umask 022
LFS=/mnt/lfs
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/usr/bin
if [ ! -L /bin ]; then PATH=/bin:$PATH; fi
PATH=$LFS/tools/bin:$PATH
CONFIG_SITE=$LFS/usr/share/config.site
export LFS LC_ALL LFS_TGT PATH CONFIG_SITE
EOF
Main things here are setting +h which turns off the hashing feature, which we need to disable in order to force our shell to search for the proper path whenever a program is to be run. The rest of these options are things we already did previously such as umask, or exporting the LFS path, or can be explained here.
For this important block here, we dont need to do this on gentoo because the usrs environment is already clean of the /etc/bash.bashrc, but we can run it anyway to be safe. we have to run it as root, though, so lets do this:
logout
[ ! -e /etc/bash.bashrc ] || mv -v /etc/bash.bashrc /etc/bash.bashrc.NOUSE
su - lfs
No output means we are in the clear.
Now to update our makeflags to reflect our cpus. Our cpu has 12 logical cores, so we can use 12 instead of 32… or better yet we can just automatically use nproc to get the true value of logical cores and append it to our bashrc like so, and then source our bash_profile like so:
cat >> ~/.bashrc << "EOF"
export MAKEFLAGS=-j$(nproc)
EOF
source ~/.bash_profile
SBUs
This will be a unit that measures how long it takes to compile/install a package. We can use the time command to test that when we install Binutils Pass 1.
Test Suites
And here it just says that test suites can’t run in Chapters 5-6 because we’re cross-compiling. We’ll run them later when building inside the LFS chroot environment where we are agnostic to the host machine.
Chapter 5 - Compiling a Cross Toolchain:
Overview
First we compile a cross toolchain, then use it to build temporary tools, and finally install the permanent system. We separate these stages because cross-compilation prevents any dependencies on the host system, ensuring a clean, self-contained build.
To build these tools, its going to be a 3 step method:
- untar the source tarball
- follow instructions on lfs book to install tool
- cd back into sources and clean it up
We will repeat this process over 100 times in lfs, so I’ll go slow for the first few and as soon as I finish 3-4 of them, you will sort of understand the pattern here.
I’ll also do my best to explain what each tool is, and thats the real benefit of LFS in my opinion, you learn what ever tool on your system is and what it does.
So let’s jump straight into Binutils.
Binutils-2.45 - Pass 1
The Binutils package contains a linker, an assembler, and other tools for handling object files.
Some of the binutils you may be familiar with are: strings, size, strip.
This will be a pattern we see.. extract the tool, cd into it, and run the commands from the handbook to create/compile the tool.
cd $LFS/sources
tar -xvf binutils
cd binutils
mkdir -v build
cd build
So lets actually time this compilation to see what we’re working with: Prepare for compilation:
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; }
As we see at the top here, binutils is 1 sbu, so that means we can use it as a reference. so if glibc is 2 sbus, we can expect it to take 2 times the amount of time that binutils took.
Once thats complete, lets get back to the sources directory, and remove the binutils directory like so:
cd ../..
rm -Rf binutils
GCC-15.2.0 - Pass 1
The GCC package contains the GNU compiler collection, which includes the C and C++ compilers. You are probabily familiar with this.. if you have ever compiled any C code, you probably used gcc.
This is our first pass of GCC, and this will be simply so that we can cross compile all the necessary tools in chapter 5.
tar -xvf gcc-tab
cd gcc-tab
Now in here we have to extract subpackages, and rename them like so:
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
Run these individually in case one errors out And since were on 64 bit architecture lets run this:
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
And now time to build gcc…
mkdir -v build
cd build
time { ../configure \
--target=$LFS_TGT \
--prefix=$LFS/tools \
--with-glibc-version=2.42 \
--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; }
And we need to recreate this header file:
cd ..
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
`dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include/limits.h
and cleanup:
cd ..
rm -rf gcc-tab
Linux-6.16.1 API Headers:
The Linux API Headers provide the kernel interface that Glibc uses to interact with the Linux kernel.
Ensure no stale files embedded in this package:
make mrproper
Expect no output above, and then run:
make headers
find usr/include -type f ! -name '*.h' -delete
cp -rv usr/include $LFS/usr
So all we’re doing here is creating the linux header files, and copying them to the lfs/usr directory.
cd ..
rm -rf linux-tab
Glibc-2.42
This is the main C library.
First we need to create symlinks for ‘LSB Compliance’ which is the linux standard base, which depends on your architecture so we can run this case statement here to create symlinks for where the dynamic linkers need to point to respectively:
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
And to apply this patch here:
patch -Np1 -i ../glibc-2.42-fhs-1.patch
Expected output:
patching file Makeconfig
Hunk #1 succeeded at 262 (offset 12 lines).
patching file nscd/nscd.h
Hunk #1 succeeded at 160 (offset 48 lines).
patching file nss/db-Makefile
Hunk #1 succeeded at 21 (offset -1 lines).
patching file sysdeps/generic/paths.h
patching file sysdeps/unix/sysv/linux/paths.h
And to build glibc:
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
make
make DESTDIR=$LFS install
sed '/RTLDLIST=/s@/usr@@g' -i $LFS/usr/bin/ldd
Sanity checks for the symlinks:
echo 'int main(){}' | $LFS_TGT-gcc -x c - -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'
Expected output:
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
Ensure we are setup to choose the correct start files:
grep -E -o "$LFS/lib.*/S?crt[1in].*succeeded" dummy.log
Expected output:
/mnt/lfs/lib/../lib/Scrt1.o succeeded
/mnt/lfs/lib/../lib/crti.o succeeded
/mnt/lfs/lib/../lib/crtn.o succeeded
Verify that the compiler is searching for the correct header files:
grep -B3 "^ $LFS/usr/include" dummy.log
Expected output:
#include <...> search starts here:
/mnt/lfs/tools/lib/gcc/x86_64-lfs-linux-gnu/15.2.0/include
/mnt/lfs/tools/lib/gcc/x86_64-lfs-linux-gnu/15.2.0/include-fixed
/mnt/lfs/usr/include
Next, verify that the new linker is being used with the correct search paths:
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
Expected output:
SEARCH_DIR("=/mnt/lfs/tools/x86_64-lfs-linux-gnu/lib64")
SEARCH_DIR("=/usr/local/lib64")
SEARCH_DIR("=/lib64")
SEARCH_DIR("=/usr/lib64")
SEARCH_DIR("=/mnt/lfs/tools/x86_64-lfs-linux-gnu/lib")
SEARCH_DIR("=/usr/local/lib")
SEARCH_DIR("=/lib")
SEARCH_DIR("=/usr/lib");
Ensure correct version of glibc:
grep "/lib.*/libc.so.6 " dummy.log
>> attempt to open /mnt/lfs/usr/lib/libc.so.6 succeeded
Make sure gcc is using correct dynamic linker:
grep found dummy.log
>> found ld-linux-x86-64.so.2 at /mnt/lfs/usr/lib/ld-linux-x86-64.so.2
Clean up dummy.log
rm -v a.out dummy.log
cd ../..
rm -Rf glibc
Libstdc++ from GCC-15.2.0
Lib standard C++ is needed to compile c++ code.
tar -xvf gcc-15.2.0...
cd gcc...
mkdir -v build; cd build
../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
rm -v $LFS/usr/lib/lib{stdc++{,exp,fs},supc++}.la
Clean this up:
cd ../..
rm -rf gcc-tab
And that is the end of chaper 5. We are ready to move on to cross compiling temporary tools!
Chapter 6 - Cross Compiling Temporary Tools:
“This chapter shows how to cross-compile basic utilities using the just built cross-toolchain. Those utilities are installed into their final location, but cannot be used yet. Basic tasks still rely on the host’s tools. Nevertheless, the installed libraries are used when linking.” - LFS manual
M4-1.4.20
cd $LFS/sources
tar -xf m4-1.4.20.tar.xz
cd m4-1.4.20
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf m4-1.4.20
Ncurses-6.5-20250809
tar -xf ncurses-6.5-20250809.tar.gz
cd ncurses-6.5-20250809
sed -i s/mawk// configure
mkdir build
pushd build
../configure
make -C include
make -C progs tic
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 \
--enable-widec
make
make DESTDIR=$LFS TIC_PATH=$(pwd)/build/progs/tic install
ln -sv libncursesw.so $LFS/usr/lib/libncurses.so
sed -e 's/^#if.*XOPEN.*$/#if 1/' \
-i $LFS/usr/include/curses.h
cd $LFS/sources
rm -rf ncurses-6.5-20250809
Bash-5.3
tar -xf bash-5.3.tar.gz
cd bash-5.3
./configure --prefix=/usr \
--build=$(sh support/config.guess) \
--host=$LFS_TGT \
--without-bash-malloc \
bash_cv_strtold_broken=no
make
make DESTDIR=$LFS install
ln -sv bash $LFS/bin/sh
cd $LFS/sources
rm -rf bash-5.3
Coreutils-9.7
tar -xf coreutils-9.7.tar.xz
cd coreutils-9.7
./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
cd $LFS/sources
rm -rf coreutils-9.7
Diffutils-3.12
tar -xf diffutils-3.12.tar.xz
cd diffutils-3.12
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(./build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf diffutils-3.12
File-5.46
tar -xf file-5.46.tar.gz
cd file-5.46
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
cd $LFS/sources
rm -rf file-5.46
Findutils-4.10.0
tar -xf findutils-4.10.0.tar.xz
cd findutils-4.10.0
./configure --prefix=/usr \
--localstatedir=/var/lib/locate \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf findutils-4.10.0
Gawk-5.3.2
tar -xf gawk-5.3.2.tar.xz
cd gawk-5.3.2
sed -i 's/extras//' Makefile.in
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf gawk-5.3.2
Grep-3.12
tar -xf grep-3.12.tar.xz
cd grep-3.12
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(./build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf grep-3.12
Gzip-1.14
tar -xf gzip-1.14.tar.xz
cd gzip-1.14
./configure --prefix=/usr --host=$LFS_TGT
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf gzip-1.14
Make-4.4.1
tar -xf make-4.4.1.tar.gz
cd make-4.4.1
./configure --prefix=/usr \
--without-guile \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf make-4.4.1
Patch-2.8
tar -xf patch-2.8.tar.xz
cd patch-2.8
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf patch-2.8
Sed-4.9
tar -xf sed-4.9.tar.xz
cd sed-4.9
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(./build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf sed-4.9
Tar-1.35
tar -xf tar-1.35.tar.xz
cd tar-1.35
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(build-aux/config.guess)
make
make DESTDIR=$LFS install
cd $LFS/sources
rm -rf tar-1.35
Xz-5.8.1
tar -xf xz-5.8.1.tar.xz
cd xz-5.8.1
./configure --prefix=/usr \
--host=$LFS_TGT \
--build=$(build-aux/config.guess) \
--disable-static \
--docdir=/usr/share/doc/xz-5.8.1
make
make DESTDIR=$LFS install
rm -v $LFS/usr/lib/liblzma.la
cd $LFS/sources
rm -rf xz-5.8.1
Binutils-2.45 - Pass 2
tar -xf binutils-2.45.tar.xz
cd binutils-2.45
sed '6009s/$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}
cd $LFS/sources
rm -rf binutils-2.45
GCC-15.2.0 - Pass 2
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
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 \
LDFLAGS_FOR_TARGET=-L$PWD/$LFS_TGT/libgcc \
--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++
make
make DESTDIR=$LFS install
ln -sv gcc $LFS/usr/bin/cc
cd $LFS/sources
rm -rf gcc-15.2.0
Chapter 7 - Entering Chroot and Building Additional Temporary Tools:
Changing Ownership
Before we enter chroot, we need to change ownership of everything to root:
chown -R root:root $LFS/{usr,lib,var,etc,bin,sbin,tools}
case $(uname -m) in
x86_64) chown -R root:root $LFS/lib64 ;;
esac
Preparing Virtual Kernel File Systems
To enter chroot, we need to create and mount all the binded directories like so:
mkdir -pv $LFS/{dev,proc,sys,run}
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
Entering Chroot
Then we can chroot into our new system like so:
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
You should now see your prompt change to `(lfs chroot) root:/#` - you’re inside your new LFS system!
Creating Directories
Now we need to create the remaining directory structure:
mkdir -pv /{boot,home,mnt,opt,srv}
mkdir -pv /etc/{opt,sysconfig}
mkdir -pv /lib/firmware
mkdir -pv /media/{floppy,cdrom}
mkdir -pv /usr/{,local/}{include,src}
mkdir -pv /usr/local/{bin,lib,sbin}
mkdir -pv /usr/{,local/}share/{color,dict,doc,info,locale,man}
mkdir -pv /usr/{,local/}share/{misc,terminfo,zoneinfo}
mkdir -pv /usr/{,local/}share/man/man{1..8}
mkdir -pv /var/{cache,local,log,mail,opt,spool}
mkdir -pv /var/lib/{color,misc,locate}
ln -sfv /run /var/run
ln -sfv /run/lock /var/lock
install -dv -m 0750 /root
install -dv -m 1777 /tmp /var/tmp
Creating Essential Files and Symlinks
Create essential system files:
ln -sv /proc/self/mounts /etc/mtab
cat > /etc/hosts << EOF
127.0.0.1 localhost $(hostname)
::1 localhost
EOF
cat > /etc/passwd << "EOF"
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/dev/null:/usr/bin/false
daemon:x:6:6:Daemon User:/dev/null:/usr/bin/false
messagebus:x:18:18:D-Bus Message Daemon User:/run/dbus:/usr/bin/false
systemd-journal-gateway:x:73:73:systemd Journal Gateway:/:/usr/bin/false
systemd-journal-remote:x:74:74:systemd Journal Remote:/:/usr/bin/false
systemd-journal-upload:x:75:75:systemd Journal Upload:/:/usr/bin/false
systemd-network:x:76:76:systemd Network Management:/:/usr/bin/false
systemd-resolve:x:77:77:systemd Resolver:/:/usr/bin/false
systemd-timesync:x:78:78:systemd Time Synchronization:/:/usr/bin/false
systemd-coredump:x:79:79:systemd Core Dumper:/:/usr/bin/false
uuidd:x:80:80:UUID Generator Daemon User:/dev/null:/usr/bin/false
systemd-oom:x:81:81:systemd Out Of Memory Daemon:/:/usr/bin/false
nobody:x:65534:65534:Unprivileged User:/dev/null:/usr/bin/false
EOF
cat > /etc/group << "EOF"
root:x:0:
bin:x:1:daemon
sys:x:2:
kmem:x:3:
tape:x:4:
tty:x:5:
daemon:x:6:
floppy:x:7:
disk:x:8:
lp:x:9:
dialout:x:10:
audio:x:11:
video:x:12:
utmp:x:13:
cdrom:x:15:
adm:x:16:
messagebus:x:18:
systemd-journal:x:23:
input:x:24:
mail:x:34:
kvm:x:61:
systemd-journal-gateway:x:73:
systemd-journal-remote:x:74:
systemd-journal-upload:x:75:
systemd-network:x:76:
systemd-resolve:x:77:
systemd-timesync:x:78:
systemd-coredump:x:79:
uuidd:x:80:
systemd-oom:x:81:
wheel:x:97:
users:x:999:
nogroup:x:65534:
EOF
echo "tester:x:101:101::/home/tester:/bin/bash" >> /etc/passwd
echo "tester:x:101:" >> /etc/group
install -o tester -d /home/tester
exec /usr/bin/bash --login
touch /var/log/{btmp,lastlog,faillog,wtmp}
chgrp -v utmp /var/log/lastlog
chmod -v 664 /var/log/lastlog
chmod -v 600 /var/log/btmp
Now we build the remaining temporary tools in chroot:
Gettext-0.26
cd /sources
tar -xf gettext-0.26.tar.xz
cd gettext-0.26
./configure --disable-shared
make
cp -v gettext-tools/src/{msgfmt,msgmerge,xgettext} /usr/bin
cd /sources
rm -rf gettext-0.26
Bison-3.8.2
tar -xf bison-3.8.2.tar.xz
cd bison-3.8.2
./configure --prefix=/usr \
--docdir=/usr/share/doc/bison-3.8.2
make
make install
cd /sources
rm -rf bison-3.8.2
Perl-5.42.0
tar -xf perl-5.42.0.tar.xz
cd perl-5.42.0
sh Configure -des \
-D prefix=/usr \
-D vendorprefix=/usr \
-D useshrplib \
-D privlib=/usr/lib/perl5/5.42/core_perl \
-D archlib=/usr/lib/perl5/5.42/core_perl \
-D sitelib=/usr/lib/perl5/5.42/site_perl \
-D sitearch=/usr/lib/perl5/5.42/site_perl \
-D vendorlib=/usr/lib/perl5/5.42/vendor_perl \
-D vendorarch=/usr/lib/perl5/5.42/vendor_perl
make
make install
cd /sources
rm -rf perl-5.42.0
Python-3.13.7
Use capital P here, and ignore ssl warnings.
tar -xf Python-3.13.7.tar.xz
cd Python-3.13.7
./configure --prefix=/usr \
--enable-shared \
--without-ensurepip
make
make install
cd /sources
rm -rf Python-3.13.7
Texinfo-7.2
tar -xf texinfo-7.2.tar.xz
cd texinfo-7.2
./configure --prefix=/usr
make
make install
cd /sources
rm -rf texinfo-7.2
Util-linux-2.41.1
tar -xf util-linux-2.41.1.tar.xz
cd util-linux-2.41.1
mkdir -pv /var/lib/hwclock
./configure --libdir=/usr/lib \
--runstatedir=/run \
--disable-chfn-chsh \
--disable-login \
--disable-nologin \
--disable-su \
--disable-setpriv \
--disable-runuser \
--disable-pylibmount \
--disable-static \
--disable-liblastlog2 \
--without-python \
ADJTIME_PATH=/var/lib/hwclock/adjtime \
--docdir=/usr/share/doc/util-linux-2.41.1
make
make install
cd /sources
rm -rf util-linux-2.41.1
Cleaning up and Saving the Temporary System
Now let’s clean up the temporary system:
rm -rf /usr/share/{info,man,doc}/*
find /usr/{lib,libexec} -name \*.la -delete
rm -rf /tools
At this point, you can optionally backup your system. If something goes wrong in Chapter 8, you can restore from here:
# Exit chroot
logout
# From the host system:
cd $LFS
tar -cJpf $HOME/lfs-temp-tools-12.4.tar.xz .
To restore the backup later if needed:
cd $LFS
rm -rf ./*
tar -xpf $HOME/lfs-temp-tools-12.4.tar.xz
And you’re done with Chapter 7! The temporary tools are now built and you’re ready to move on to Chapter 8 where you’ll build the final LFS system.
Chapter 8 - Installing Basic System Software:
Man-pages-6.15
cd /sources
tar -xf man-pages-6.15.tar.xz
cd man-pages-6.15
rm -v man3/crypt*
make prefix=/usr install
cd /sources
rm -rf man-pages-6.15
Iana-Etc-20250807
tar -xf iana-etc-20250807.tar.gz
cd iana-etc-20250807
cp services protocols /etc
cd /sources
rm -rf iana-etc-20250807
Glibc-2.42
tar -xf glibc-2.42.tar.xz
cd glibc-2.42
patch -Np1 -i ../glibc-2.42-fhs-1.patch
mkdir -v build
cd build
echo "rootsbindir=/usr/sbin" > configparms
../configure --prefix=/usr \
--disable-werror \
--enable-kernel=5.4 \
--enable-stack-protector=strong \
--disable-nscd \
libc_cv_slibdir=/usr/lib
make
# Optional but recommended - run tests
make check
# Install
touch /etc/ld.so.conf
sed '/test-installation/s@$(PERL)@echo not running@' -i ../Makefile
make install
sed '/RTLDLIST=/s@/usr@@g' -i /usr/bin/ldd
# Configure Glibc
cat > /etc/nsswitch.conf << "EOF"
passwd: files
group: files
shadow: files
hosts: files dns
networks: files
protocols: files
services: files
ethers: files
rpc: files
EOF
# Add timezone data
tar -xf ../../tzdata2025b.tar.gz
ZONEINFO=/usr/share/zoneinfo
mkdir -pv $ZONEINFO/{posix,right}
for tz in etcetera southamerica northamerica europe africa antarctica \
asia australasia backward; do
zic -L /dev/null -d $ZONEINFO ${tz}
zic -L /dev/null -d $ZONEINFO/posix ${tz}
zic -L leapseconds -d $ZONEINFO/right ${tz}
done
cp -v zone.tab zone1970.tab iso3166.tab $ZONEINFO
zic -d $ZONEINFO -p America/New_York
unset ZONEINFO
# Set your timezone (adjust as needed)
ln -sfv /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
# Configure dynamic loader
cat > /etc/ld.so.conf << "EOF"
/usr/local/lib
/opt/lib
EOF
# Configure limits
cat > /etc/ld.so.conf << "EOF"
/usr/local/lib
/opt/lib
EOF
mkdir -pv /usr/lib/locale
localedef -i C -f UTF-8 C.UTF-8
localedef -i en_US -f UTF-8 en_US.UTF-8
cd /sources
rm -rf glibc-2.42
Zlib-1.3.1
I had a bug with this package so I copied it from the host iso image.
sudo cp -av /usr/lib64/libz.so* /mnt/lfs/usr/lib/
Or do it the proper way:
tar -xf zlib-1.3.1.tar.xz
cd zlib-1.3.1
./configure --prefix=/usr
make
make check
make install
rm -fv /usr/lib/libz.a
cd /sources
rm -rf zlib-1.3.1
Bzip2-1.0.8
tar -xf bzip2-1.0.8.tar.gz
cd bzip2-1.0.8
patch -Np1 -i ../bzip2-1.0.8-install_docs-1.patch
sed -i 's@\(ln -s -f \)$(PREFIX)/bin/@\1@' Makefile
sed -i "s@(PREFIX)/man@(PREFIX)/share/man@g" Makefile
make -f Makefile-libbz2_so
make clean
make
make PREFIX=/usr install
cp -av libbz2.so.* /usr/lib
ln -sv libbz2.so.1.0.8 /usr/lib/libbz2.so
cp -v bzip2-shared /usr/bin/bzip2
for i in /usr/bin/{bzcat,bunzip2}; do
ln -sfv bzip2 $i
done
rm -fv /usr/lib/libbz2.a
cd /sources
rm -rf bzip2-1.0.8
Xz-5.8.1
tar -xf xz-5.8.1.tar.xz
cd xz-5.8.1
./configure --prefix=/usr \
--disable-static \
--docdir=/usr/share/doc/xz-5.8.1
make
make check
make install
cd /sources
rm -rf xz-5.8.1
Lz4-1.10.0
tar -xf lz4-1.10.0.tar.gz
cd lz4-1.10.0
make BUILD_STATIC=no PREFIX=/usr
make -j1 check
make BUILD_STATIC=no PREFIX=/usr install
cd /sources
rm -rf lz4-1.10.0
Zstd-1.5.7
tar -xf zstd-1.5.7.tar.gz
cd zstd-1.5.7
make prefix=/usr
make check
make prefix=/usr install
rm -v /usr/lib/libzstd.a
cd /sources
rm -rf zstd-1.5.7
File-5.46
tar -xf file-5.46.tar.gz
cd file-5.46
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf file-5.46
Readline-8.3
tar -xf readline-8.3.tar.gz
cd readline-8.3
sed -i '/MV.*old/d' Makefile.in
sed -i '/{OLDSUFF}/c:' support/shlib-install
./configure --prefix=/usr \
--disable-static \
--with-curses \
--docdir=/usr/share/doc/readline-8.3
make SHLIB_LIBS="-lncursesw"
make SHLIB_LIBS="-lncursesw" install
install -v -m644 doc/*.{ps,pdf,html,dvi} /usr/share/doc/readline-8.3
cd /sources
rm -rf readline-8.3
M4-1.4.20
tar -xf m4-1.4.20.tar.xz
cd m4-1.4.20
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf m4-1.4.20
Bc-7.0.3
tar -xf bc-7.0.3.tar.xz
cd bc-7.0.3
CC=gcc ./configure --prefix=/usr -G -O3 -r
make
make test
make install
cd /sources
rm -rf bc-7.0.3
Flex-2.6.4
tar -xf flex-2.6.4.tar.gz
cd flex-2.6.4
./configure --prefix=/usr \
--docdir=/usr/share/doc/flex-2.6.4 \
--disable-static
make
make check
make install
ln -sv flex /usr/bin/lex
ln -sv flex.1 /usr/share/man/man1/lex.1
cd /sources
rm -rf flex-2.6.4
Tcl-8.6.16
tar -xf tcl8.6.16-src.tar.gz
cd tcl8.6.16
SRCDIR=$(pwd)
cd unix
./configure --prefix=/usr \
--mandir=/usr/share/man
make
sed -e "s|$SRCDIR/unix|/usr/lib|" \
-e "s|$SRCDIR|/usr/include|" \
-i tclConfig.sh
sed -e "s|$SRCDIR/unix/pkgs/tdbc1.1.10|/usr/lib/tdbc1.1.10|" \
-e "s|$SRCDIR/pkgs/tdbc1.1.10/generic|/usr/include|" \
-e "s|$SRCDIR/pkgs/tdbc1.1.10/library|/usr/lib/tcl8.6|" \
-e "s|$SRCDIR/pkgs/tdbc1.1.10|/usr/include|" \
-i pkgs/tdbc1.1.10/tdbcConfig.sh
sed -e "s|$SRCDIR/unix/pkgs/itcl4.2.5|/usr/lib/itcl4.2.5|" \
-e "s|$SRCDIR/pkgs/itcl4.2.5/generic|/usr/include|" \
-e "s|$SRCDIR/pkgs/itcl4.2.5|/usr/include|" \
-i pkgs/itcl4.2.5/itclConfig.sh
unset SRCDIR
make test
make install
chmod -v u+w /usr/lib/libtcl8.6.so
make install-private-headers
ln -sfv tclsh8.6 /usr/bin/tclsh
mv /usr/share/man/man3/{Thread,Tcl_Thread}.3
cd /sources
rm -rf tcl8.6.16
Expect-5.45.4
tar -xf expect5.45.4.tar.gz
cd expect5.45.4
python3 -c 'from pty import spawn; spawn(["echo", "ok"])'
patch -Np1 -i ../expect-5.45.4-gcc15-1.patch
./configure --prefix=/usr \
--with-tcl=/usr/lib \
--enable-shared \
--disable-rpath \
--mandir=/usr/share/man \
--with-tclinclude=/usr/include
make
make test
make install
ln -svf expect5.45.4/libexpect5.45.4.so /usr/lib
cd /sources
rm -rf expect5.45.4
DejaGNU-1.6.3
tar -xf dejagnu-1.6.3.tar.gz
cd dejagnu-1.6.3
mkdir -v build
cd build
../configure --prefix=/usr
makeinfo --html --no-split -o doc/dejagnu.html ../doc/dejagnu.texi
makeinfo --plaintext -o doc/dejagnu.txt ../doc/dejagnu.texi
make install
install -v -dm755 /usr/share/doc/dejagnu-1.6.3
install -v -m644 doc/dejagnu.{html,txt} /usr/share/doc/dejagnu-1.6.3
make check
cd /sources
rm -rf dejagnu-1.6.3
Pkgconf-2.5.1
tar -xf pkgconf-2.5.1.tar.xz
cd pkgconf-2.5.1
./configure --prefix=/usr \
--disable-static \
--docdir=/usr/share/doc/pkgconf-2.5.1
make
make install
ln -sv pkgconf /usr/bin/pkg-config
ln -sv pkgconf.1 /usr/share/man/man1/pkg-config.1
cd /sources
rm -rf pkgconf-2.5.1
Binutils-2.45
tar -xf binutils-2.45.tar.xz
cd binutils-2.45
mkdir -v build
cd build
../configure --prefix=/usr \
--sysconfdir=/etc \
--enable-gold \
--enable-ld=default \
--enable-plugins \
--enable-shared \
--disable-werror \
--enable-64-bit-bfd \
--enable-new-dtags \
--with-system-zlib \
--enable-default-hash-style=gnu
make tooldir=/usr
make -k check
make tooldir=/usr install
rm -fv /usr/lib/lib{bfd,ctf,ctf-nobfd,gprofng,opcodes,sframe}.a
cd /sources
rm -rf binutils-2.45
GMP-6.3.0
tar -xf gmp-6.3.0.tar.xz
cd gmp-6.3.0
./configure --prefix=/usr \
--enable-cxx \
--disable-static \
--docdir=/usr/share/doc/gmp-6.3.0
make
make html
make check 2>&1 | tee gmp-check-log
awk '/# PASS:/{total+=$3} ; END{print total}' gmp-check-log
make install
make install-html
cd /sources
rm -rf gmp-6.3.0
MPFR-4.2.2
tar -xf mpfr-4.2.2.tar.xz
cd mpfr-4.2.2
./configure --prefix=/usr \
--disable-static \
--enable-thread-safe \
--docdir=/usr/share/doc/mpfr-4.2.2
make
make html
make check
make install
make install-html
cd /sources
rm -rf mpfr-4.2.2
MPC-1.3.1
tar -xf mpc-1.3.1.tar.gz
cd mpc-1.3.1
./configure --prefix=/usr \
--disable-static \
--docdir=/usr/share/doc/mpc-1.3.1
make
make html
make check
make install
make install-html
cd /sources
rm -rf mpc-1.3.1
Attr-2.5.2
tar -xf attr-2.5.2.tar.gz
cd attr-2.5.2
./configure --prefix=/usr \
--disable-static \
--sysconfdir=/etc \
--docdir=/usr/share/doc/attr-2.5.2
make
make check
make install
cd /sources
rm -rf attr-2.5.2
Acl-2.3.2
tar -xf acl-2.3.2.tar.xz
cd acl-2.3.2
./configure --prefix=/usr \
--disable-static \
--docdir=/usr/share/doc/acl-2.3.2
make
make install
cd /sources
rm -rf acl-2.3.2
Libcap-2.76
tar -xf libcap-2.76.tar.xz
cd libcap-2.76
sed -i '/install -m.*STA/d' libcap/Makefile
make prefix=/usr lib=lib
make test
make prefix=/usr lib=lib install
cd /sources
rm -rf libcap-2.76
Libxcrypt-4.4.38
We can ignore note about api headers
tar -xf libxcrypt-4.4.38.tar.xz
cd libxcrypt-4.4.38
./configure --prefix=/usr \
--enable-hashes=strong,glibc \
--enable-obsolete-api=no \
--disable-static \
--disable-failure-tokens
make
make check
make install
cd /sources
rm -rf libxcrypt-4.4.38
Shadow-4.18.0
tar -xf shadow-4.18.0.tar.xz
cd shadow-4.18.0
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 \
--with-group-name-max-length=32
make
make exec_prefix=/usr install
make -C man install-man
pwconv
grpconv
mkdir -p /etc/default
useradd -D --gid 999
sed -i '/MAIL/s/yes/no/' /etc/default/useradd
cd /sources
rm -rf shadow-4.18.0
GCC-15.2.0
Check gcc failures with the link in the build logs in the book pr0576 or whatever is a known fail
tar -xf gcc-15.2.0.tar.xz
cd gcc-15.2.0
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
# Test suite (takes a long time)
ulimit -s -H unlimited
sed -e '/cpython/d' -i ../gcc/testsuite/gcc.dg/plugin/plugin.exp
sed -e 's/no-pic /&-no-pie /' -i ../gcc/testsuite/gcc.target/i386/pr113689-1.c
sed -e 's/300000/(1|300000)/' -i ../libgomp/testsuite/libgomp.c-c++-common/pr94366.c
sed -e 's/{ target nonpic }//' -e '/GOTPCREL/d' -i ../gcc/testsuite/gcc.target/i386/fentryname3.c
chown -R tester .
su tester -c "PATH=$PATH make -k check"
# See test results
../contrib/test_summary
make install
chown -v -R root:root \
/usr/lib/gcc/$(gcc -dumpmachine)/15.2.0/include{,-fixed}
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/
echo 'int main(){}' > dummy.c
cc dummy.c -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'
# Should output: [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
grep -E -o '/usr/lib.*/S?crt[1in].*succeeded' dummy.log
grep -B4 '^ /usr/include' dummy.log
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
grep "/lib.*/libc.so.6 " dummy.log
grep found dummy.log
rm -v dummy.c 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
cd /sources
rm -rf gcc-15.2.0
Ncurses-6.5-20250809
Ignore warning, and do cd test and play with some of these. could be good content. ./hanoi, ./knight
tar -xf ncurses-6.5-20250809.tar.gz
cd ncurses-6.5-20250809
./configure --prefix=/usr \
--mandir=/usr/share/man \
--with-shared \
--without-debug \
--without-normal \
--with-cxx-shared \
--enable-pc-files \
--with-pkg-config-libdir=/usr/lib/pkgconfig
make
make DESTDIR=$PWD/dest install
install -vm755 dest/usr/lib/libncursesw.so.6.5 /usr/lib
rm -v dest/usr/lib/libncursesw.so.6.5
sed -e 's/^#if.*XOPEN.*$/#if 1/' \
-i dest/usr/include/curses.h
cp -av dest/* /
for lib in ncurses form panel menu ; do
ln -sfv lib${lib}w.so /usr/lib/lib${lib}.so
ln -sfv ${lib}w.pc /usr/lib/pkgconfig/${lib}.pc
done
ln -sfv libncursesw.so /usr/lib/libcurses.so
cp -v -R doc -T /usr/share/doc/ncurses-6.5-20250809
# Optional - play with demos
make distclean
./configure --prefix=/usr \
--with-shared \
--without-normal \
--without-debug \
--without-cxx-binding \
--with-abi-version=5
make sources libs
cp -av lib/lib*.so.5* /usr/lib
cd /sources
rm -rf ncurses-6.5-20250809
Sed-4.9
tar -xf sed-4.9.tar.xz
cd sed-4.9
./configure --prefix=/usr
make
make html
chown -R tester .
su tester -c "PATH=$PATH make check"
make install
install -d -m755 /usr/share/doc/sed-4.9
install -m644 doc/sed.html /usr/share/doc/sed-4.9
cd /sources
rm -rf sed-4.9
Psmisc-23.7
tar -xf psmisc-23.7.tar.xz
cd psmisc-23.7
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf psmisc-23.7
Gettext-0.26
tar -xf gettext-0.26.tar.xz
cd gettext-0.26
./configure --prefix=/usr \
--disable-static \
--docdir=/usr/share/doc/gettext-0.26
make
make check
make install
chmod -v 0755 /usr/lib/preloadable_libintl.so
cd /sources
rm -rf gettext-0.26
Bison-3.8.2
tar -xf bison-3.8.2.tar.xz
cd bison-3.8.2
./configure --prefix=/usr --docdir=/usr/share/doc/bison-3.8.2
make
make check
make install
cd /sources
rm -rf bison-3.8.2
Grep-3.12
tar -xf grep-3.12.tar.xz
cd grep-3.12
sed -i "s/echo/#echo/" src/egrep.sh
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf grep-3.12
Bash-5.3
tar -xf bash-5.3.tar.gz
cd bash-5.3
./configure --prefix=/usr \
--without-bash-malloc \
--with-installed-readline \
bash_cv_strtold_broken=no \
--docdir=/usr/share/doc/bash-5.3
make
chown -R tester .
su -s /usr/bin/expect tester << "EOF"
set timeout -1
spawn make tests
expect eof
lassign [wait] _ _ _ value
exit $value
EOF
make install
# Start a new shell
exec /usr/bin/bash --login
cd /sources
rm -rf bash-5.3
Libtool-2.5.4
tar -xf libtool-2.5.4.tar.xz
cd libtool-2.5.4
./configure --prefix=/usr
make
make -k check
make install
rm -fv /usr/lib/libltdl.a
cd /sources
rm -rf libtool-2.5.4
GDBM-1.26
tar -xf gdbm-1.26.tar.gz
cd gdbm-1.26
./configure --prefix=/usr \
--disable-static \
--enable-libgdbm-compat
make
make check
make install
cd /sources
rm -rf gdbm-1.26
Gperf-3.3
tar -xf gperf-3.3.tar.gz
cd gperf-3.3
./configure --prefix=/usr --docdir=/usr/share/doc/gperf-3.3
make
make -j1 check
make install
cd /sources
rm -rf gperf-3.3
Expat-2.7.1
tar -xf expat-2.7.1.tar.xz
cd expat-2.7.1
./configure --prefix=/usr \
--disable-static \
--docdir=/usr/share/doc/expat-2.7.1
make
make check
make install
install -v -m644 doc/*.{html,css} /usr/share/doc/expat-2.7.1
cd /sources
rm -rf expat-2.7.1
Inetutils-2.6
tar -xf inetutils-2.6.tar.xz
cd inetutils-2.6
sed -i 's/def HAVE_TERMCAP_TGETENT/ 1/' telnet/telnet.c
./configure --prefix=/usr \
--bindir=/usr/bin \
--localstatedir=/var \
--disable-logger \
--disable-whois \
--disable-rcp \
--disable-rexec \
--disable-rlogin \
--disable-rsh \
--disable-servers
make
make check
make install
mv -v /usr/{,s}bin/ifconfig
cd /sources
rm -rf inetutils-2.6
Less-679
tar -xf less-679.tar.gz
cd less-679
./configure --prefix=/usr --sysconfdir=/etc
make
make check
make install
cd /sources
rm -rf less-679
Perl-5.42.0
tar -xf perl-5.42.0.tar.xz
cd perl-5.42.0
export BUILD_ZLIB=False
export BUILD_BZIP2=0
sh Configure -des \
-D prefix=/usr \
-D vendorprefix=/usr \
-D privlib=/usr/lib/perl5/5.42/core_perl \
-D archlib=/usr/lib/perl5/5.42/core_perl \
-D sitelib=/usr/lib/perl5/5.42/site_perl \
-D sitearch=/usr/lib/perl5/5.42/site_perl \
-D vendorlib=/usr/lib/perl5/5.42/vendor_perl \
-D vendorarch=/usr/lib/perl5/5.42/vendor_perl \
-D man1dir=/usr/share/man/man1 \
-D man3dir=/usr/share/man/man3 \
-D pager="/usr/bin/less -isR" \
-D useshrplib \
-D usethreads
make
TEST_JOBS=$(nproc) make test_harness
make install
unset BUILD_ZLIB BUILD_BZIP2
cd /sources
rm -rf perl-5.42.0
XML::Parser-2.47
tar -xf XML-Parser-2.47.tar.gz
cd XML-Parser-2.47
perl Makefile.PL
make
make test
make install
cd /sources
rm -rf XML-Parser-2.47
Intltool-0.51.0
tar -xf intltool-0.51.0.tar.gz
cd intltool-0.51.0
sed -i 's:\\\${:\\\$\\{:' intltool-update.in
./configure --prefix=/usr
make
make check
make install
install -v -Dm644 doc/I18N-HOWTO /usr/share/doc/intltool-0.51.0/I18N-HOWTO
cd /sources
rm -rf intltool-0.51.0
Autoconf-2.72
tar -xf autoconf-2.72.tar.xz
cd autoconf-2.72
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf autoconf-2.72
Automake-1.18.1
tar -xf automake-1.18.1.tar.xz
cd automake-1.18.1
./configure --prefix=/usr --docdir=/usr/share/doc/automake-1.18.1
make
make -j$(($(nproc)>4?$(nproc):4)) check
make install
cd /sources
rm -rf automake-1.18.1
OpenSSL-3.5.2
tar -xf openssl-3.5.2.tar.gz
cd openssl-3.5.2
./config --prefix=/usr \
--openssldir=/etc/ssl \
--libdir=lib \
shared \
zlib-dynamic
make
HARNESS_JOBS=$(nproc) make test
sed -i '/INSTALL_LIBS/s/libcrypto.a libssl.a//' Makefile
make MANSUFFIX=ssl install
mv -v /usr/share/doc/openssl /usr/share/doc/openssl-3.5.2
cp -vfr doc/* /usr/share/doc/openssl-3.5.2
cd /sources
rm -rf openssl-3.5.2
Libelf from Elfutils-0.193
Known failure in dwarf_scrlang_check
tar -xf elfutils-0.193.tar.bz2
cd elfutils-0.193
./configure --prefix=/usr \
--disable-debuginfod \
--enable-libdebuginfod=dummy
make
make check
make -C libelf install
install -vm644 config/libelf.pc /usr/lib/pkgconfig
rm /usr/lib/libelf.a
cd /sources
rm -rf elfutils-0.193
Libffi-3.5.2
tar -xf libffi-3.5.2.tar.gz
cd libffi-3.5.2
./configure --prefix=/usr \
--disable-static \
--with-gcc-arch=native
make
make check
make install
cd /sources
rm -rf libffi-3.5.2
Python-3.13.7
Known expected warning can be surpressed by adding root user action to the global pip.conf like so
tar -xf Python-3.13.7.tar.xz
cd Python-3.13.7
./configure --prefix=/usr \
--enable-shared \
--with-system-expat \
--enable-optimizations
make
make test
make install
cat > /etc/pip.conf << EOF
[global]
root-user-action = ignore
disable-pip-version-check = true
EOF
install -v -dm755 /usr/share/doc/python-3.13.7/html
tar --no-same-owner \
-xvf ../python-3.13.7-docs-html.tar.bz2
cp -R --no-preserve=mode python-3.13.7-docs-html/* \
/usr/share/doc/python-3.13.7/html
cd /sources
rm -rf Python-3.13.7
Flit-Core-3.12.0
tar -xf flit_core-3.12.0.tar.gz
cd flit_core-3.12.0
pip3 wheel -w dist --no-cache-dir --no-build-isolation --no-deps $PWD
pip3 install --no-index --no-user --find-links dist flit_core
cd /sources
rm -rf flit_core-3.12.0
Packaging-25.0
tar -xf packaging-25.0.tar.gz
cd packaging-25.0
pip3 wheel -w dist --no-cache-dir --no-build-isolation --no-deps $PWD
pip3 install --no-index --no-user --find-links dist packaging
cd /sources
rm -rf packaging-25.0
Wheel-0.46.1
tar -xf wheel-0.46.1.tar.gz
cd wheel-0.46.1
pip3 wheel -w dist --no-cache-dir --no-build-isolation --no-deps $PWD
pip3 install --no-index --find-links=dist wheel
cd /sources
rm -rf wheel-0.46.1
Setuptools-80.9.0
tar -xf setuptools-80.9.0.tar.gz
cd setuptools-80.9.0
pip3 wheel -w dist --no-cache-dir --no-build-isolation --no-deps $PWD
pip3 install --no-index --find-links dist setuptools
cd /sources
rm -rf setuptools-80.9.0
Ninja-1.13.1
tar -xf ninja-1.13.1.tar.gz
cd ninja-1.13.1
export NINJAJOBS=$(nproc)
sed -i '/int Guess/a \
int j = 0;\
char* jobs = getenv( "NINJAJOBS" );\
if ( jobs != NULL ) j = atoi( jobs );\
if ( j > 0 ) return j;\
' src/ninja.cc
python3 configure.py --bootstrap
./ninja ninja_test
./ninja_test --gtest_filter=-SubprocessTest.SetWithLots
install -vm755 ninja /usr/bin/
install -vDm644 misc/bash-completion /usr/share/bash-completion/completions/ninja
install -vDm644 misc/zsh-completion /usr/share/zsh/site-functions/_ninja
cd /sources
rm -rf ninja-1.13.1
Meson-1.8.3
tar -xf meson-1.8.3.tar.gz
cd meson-1.8.3
pip3 wheel -w dist --no-cache-dir --no-build-isolation --no-deps $PWD
pip3 install --no-index --find-links dist meson
install -vDm644 data/shell-completions/bash/meson /usr/share/bash-completion/completions/meson
install -vDm644 data/shell-completions/zsh/_meson /usr/share/zsh/site-functions/_meson
cd /sources
rm -rf meson-1.8.3
Kmod-34.2
tar -xf kmod-34.2.tar.xz
cd kmod-34.2
./configure --prefix=/usr \
--sysconfdir=/etc \
--with-openssl \
--with-xz \
--with-zstd \
--with-zlib \
--disable-manpages
make
make install
for target in depmod insmod modinfo modprobe rmmod; do
ln -sfv ../bin/kmod /usr/sbin/$target
rm -fv /usr/bin/$target
done
cd /sources
rm -rf kmod-34.2
Coreutils-9.7
tar -xf coreutils-9.7.tar.xz
cd coreutils-9.7
patch -Np1 -i ../coreutils-9.7-i18n-2.patch
autoreconf -fiv
FORCE_UNSAFE_CONFIGURE=1 ./configure \
--prefix=/usr \
--enable-no-install-program=kill,uptime
make
make NON_ROOT_USERNAME=tester check-root
groupadd -g 102 dummy -U tester
chown -R tester .
su tester -c "PATH=$PATH make -k RUN_EXPENSIVE_TESTS=yes check"
groupdel dummy
make install
mv -v /usr/bin/chroot /usr/sbin
mv -v /usr/share/man/man1/chroot.1 /usr/share/man/man8/chroot.8
sed -i 's/"1"/"8"/' /usr/share/man/man8/chroot.8
cd /sources
rm -rf coreutils-9.7
Diffutils-3.12
tar -xf diffutils-3.12.tar.xz
cd diffutils-3.12
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf diffutils-3.12
Gawk-5.3.2
tar -xf gawk-5.3.2.tar.xz
cd gawk-5.3.2
sed -i 's/extras//' Makefile.in
./configure --prefix=/usr
make
chown -R tester .
su tester -c "PATH=$PATH make check"
rm -f /usr/bin/gawk-5.3.2
make install
ln -sv gawk.1 /usr/share/man/man1/awk.1
mkdir -pv /usr/share/doc/gawk-5.3.2
cp -v doc/{awkforai.txt,*.{eps,pdf,jpg}} /usr/share/doc/gawk-5.3.2
cd /sources
rm -rf gawk-5.3.2
Findutils-4.10.0
tar -xf findutils-4.10.0.tar.xz
cd findutils-4.10.0
./configure --prefix=/usr --localstatedir=/var/lib/locate
make
chown -R tester .
su tester -c "PATH=$PATH make check"
make install
cd /sources
rm -rf findutils-4.10.0
Groff-1.23.0
tar -xf groff-1.23.0.tar.gz
cd groff-1.23.0
PAGE=letter ./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf groff-1.23.0
GRUB-2.12
tar -xf grub-2.12.tar.xz
cd grub-2.12
unset {C,CPP,CXX,LD}FLAGS
echo depends bli part_gpt > grub-core/extra_deps.lst
./configure --prefix=/usr \
--sysconfdir=/etc \
--disable-efiemu \
--disable-werror
make
make install
mv -v /etc/bash_completion.d/grub /usr/share/bash-completion/completions
cd /sources
rm -rf grub-2.12
Gzip-1.14
tar -xf gzip-1.14.tar.xz
cd gzip-1.14
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf gzip-1.14
IPRoute2-6.16.0
tar -xf iproute2-6.16.0.tar.xz
cd iproute2-6.16.0
sed -i /ARPD/d Makefile
rm -fv man/man8/arpd.8
make NETNS_RUN_DIR=/run/netns
make SBINDIR=/usr/sbin install
mkdir -pv /usr/share/doc/iproute2-6.16.0
cp -v COPYING README* /usr/share/doc/iproute2-6.16.0
cd /sources
rm -rf iproute2-6.16.0
Kbd-2.8.0
tar -xf kbd-2.8.0.tar.xz
cd kbd-2.8.0
patch -Np1 -i ../kbd-2.8.0-backspace-1.patch
sed -i '/RESIZECONS_PROGS=/s/yes/no/' configure
sed -i 's/resizecons.8 //' docs/man/man8/Makefile.in
./configure --prefix=/usr --disable-vlock
make
make check
make install
cp -R -v docs/doc -T /usr/share/doc/kbd-2.8.0
cd /sources
rm -rf kbd-2.8.0
Libpipeline-1.5.8
tar -xf libpipeline-1.5.8.tar.gz
cd libpipeline-1.5.8
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf libpipeline-1.5.8
Make-4.4.1
tar -xf make-4.4.1.tar.gz
cd make-4.4.1
./configure --prefix=/usr
make
chown -R tester .
su tester -c "PATH=$PATH make check"
make install
cd /sources
rm -rf make-4.4.1
Patch-2.8
tar -xf patch-2.8.tar.xz
cd patch-2.8
./configure --prefix=/usr
make
make check
make install
cd /sources
rm -rf patch-2.8
Tar-1.35
Known failure test suite 233 (read comment)
tar -xf tar-1.35.tar.xz
cd tar-1.35
FORCE_UNSAFE_CONFIGURE=1 \
./configure --prefix=/usr
make
make check
make install
make -C doc install-html docdir=/usr/share/doc/tar-1.35
cd /sources
rm -rf tar-1.35
Texinfo-7.2
tar -xf texinfo-7.2.tar.xz
cd texinfo-7.2
./configure --prefix=/usr
make
make check
make install
make TEXMF=/usr/share/texmf install-tex
cd /sources
rm -rf texinfo-7.2
Vim-9.1.1629
Use my own vimrc, ignore vi symlink
tar -xf vim-9.1.1629.tar.gz
cd vim-9.1.1629
echo '#define SYS_VIMRC_FILE "/etc/vimrc"' >> src/feature.h
./configure --prefix=/usr
make
chown -R tester .
su tester -c "TERM=xterm-256color LANG=en_US.UTF-8 make -j1 test" \
&> vim-test.log
make install
ln -sv vim /usr/bin/vi
for L in /usr/share/man/{,*/}man1/vim.1; do
ln -sv vim.1 $(dirname $L)/vi.1
done
ln -sv ../vim/vim91/doc /usr/share/doc/vim-9.1.1629
cat > /etc/vimrc << "EOF"
" Begin /etc/vimrc
filetype plugin indent on
set expandtab
set shiftwidth=4
set softtabstop=4
set tabstop=4
set number
set relativenumber
set smartindent
set showmatch
set backspace=indent,eol,start
syntax on
if (&term == "xterm") || (&term == "putty")
set background=dark
endif
" End /etc/vimrc
EOF
cd /sources
rm -rf vim-9.1.1629
MarkupSafe-3.0.2
tar -xf MarkupSafe-3.0.2.tar.gz
cd MarkupSafe-3.0.2
pip3 wheel -w dist --no-cache-dir --no-build-isolation --no-deps $PWD
pip3 install --no-index --no-user --find-links dist Markupsafe
cd /sources
rm -rf MarkupSafe-3.0.2
Jinja2-3.1.6
tar -xf jinja2-3.1.6.tar.gz
cd jinja2-3.1.6
pip3 wheel -w dist --no-cache-dir --no-build-isolation --no-deps $PWD
pip3 install --no-index --no-user --find-links dist Jinja2
cd /sources
rm -rf jinja2-3.1.6
Udev from Systemd-257.8
tar -xf systemd-257.8.tar.gz
cd systemd-257.8
sed -i -e 's/GROUP="render"/GROUP="video"/' \
-e 's/GROUP="sgx", //' rules.d/50-udev-default.rules.in
sed '/systemd-sysctl/s/^/#/' -i rules.d/99-systemd.rules.in
sed '/NETWORK_DIRS/s/systemd/udev/' -i src/basic/path-lookup.h
mkdir -p build
cd build
meson setup .. \
--prefix=/usr \
--buildtype=release \
-D mode=release \
-D dev-kvm-mode=0660 \
-D link-udev-shared=false \
-D logind=false \
-D vconsole=false
export udev_helpers=$(grep "'name' :" ../src/udev/meson.build | \
awk '{print $2}' | tr -d ",'" | grep -v 'udevadm')
ninja udevadm systemd-hwdb \
$(ninja -n | grep -Eo '(src/(lib)?udev|rules.d|hwdb.d)/[^ ]*') \
$(realpath libudev.so --relative-to .) \
$udev_helpers
install -vm755 -d {/usr/lib,/etc}/udev/{hwdb.d,rules.d,network}
install -vm755 -d /usr/{lib,share}/pkgconfig
install -vm755 udevadm /usr/bin/
install -vm755 systemd-hwdb /usr/bin/udev-hwdb
ln -svfn ../bin/udevadm /usr/sbin/udevd
cp -av libudev.so{,*[0-9]} /usr/lib/
install -vm644 ../src/libudev/libudev.h /usr/include/
install -vm644 src/libudev/*.pc /usr/lib/pkgconfig/
install -vm644 src/udev/*.pc /usr/share/pkgconfig/
install -vm644 ../src/udev/udev.conf /etc/udev/
install -vm644 rules.d/* ../rules.d/README /usr/lib/udev/rules.d/
install -vm644 $(find ../rules.d/*.rules \
-not -name '*power-switch*') /usr/lib/udev/rules.d/
install -vm644 hwdb.d/* ../hwdb.d/{*.hwdb,README} /usr/lib/udev/hwdb.d/
install -vm755 $udev_helpers /usr/lib/udev
install -vm644 ../network/99-default.link /usr/lib/udev/network
tar -xvf ../../udev-lfs-20230818.tar.xz
make -f udev-lfs-20230818/Makefile.lfs install
tar -xf ../../systemd-man-pages-257.8.tar.xz \
--no-same-owner --strip-components=1 \
-C /usr/share/man --wildcards '*/udev*' '*/libudev*' \
'*/systemd.link.5' \
'*/systemd-'{hwdb,udevd.service}.8
sed 's|systemd/network|udev/network|' \
/usr/share/man/man5/systemd.link.5 \
> /usr/share/man/man5/udev.link.5
sed 's/systemd\(\\\?-\)/udev\1/' /usr/share/man/man8/systemd-hwdb.8 \
> /usr/share/man/man8/udev-hwdb.8
sed 's|lib.*udevd|sbin/udevd|' \
/usr/share/man/man8/systemd-udevd.service.8 \
> /usr/share/man/man8/udevd.8
rm /usr/share/man/man*/systemd*
unset udev_helpers
udev-hwdb update
cd /sources
rm -rf systemd-257.8
Man-DB-2.13.1
tar -xf man-db-2.13.1.tar.xz
cd man-db-2.13.1
./configure --prefix=/usr \
--docdir=/usr/share/doc/man-db-2.13.1 \
--sysconfdir=/etc \
--disable-setuid \
--enable-cache-owner=bin \
--with-browser=/usr/bin/lynx \
--with-vgrind=/usr/bin/vgrind \
--with-grap=/usr/bin/grap \
--with-systemdtmpfilesdir= \
--with-systemdsystemunitdir=
make
make check
make install
cd /sources
rm -rf man-db-2.13.1
Procps-ng-4.0.5
tar -xf procps-ng-4.0.5.tar.xz
cd procps-ng-4.0.5
./configure --prefix=/usr \
--docdir=/usr/share/doc/procps-ng-4.0.5 \
--disable-static \
--disable-kill \
--with-systemd
make src_w_LDADD='$(LDADD) -lsystemd'
make check
make install
cd /sources
rm -rf procps-ng-4.0.5
Util-linux-2.41.1
Kill error is known
tar -xf util-linux-2.41.1.tar.xz
cd util-linux-2.41.1
./configure --bindir=/usr/bin \
--libdir=/usr/lib \
--runstatedir=/run \
--sbindir=/usr/sbin \
--disable-chfn-chsh \
--disable-login \
--disable-nologin \
--disable-su \
--disable-setpriv \
--disable-runuser \
--disable-pylibmount \
--disable-static \
--disable-liblastlog2 \
--without-python \
ADJTIME_PATH=/var/lib/hwclock/adjtime \
--docdir=/usr/share/doc/util-linux-2.41.1
make
# Skip tests in chroot
# chown -R tester .
# su tester -c "make -k check"
make install
cd /sources
rm -rf util-linux-2.41.1
E2fsprogs-1.47.3
Known m_asdfasfd test fail
tar -xf e2fsprogs-1.47.3.tar.gz
cd e2fsprogs-1.47.3
mkdir -v build
cd build
../configure --prefix=/usr \
--sysconfdir=/etc \
--enable-elf-shlibs \
--disable-libblkid \
--disable-libuuid \
--disable-uuidd \
--disable-fsck
make
make check
make install
rm -fv /usr/lib/{libcom_err,libe2p,libext2fs,libss}.a
gunzip -v /usr/share/info/libext2fs.info.gz
install-info --dir-file=/usr/share/info/dir /usr/share/info/libext2fs.info
makeinfo -o doc/com_err.info ../lib/et/com_err.texinfo
install -v -m644 doc/com_err.info /usr/share/info
install-info --dir-file=/usr/share/info/dir /usr/share/info/com_err.info
sed 's/metadata_csum_seed,//' -i /etc/mke2fs.conf
cd /sources
rm -rf e2fsprogs-1.47.3
Sysklogd-2.7.2
tar -xf sysklogd-2.7.2.tar.gz
cd sysklogd-2.7.2
./configure --prefix=/usr \
--sysconfdir=/etc \
--runstatedir=/run \
--without-logger
make
make install
cat > /etc/syslog.conf << "EOF"
# Begin /etc/syslog.conf
auth,authpriv.* -/var/log/auth.log
*.*;auth,authpriv.none -/var/log/sys.log
daemon.* -/var/log/daemon.log
kern.* -/var/log/kern.log
mail.* -/var/log/mail.log
user.* -/var/log/user.log
*.emerg *
# End /etc/syslog.conf
EOF
cd /sources
rm -rf sysklogd-2.7.2
SysVinit-3.14
tar -xf sysvinit-3.14.tar.xz
cd sysvinit-3.14
patch -Np1 -i ../sysvinit-3.14-consolidated-1.patch
make
make install
cd /sources
rm -rf sysvinit-3.14
Stripping Debug Symbols
Now we clean up debug symbols to save space:
save_usrlib="$(cd /usr/lib; ls ld-linux*[^g])
libc.so.6
libthread_db.so.1
libquadmath.so.0.0.0
libstdc++.so.6.0.33
libitm.so.1.0.0
libatomic.so.1.2.0"
cd /usr/lib
for LIB in $save_usrlib; do
objcopy --only-keep-debug --compress-debug-sections=zlib $LIB $LIB.dbg
cp $LIB /tmp/$LIB
strip --strip-unneeded /tmp/$LIB
objcopy --add-gnu-debuglink=$LIB.dbg /tmp/$LIB
install -vm755 /tmp/$LIB /usr/lib
rm /tmp/$LIB
done
online_usrbin="bash find strip"
online_usrlib="libbfd-2.45.so
libsframe.so.1.0.0
libhistory.so.8.3
libncursesw.so.6.5
libm.so.6
libreadline.so.8.3
libz.so.1.3.1
libzstd.so.1.5.7
$(cd /usr/lib; find libnss*.so* -type f)"
for BIN in $online_usrbin; do
cp /usr/bin/$BIN /tmp/$BIN
strip --strip-unneeded /tmp/$BIN
install -vm755 /tmp/$BIN /usr/bin
rm /tmp/$BIN
done
for LIB in $online_usrlib; do
cp /usr/lib/$LIB /tmp/$LIB
strip --strip-unneeded /tmp/$LIB
install -vm755 /tmp/$LIB /usr/lib
rm /tmp/$LIB
done
for i in $(find /usr/lib -type f -name \*.so* ! -name \*dbg) \
$(find /usr/lib -type f -name \*.a) \
$(find /usr/{bin,sbin,libexec} -type f); do
case "$online_usrbin $online_usrlib $save_usrlib" in
*$(basename $i)* )
;;
* ) strip --strip-unneeded $i 2>/dev/null
;;
esac
done
unset BIN LIB save_usrlib online_usrbin online_usrlib
Cleaning Up
rm -rf /tmp/{*,.*}
find /usr/lib /usr/libexec -name \*.la -delete
find /usr -depth -name $(uname -m)-lfs-linux-gnu\* | xargs rm -rf
userdel -r tester
GRUB BLFS LINK
mkdir BLFS cd BLFS screen -> control A D cd /mnt/lfs/sources/BLFS (middle click freetype, middle click efitbootmgr, copy links..) wget https://downloads.sourceforge.net/freetype/freetype-2.13.3.tar.xz wget https://downloads.sourceforge.net/freetype/freetype-doc-2.13.3.tar.xz md5sum * (eyeball check on this to match the md5sums) screen -r lfs extract freetype-2 cd freetype-2 follow instructions from here.. https://www.linuxfromscratch.org/blfs/view/12.4/general/freetype2.html tar -xf ../freetype-doc-2.13.3.tar.xz –strip-components=2 -C docs …
we need to get dependencies for efibootmgr…
efivar-39 and popt-1.19 skip api documentaiton for popt as it is optional get grub the same way, just the additional dependency then in BLFS, do
tar -xvf ../grub-2.
Chapter 9 - System Configuration:
Managing Devies:
Udev script wont work on qemu.. We dont need to run it though because all it does is rename enp1s0 to eth0 We can skip the rest of all this actually since we are not worried about changing our network interface.
Network Interface:
cd /etc/sysconfig/
vim ifconfig.enp1s0
ONBOOT=yes
IFACE=enp1s0
SERVICE=ipv4-static
IP=192.168.122.101
GATEWAY=192.168.122.1
PREFIX=24
BROADCAST=192.168.122.255
You can get all this information from running ip a on another machine, or your host machine
vim /etc/resolv.conf
# Begin /etc/resolv.conf
nameserver 8.8.8.8
nameserver 1.1.1.1
# End /etc/resolv.conf
I use lfs, btw..
echo 'lfs-btw' >> /etc/hostname
/etc/hosts..
cat > /etc/hosts << "EOF"
# Begin /etc/hosts
127.0.0.1 localhost
127.0.1.1 lfs-btw
::1 localhost
# End /etc/hosts
EOF
sysvinit
Create inittab file for sysvinit
cat > /etc/inittab << "EOF"
# Begin /etc/inittab
id:3:initdefault:
si::sysinit:/etc/rc.d/init.d/rc S
l0:0:wait:/etc/rc.d/init.d/rc 0
l1:S1:wait:/etc/rc.d/init.d/rc 1
l2:2:wait:/etc/rc.d/init.d/rc 2
l3:3:wait:/etc/rc.d/init.d/rc 3
l4:4:wait:/etc/rc.d/init.d/rc 4
l5:5:wait:/etc/rc.d/init.d/rc 5
l6:6:wait:/etc/rc.d/init.d/rc 6
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
su:S06:once:/sbin/sulogin
s1:1:respawn:/sbin/sulogin
1:2345:respawn:/sbin/agetty --noclear tty1 9600
2:2345:respawn:/sbin/agetty tty2 9600
3:2345:respawn:/sbin/agetty tty3 9600
4:2345:respawn:/sbin/agetty tty4 9600
5:2345:respawn:/sbin/agetty tty5 9600
6:2345:respawn:/sbin/agetty tty6 9600
# End /etc/inittab
EOF
Hardware clock: Linux only, leave it as 1
cat > /etc/sysconfig/clock << "EOF"
# Begin /etc/sysconfig/clock
UTC=1
# Set this to any options you might need to give to hwclock,
# such as machine hardware clock type for Alphas.
CLOCKPARAMS=
# End /etc/sysconfig/clock
EOF
We can run this since we’re on en-us_UTF-8
cat > /etc/sysconfig/console << "EOF"
# Begin /etc/sysconfig/console
UNICODE="1"
FONT="Lat2-Terminus16"
# End /etc/sysconfig/console
EOF
If you are on a different keymap, such as gb, or romania or something, check out these instructions here on how to change that.
The rest is optional, let’s move on to generating the locales
Locale Configuration
For my situation, im on en_US.UTF-8, and you can check yours with `locale -a`
so i’ll do:
cat > /etc/profile << "EOF"
# Begin /etc/profile
for i in $(locale); do
unset ${i%=*}
done
if [[ "$TERM" = linux ]]; then
export LANG=C.UTF-8
else
export LANG=en_US.UTF-8
fi
# End /etc/profile
EOF
If you are on a different locale, you will need to run this command to figure out what to add to the suffix of en_<>
LC_ALL=en_GB locale charmap
Chapter 10 - Make System Bootable:
Fstab
I can make my fstab just like this, you just need to ensure your /dev/vda matches your actual partition names.
cat > /etc/fstab << "EOF"
# Begin /etc/fstab
# file system mount-point type options dump fsck
# order
/dev/vda3 / ext4 defaults 1 1
/dev/vda1 /boot/efi vfat defaults 0 2
/dev/vda2 swap swap pri=1 0 0
proc /proc proc nosuid,noexec,nodev 0 0
sysfs /sys sysfs nosuid,noexec,nodev 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
tmpfs /run tmpfs defaults 0 0
devtmpfs /dev devtmpfs mode=0755,nosuid 0 0
tmpfs /dev/shm tmpfs nosuid,nodev 0 0
cgroup2 /sys/fs/cgroup cgroup2 nosuid,noexec,nodev 0 0
# End /etc/fstab
EOF
Kernel
Hard part… but final boss if you will
cd /sources tar -xvf linux-16 cd linux-
make mrproper make defconfig
This creates a config based on my current system architecture. It doesnt make sense for me to customize the kernel here since your hardware will not be the same as mine, so I’ll just show you the defaults and how to get it up and running.
Lets add some defaults to get it working
make menuconfig
Actually everything here is good, but just verify some stuff For my situation, I’m going to just take a look at every default setting the guide offers, and provide those, as well as enable a few virtio drivers for my qemu setup. Also its important here to follow the blfs guide on EFI since we installed grub using efi.
Bare Minimum to Enable for QEMU/KVM VM:
1. Virtio Drivers (CRITICAL - without these you won't see your disk!)
Navigate to: Device Drivers → Block devices
Enable: [*] Virtio block driver
Navigate to: Device Drivers → Network device support
Enable: [*] Virtio network driver
Navigate to: Device Drivers → SCSI device support
Enable: [*] virtio-scsi support
Or search in menuconfig (press / then type VIRTIO):
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_NET=y
CONFIG_VIRTIO_PCI=y
CONFIG_SCSI_VIRTIO=y
2. EXT4 Filesystem
Navigate to: File systems
Enable: [*] The Extended 4 (ext4) filesystem
Already enabled in defconfig, but verify
3. EFI Boot Support
Navigate to: Processor type and features
Enable: [*] EFI runtime service support
Enable: [*] EFI stub support
That's it
Save and exit (Save → Exit), then:
time to build the kernel:
make
make modules_install
Copy these boot files… Change ownership of this for BLFS
chown -R 0:0 ../linux-6.16.1
Skip the modprobe, we didnt do anything with usb ports so we are good. We’re ready to install grub.
GRUB
Skip all of this, just go straight to BLFS page because we are on UEFI, not bios. I’ve installed grub over 100 times on efi, we can just do the following:
mount the efi vars:
# mount efivarfs
mountpoint /sys/firmware/efi/efivars || mount -v -t efivarfs efivarfs /sys/firmware/efi/efivars
# Add efivarfs to fstab
cat >> /etc/fstab << "EOF"
efivarfs /sys/firmware/efi/efivars efivarfs defaults 0 0
EOF
# Install GRUB
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=LFS --recheck
# Generate config
We need to generate a minimal grub config here, we can do so by just pasting this in here:
cat > /boot/grub/grub.cfg << EOF
# Begin /boot/grub/grub.cfg
set default=0
set timeout=5
insmod part_gpt
insmod ext2
set root=(hd0,3)
insmod efi_gop
insmod efi_uga
if loadfont /boot/grub/fonts/unicode.pf2; then
terminal_output gfxterm
fi
menuentry "GNU/Linux, Linux 6.16.1-lfs-12.4" {
linux /boot/vmlinuz-6.16.1-lfs-12.4 root=/dev/vda3 ro
}
menuentry "Firmware Setup" {
fwsetup
}
EOF
Note i’ve changed sda to vda here, and 2 to 3, because that is where my root file system is. I cant believe I’m about to say this but its finally time to reboot into our LFS system..
Reboot and Pray:
# Exit chroot
logout
# Unmount everything (from live CD)
umount -v /mnt/lfs/dev/pts
umount -v /mnt/lfs/dev
umount -v /mnt/lfs/run
umount -v /mnt/lfs/proc
umount -v /mnt/lfs/sys
umount -v /mnt/lfs/boot/efi
umount -v /mnt/lfs
# Reboot
reboot
Final Thoughts:
Alright, that’s it. We just built an entire Linux system from scratch. This is probably the most educational project you can do in Linux.
We compiled over 80 packages, built a custom kernel, configured a bootloader, and created a fully functional system. You now understand how Linux works at a fundamental level - how packages depend on each other, how the toolchain works, how the boot process functions.
This is just the base LFS system. From here, you can continue with BLFS (Beyond Linux From Scratch) to add X11, desktop environments, browsers, and all the software you actually use day to day.
If you provide proof that you installed LFS using this guide, I will give you a special role in my discord server.
Thanks so much for checking out this tutorial. If you got value from it, and you want to find more tutorials like this, check out my youtube channel here: YouTube, or my website here: tony,btw
You can support me here: kofi