LFS: a companion

introduction

this article was written to be read alongside the LFS book.

{create-host.sh 1}
mkdir LFSc && cd "$_"
{create-vm, 2}
{launch-vm, 2}
{prepare-host.sh 1}
myfn () {
{partition, 3}
# without this, mkfs will error
sleep 1
{format, 3}
{export variables, 3}
{mount, 3}
{sources, 3}
{directory structure, 4}
{lfs user, 4}
# allow for passwordless login to new account
passwd -d lfs
}
ssh root@127.0.0.1 -p 2222 "$(typeset -f myfn); myfn"
{prepare-lfs.sh 1}
myfn () {
{bash setup, 4}
}
ssh lfs@127.0.0.1 -p 2222 "$(typeset -f myfn); myfn"
myfn2 () {
cd /mnt/lfs/root/sources
{pass 1 binutils, 5}
{pass 1 gcc, 5}
{linux api headers, 5}
{glibc, 5}
{libstdc++, 5}
{m4, 6}
{ncurses, 6}
{bash, 6}
{coreutils, 6}
{diffutils, 6}
{file, 6}
{findutils, 6}
{gawk, 6}
{grep, 6}
{gzip, 6}
{make, 6}
{patch, 6}
{sed, 6}
{tar, 6}
{xz, 6}
{pass 2 binutils, 6}
{pass 2 gcc, 6}
}
ssh lfs@127.0.0.1 -p 2222 "$(typeset -f myfn2); myfn2"
{chroot-lfs.sh 1}
myfn () {
{revert dir ownership, 7}
{mount device files, 7}
}
#ssh root@127.0.0.1 -p 2222 "source .bash_profile; $(typeset -f myfn); myfn"

ssh root@127.0.0.1 -p 2222 << "EOF"
source .bash_profile;
myfn2 () {
{chroot1, 7}
}
declare -f myfn2 > $ROOT/script.sh
/usr/sbin/chroot "$ROOT"     \
 /usr/bin/env -i             \
 HOME=/root                  \
 TERM="$TERM"                \
 PS1='(lfs chroot) \u:\w\$'  \
 PATH=/usr/bin:/usr/sbin     \
 /bin/bash -c "source script.sh; myfn2; rm script.sh"
EOF

ssh root@127.0.0.1 -p 2222 << "EOF"
source .bash_profile;
myfn2 () {
{chroot2, 7}
}
declare -f myfn2 > $ROOT/script.sh
/usr/sbin/chroot "$ROOT"     \
 /usr/bin/env -i             \
 HOME=/root                  \
 TERM="$TERM"                \
 PS1='(lfs chroot) \u:\w\$'  \
 PATH=/usr/bin:/usr/sbin     \
 /bin/bash -c "source script.sh; myfn2; rm script.sh"
EOF

stage0

{create-vm 2}
#wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img --output-document host.img
cp ../jammy-server-cloudimg-amd64.img host.img
cp ../prepare-host.sh .

qemu-img resize host.img 10G
qemu-img create -f qcow2 lfs.img 30G

cat > init.sh << "OUTER_EOF"

growpart /dev/sda 1
resize2fs /dev/sda1

passwd -d root
cat << "EOF" > /etc/ssh/sshd_config.d/lfs.conf
PermitRootLogin yes
PermitEmptyPasswords yes
EOF
systemctl restart sshd

apt update
apt install --yes --fix-missing binutils bison gawk gcc g++ m4 make texinfo
ln -sf /bin/bash /bin/sh

OUTER_EOF

virt-customize --add host.img --firstboot init.sh

Used in section 1

now that our host system is ready, we'll boot it up and connect to it via ssh

{launch-vm 2}
qemu-system-x86_64 -m 8G -smp $(nproc) -accel kvm -cpu host \
 -nic user,hostfwd=tcp::2222-:22 \
 -hda host.img \
 -hdb lfs.img \
 -daemonize
 ssh root@127.0.0.1 -p 2222

Redefined in section 8

Used in section 1

from now on, all commands are run on our host through this ssh session, unless otherwise specified.

stage1

we need to partition lfs.img, which has been attached to our system at /dev/sdb. we won't bother creating a swap partition as we have provided plenty of memory to our host. for now, we'll just create boot and root partitions. https://www.gnu.org/software/parted/manual/parted.html

{partition 3}
parted /dev/sdb mklabel gpt

Used in section 1

first we create a partition table on our blank disk. a GPT partition table reserves 34 sectors at the start and end of the disk.

you will have noticed the output Information: You may need to update /etc/fstab. /etc/fstab (from 'filesystem table') is a configuration file which controls the mounting of filesystems when detected by a linux system on boot. provided you complete your LFS build in one session this will not be necessary, but if you have to reboot at some stage there's a good chance you will forget to mount these partitions, so we will update our fstab later.

{partition 3} +=
parted /dev/sdb mkpart boot fat32 34s 1GiB
parted /dev/sdb set 1 esp on

Used in section 1

here we create a partition called "boot", with a filesystem type of fat32. we start our partition at the first available sector, and end it 1 Gibibyte from the start. the output will show Warning: The resulting partition is not properly aligned for best performance: 34s % 2048s != 0s partition alignment is a little involved, and learning about it is out of scope, so we will ignore this. we also set the esp flag to let our UEFI firmware know that this is an EFI System Partition.

{partition 3} +=
parted /dev/sdb -- mkpart root ext4 1GiB -34s print

Used in section 1

then we create our root partition, starting from an offset of 1GiB and ending at the last available sector. in bash and other shells the double dashes -- signify the end of command options, and are required to prevent -34s from being interpreted by parted as an argument.

we can use parted /dev/sdb print to confirm the disk was successfully partitioned. the output should look something like this:

Model: ATA QEMU HARDDISK (scsi)
Disk /dev/sdb: 32.2GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system  Name  Flags
 1      17.4kB  1074MB  1074MB               boot  boot, esp
 2      1074MB  32.2GB  31.1GB  ext4         root
{format 3}
mkfs.fat -F 32 -n BOOT /dev/sdb1
mkfs.ext4 -L ROOT /dev/sdb2

Used in section 1

we create filesystems on our new partitions and label them appropriately

{export variables 3}
echo "export BOOT=/mnt/lfs/boot ROOT=/mnt/lfs/root" >> /root/.bash_profile
source .bash_profile

Used in section 1

export does something when we log in to our host system, bash executes commands found in the .bash_profile file in the user's home directory. this ensures that the variables will always be available, even if we disconnect from or reboot the host.

{mount 3}
mkdir --parents $BOOT $ROOT
mount /dev/sdb1 $BOOT
mount /dev/sdb2 $ROOT
cat >> /etc/fstab << "EOF"
LABEL=ROOT /mnt/lfs/root ext4 defaults 0 0
LABEL=BOOT /mnt/lfs/boot vfat defaults 0 0
EOF

Used in section 1

we create two directories to which we mount our new filesystems, and add rules for mounting them to fstab.

{sources 3}
mkdir $ROOT/sources
chmod -v a+wt $ROOT/sources
cd $ROOT/sources
wget http://ftp.lfs-matrix.net/pub/lfs/lfs-packages/lfs-packages-12.0.tar
tar --extract --file lfs-packages-12.0.tar

Redefined in section 8

Used in section 1

we create a directory sources and modify its permissions such that other users can write to it, but only we (root) can delete files within it. we download an archive of all the required packages from a mirror and extract it

stage2

we have finished preparing our disk, so it is time to start building our new linux system. https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html the FHS documents common practices at a given time and is updated as and when they change. thus most modern linux distributions deviate from the standard in some way, so while it is a useful convention, do not take it to be authoritative.

{directory structure 4}
mkdir --parents $ROOT/{etc,var,lib64} $ROOT/usr/{bin,lib,sbin}
for i in bin lib sbin; do
  ln -sv usr/$i $ROOT/$i
done
mkdir $ROOT/tools

Used in section 1

we begin by creating some required directories /etc is now used for storing configuration files, but historically it held that which did not belong elsewhere, hence the name (et cetera). /var is for storing variable data files; files generated during programs at runtime such as logs and caches. /usr is for shareable read-only data. /usr/bin is the primary store of executables. /usr/lib is for storing shared libraries. /usr/sbin is for storing binaries used for system administration. in common with some other modern linux distributions, we will not differentiate between the bin, lib and sbin directories under /usr and those at the root of our filesystem. however for compatibility reasons the directories are required, so we create symlinks instead. finally we create a tools directory, which will store the cross-compiler that we will use later

{lfs user 4}
groupadd lfs
useradd -s /bin/bash -g lfs -m -k /dev/null lfs
chown lfs $ROOT/{usr{,/*},var,etc,lib64,tools}
rm /etc/bash.bashrc

Used in section 1

to ensure a clean build environment, we create a new user. we'll give it ownership of the directory structure we created. we can now switch to our new user. while we are still root, let's delete /etc/bash.bashrc to prevent it contaminating our build environment.

{bash setup 4}
su - lfs

cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF

cat > ~/.bashrc << "EOF"
set +h
umask 022
BOOT=/mnt/lfs/boot
ROOT=/mnt/lfs/root
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/usr/bin
if [ ! -L /bin ]; then PATH=/bin:$PATH; fi
PATH=$ROOT/tools/bin:$PATH
CONFIG_SITE=$ROOT/usr/share/config.site
export BOOT ROOT LC_ALL LFS_TGT PATH CONFIG_SITE
EOF

source ~/.bash_profile

Redefined in section 8

Used in section 1

we create a .bash_profile and .bashrc, then read the new profile into our current shell with source. the details of these two files are explained in LFS.

stage3 cross-compiler

{pass 1 binutils 5}
cd $ROOT/sources
tar --extract --file 12.0/binutils-2.41.tar.xz
(
cd binutils-2.41
mkdir build
(
cd build
../configure --prefix=$ROOT/tools \
             --with-sysroot=$ROOT \
             --target=$LFS_TGT   \
             --disable-nls       \
             --enable-gprofng=no \
             --disable-werror
make -j $(nproc)
make install
)
)
rm --recursive --force binutils-2.41

Used in section 1

{pass 1 gcc 5}
tar --extract --file  12.0/gcc-13.2.0.tar.xz
(
cd gcc-13.2.0
tar --extract --file ../12.0/mpfr-4.2.0.tar.xz
mv mpfr-4.2.0 mpfr
tar --extract --file ../12.0/gmp-6.3.0.tar.xz
mv gmp-6.3.0 gmp
tar --extract --file ../12.0/mpc-1.3.1.tar.gz
mv mpc-1.3.1 mpc
sed -e '/m64=/s/lib64/lib/' -i.orig gcc/config/i386/t-linux64
mkdir build
(
cd build
../configure                  \
    --target=$LFS_TGT         \
    --prefix=$ROOT/tools      \
    --with-glibc-version=2.38 \
    --with-sysroot=$ROOT      \
    --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 -j $(nproc)
make install
)
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
  $(dirname $($LFS_TGT-gcc -print-libgcc-file-name))/include/limits.h
)
rm --recursive --force gcc-13.2.0

Used in section 1

{linux api headers 5}
tar --extract --file 12.0/linux-6.4.12.tar.xz
(
cd linux-6.4.12
make mrproper
make headers
find usr/include -type f ! -name '*.h' -delete
cp --recursive usr/include $ROOT/usr
)
rm --recursive --force linux-6.4.12

Used in section 1

{glibc 5}
tar --extract --file 12.0/glibc-2.38.tar.xz
(
cd glibc-2.38
ln -sf ../lib/ld-linux-x86-64.so.2 $ROOT/lib64
ln -sf ../lib/ld-linux-x86-64.so.2 $ROOT/lib64/ld-lsb-x86-64.so.3
patch -Np1 -i $ROOT/sources/12.0/glibc-2.38-fhs-1.patch
mkdir build
(
cd build
echo "rootsbindir=/usr/sbin" > configparms
../configure                             \
      --prefix=/usr                      \
      --host=$LFS_TGT                    \
      --build=$(../scripts/config.guess) \
      --enable-kernel=4.14               \
      --with-headers=$ROOT/usr/include    \
      libc_cv_slibdir=/usr/lib
make -j 1
make DESTDIR=$ROOT install
)
sed '/RTLDLIST=/s@/usr@@g' -i $ROOT/usr/bin/ldd
)
rm --recursive --force glibc-2.38

Used in section 1


echo 'int main(){}' | $LFS_TGT-gcc -xc - readelf -l a.out | grep ld-linux rm a.out


{libstdc++ 5}
tar --extract --file 12.0/gcc-13.2.0.tar.xz
(
cd gcc-13.2.0
mkdir 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++/13.2.0
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm $ROOT/usr/lib/lib{stdc++,stdc++fs,supc++}.la
)
rm --recursive --force gcc-13.2.0

Used in section 1

stage4 - cross compiling tools

{m4 6}
tar --extract --file 12.0/m4-1.4.19.tar.xz
(
cd m4-1.4.19
./configure --prefix=/usr   \
            --host=$LFS_TGT \
            --build=$(build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force m4-1.4.19

Used in section 1

{ncurses 6}
tar --extract --file 12.0/ncurses-6.4.tar.gz
(
cd ncurses-6.4
sed -i s/mawk// configure
mkdir build
(
cd build
../configure
make -C include -j $(nproc)
make -C progs tic -j $(nproc)
)
./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 -j $(nproc)
make DESTDIR=$ROOT TIC_PATH=$(pwd)/build/progs/tic install
echo "INPUT(-lncursesw)" > $ROOT/usr/lib/libncurses.so
)
rm --recursive --force ncurses-6.4

Used in section 1

{bash 6}
tar --extract --file 12.0/bash-5.2.15.tar.gz
(
cd bash-5.2.15
./configure --prefix=/usr                      \
            --build=$(sh support/config.guess) \
            --host=$LFS_TGT                    \
            --without-bash-malloc
make -j $(nproc)
make DESTDIR=$ROOT install
ln -s bash $ROOT/bin/sh
)
rm --recursive --force bash-5.2.15

Used in section 1

{coreutils 6}
tar --extract --file 12.0/coreutils-9.3.tar.xz
(
cd coreutils-9.3
./configure --prefix=/usr                     \
            --host=$LFS_TGT                   \
            --build=$(build-aux/config.guess) \
            --enable-install-program=hostname \
            --enable-no-install-program=kill,uptime \
            gl_cv_macro_MB_CUR_MAX_good=y
make -j $(nproc)
make DESTDIR=$ROOT install
mv $ROOT/usr/bin/chroot              $ROOT/usr/sbin
mkdir --parents $ROOT/usr/share/man/man8
mv $ROOT/usr/share/man/man1/chroot.1 $ROOT/usr/share/man/man8/chroot.8
sed -i 's/"1"/"8"/'                    $ROOT/usr/share/man/man8/chroot.8
)
rm --recursive --force coreutils-9.3

Used in section 1

{diffutils 6}
tar --extract --file 12.0/diffutils-3.10.tar.xz
(
cd diffutils-3.10
./configure --prefix=/usr   \
            --host=$LFS_TGT \
            --build=$(./build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force diffutils-3.10

Used in section 1

{file 6}
tar --extract --file 12.0/file-5.45.tar.gz
(
cd file-5.45
mkdir build
(
cd build
../configure --disable-bzlib      \
             --disable-libseccomp \
             --disable-xzlib      \
             --disable-zlib
make
)
./configure --prefix=/usr --host=$LFS_TGT --build=$(./config.guess)
make -j $(nproc) FILE_COMPILE=$(pwd)/build/src/file
make DESTDIR=$ROOT install
rm $ROOT/usr/lib/libmagic.la
)
rm --recursive --force file-5.45

Used in section 1

{findutils 6}
tar --extract --file 12.0/findutils-4.9.0.tar.xz
(
cd findutils-4.9.0
./configure --prefix=/usr                   \
            --localstatedir=/var/lib/locate \
            --host=$LFS_TGT                 \
            --build=$(build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force findutils-4.9.0

Used in section 1

{gawk 6}
tar --extract --file 12.0/gawk-5.2.2.tar.xz 
(
cd gawk-5.2.2
sed -i 's/extras//' Makefile.in
./configure --prefix=/usr   \
            --host=$LFS_TGT \
            --build=$(build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force gawk-5.2.2

Used in section 1

{grep 6}
tar --extract --file 12.0/grep-3.11.tar.xz
(
cd grep-3.11
./configure --prefix=/usr   \
            --host=$LFS_TGT \
            --build=$(./build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force grep-3.11

Used in section 1

{gzip 6}
tar --extract --file 12.0/gzip-1.12.tar.xz
(
cd gzip-1.12
./configure --prefix=/usr --host=$LFS_TGT
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force gzip-1.12

Used in section 1

{make 6}
tar --extract --file 12.0/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 -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force make-4.4.1

Used in section 1

{patch 6}
tar --extract --file 12.0/patch-2.7.6.tar.xz
(
cd patch-2.7.6
./configure --prefix=/usr   \
            --host=$LFS_TGT \
            --build=$(build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force patch-2.7.6

Used in section 1

{sed 6}
tar --extract --file 12.0/sed-4.9.tar.xz
(
cd sed-4.9
./configure --prefix=/usr   \
            --host=$LFS_TGT \
            --build=$(./build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force sed-4.9

Used in section 1

{tar 6}
tar --extract --file 12.0/tar-1.35.tar.xz
(
cd tar-1.35
./configure --prefix=/usr                     \
            --host=$LFS_TGT                   \
            --build=$(build-aux/config.guess)
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force tar-1.35

Used in section 1

{xz 6}
tar --extract --file 12.0/xz-5.4.4.tar.xz
(
cd xz-5.4.4
./configure --prefix=/usr                     \
            --host=$LFS_TGT                   \
            --build=$(build-aux/config.guess) \
            --disable-static                  \
            --docdir=/usr/share/doc/xz-5.4.4
make -j $(nproc)
make DESTDIR=$ROOT install
rm $ROOT/usr/lib/liblzma.la
)
rm --recursive --force xz-5.4.4

Used in section 1

{pass 2 binutils 6}
tar --extract --file 12.0/binutils-2.41.tar.xz
(
cd  binutils-2.41
sed '6009s/$add_dir//' -i ltmain.sh
mkdir 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
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm -v $ROOT/usr/lib/lib{bfd,ctf,ctf-nobfd,opcodes,sframe}.{a,la}
)
rm --recursive --force binutils-2.41

Used in section 1

{pass 2 gcc 6}
tar --extract --file 12.0/gcc-13.2.0.tar.xz
(
cd gcc-13.2.0
tar -xf ../12.0/mpfr-4.2.0.tar.xz
mv -v mpfr-4.2.0 mpfr
tar -xf ../12.0/gmp-6.3.0.tar.xz
mv -v gmp-6.3.0 gmp
tar -xf ../12.0/mpc-1.3.1.tar.gz
mv -v mpc-1.3.1 mpc
sed -e '/m64=/s/lib64/lib/' -i.orig gcc/config/i386/t-linux64
sed '/thread_header =/s/@.*@/gthr-posix.h/' \
    -i libgcc/Makefile.in libstdc++-v3/include/Makefile.in
mkdir 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=$ROOT                     \
    --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 -j $(nproc)
make DESTDIR=$ROOT install
)
ln -sv gcc $ROOT/usr/bin/cc
)
rm --recursive --force gcc-13.2.0

Used in section 1

stage4 chroot

we are now going to chroot into our newly built environment

{revert dir ownership 7}
#exit
chown --recursive root:root $ROOT/{usr,lib,var,etc,bin,sbin,lib64,tools}

Used in section 1

first we change ownership of all the directories we created back to root, as the LFS user was only needed temporarily for cleanly building the cross-toolchain and won't exist on our new system.

{mount device files 7}
mkdir --parents $ROOT/{dev,proc,sys,run}
mount --bind /dev $ROOT/dev
mount --bind /dev/pts $ROOT/dev/pts
mount --types proc proc $ROOT/proc
mount --types sysfs sysfs $ROOT/sys
mount --types tmpfs tmpfs $ROOT/run
mount --types tmpfs --options nosuid,nodev tmpfs $ROOT/dev/shm

Used in section 1

we mount all our special device files

{enter chroot 7}
/usr/sbin/chroot "$ROOT" /usr/bin/env -i  \
 HOME=/root                  \
 TERM="$TERM"                \
 PS1='(lfs chroot) \u:\w\$ ' \
 PATH=/usr/bin:/usr/sbin     \
 /bin/bash --login

enter chroot

{chroot1 7}
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

Used in section 1

{chroot2 7}
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 Generation 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:
usb:x:14:
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

Used in section 1

{chroot3 7}
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

cd sources

tar --extract --file 12.0/gettext-0.22.tar.xz ( cd gettext-0.22 ./configure --disable-shared make -j (nproc) cp -v gettext-tools/src/{msgfmt,msgmerge,xgettext} /usr/bin ) rm --recursive --force gettext-0.22



tar --extract --file 12.0/bison-3.8.2.tar.xz ( cd bison-3.8.2 ./configure --prefix=/usr \ --docdir=/usr/share/doc/bison-3.8.2 make -j (nproc) make install ) rm --recursive --force bison-3.8.2



tar --extract --file 12.0/perl-5.38.0.tar.xz ( cd perl-5.38.0 sh Configure -des \ -Dprefix=/usr \ -Dvendorprefix=/usr \ -Duseshrplib \ -Dprivlib=/usr/lib/perl5/5.38/core_perl \ -Darchlib=/usr/lib/perl5/5.38/core_perl \ -Dsitelib=/usr/lib/perl5/5.38/site_perl \ -Dsitearch=/usr/lib/perl5/5.38/site_perl \ -Dvendorlib=/usr/lib/perl5/5.38/vendor_perl \ -Dvendorarch=/usr/lib/perl5/5.38/vendor_perl make -j (nproc) make install ) rm --recursive --force perl-5.38.0



tar --extract --file 12.0/Python-3.11.4.tar.xz ( cd Python-3.11.4 ./configure --prefix=/usr \ --enable-shared \ --without-ensurepip make -j (nproc) make install ) rm --recursive --force Python-3.11.4



tar --extract --file 12.0/texinfo-7.0.3.tar.xz ( cd texinfo-7.0.3 ./configure --prefix=/usr make -j (nproc) make install ) rm --recursive --force texinfo-7.0.3



tar --extract --file 12.0/util-linux-2.39.1.tar.xz ( cd util-linux-2.39.1 mkdir -pv /var/lib/hwclock ./configure ADJTIME_PATH=/var/lib/hwclock/adjtime \ --libdir=/usr/lib \ --runstatedir=/run \ --docdir=/usr/share/doc/util-linux-2.39.1 \ --disable-chfn-chsh \ --disable-login \ --disable-nologin \ --disable-su \ --disable-setpriv \ --disable-runuser \ --disable-pylibmount \ --disable-static \ --without-python make -j (nproc) make install ) rm --recursive --force util-linux-2.39.1


{cleanup 7}
rm -rf /usr/share/{info,man,doc}/*
find /usr/{lib,libexec} -name \*.la -delete
rm -rf /tools

stage5

{launch-vm 2} :=
qemu-system-x86_64 -m 8G -smp $(nproc) -accel kvm -cpu host \
 -nic user,hostfwd=tcp::2222-:22 \
 -hda host.img \
 -hdb lfs.img \
 -daemonize

Used in section 1

{sources 3} :=
mkdir $ROOT/sources
chmod -v a+wt $ROOT/sources
cd $ROOT/sources
wget --progress=dot:giga http://ftp.lfs-matrix.net/pub/lfs/lfs-packages/lfs-packages-12.0.tar
tar --extract --file lfs-packages-12.0.tar

Used in section 1

{bash setup 4} :=
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF

cat > ~/.bashrc << "EOF"
set +h
umask 022
BOOT=/mnt/lfs/boot
ROOT=/mnt/lfs/root
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/usr/bin
if [ ! -L /bin ]; then PATH=/bin:$PATH; fi
PATH=$ROOT/tools/bin:$PATH
CONFIG_SITE=$ROOT/usr/share/config.site
export BOOT ROOT LC_ALL LFS_TGT PATH CONFIG_SITE
EOF

Used in section 1

{foo 8}
tar --extract --file 12.0/
(
cd 
make -j $(nproc)
make DESTDIR=$ROOT install
)
rm --recursive --force