Building Linux From Scratch on a Google Cloud Virtual Machine

Part 2: Building the Temporary System

Michael Fitzgerald
6 min readAug 15, 2020

Links to Part 1, Part 3

A barn raising in Canada. Source: Wikipedia

References are made to LFS Version 9.1-systemd using the format LFS x.x.x… for specific sections. Command blocks are largely copied verbatim with minor customizations for this setup.

This part of the tutorial will comprise annotations for each build step for the temporary system in LFS 5.4–5.35 and some notes on system preparation for the final build.

Increasing compute resources (Optional).

Caution! Doing this could cost you money. Make yourself aware of the costs associated with using more compute resources. You can complete the LFS build without doing this.

I am going to increase the compute power of my VM to 8 cpus and 32GB of memory so that we can take advantage of parallel compilation and speed up the overall build time. A list of machine types can be found here.

LFS 4.5 warns that using parallel compilation could lead to failed builds and debug messages that are hard to interpret. We are going to try anyway and adjust as needed.

#stop instance
gcloud compute instances stop [INSTANCE-NAME]
#change machine type
gcloud compute instances set-machine-type [INSTANCE-NAME] --machine-type e2-standard-8
#start instance
gcloud compute instances start [INSTANCE-NAME]

The output of nproc should be 8.

Log in as the lfs user.

#start login shell
su - lfs
#if your prompt doesn't switch use the foreground command
fg

Binutils

This is the first pass build of binutils. We will time the compilation and installation to determine the SBU.

cd $LFS/sourcestar -xf binutils-2.34.tar.xz && cd binutils-2.34mkdir -v build && cd build#wrap in time function to compute SBU
#this will be the only time we doe this.
time { ../configure --prefix=/tools \
--with-sysroot=$LFS \
--with-lib-path=/tools/lib \
--target=$LFS_TGT \
--disable-nls \
--disable-werror && \
make -j$(nproc) && \
case $(uname -m) in
x86_64) mkdir -v /tools/lib && ln -sv lib /tools/lib64 ;;
esac && \
make install; }
#end time block
cd $LFS/sources && rm -rf binutils

Here is the output of the time function:

real 0m50.484s
user 2m45.603s
sys 0m28.736s

This suggests that our SBU is about 1 minute. We can use that to estimate build times for other packages. Again, this assumes you are using the same machine type with 8 processors.

GCC (10 minute estimated build time)

#step 1: 
tar -xf gcc && mv gcc-9.2.0 gcc & cd gcc
#step 2: untar additional required packages
tar -xf ../mpfr-4.0.2.tar.xz && \
mv -v mpfr-4.0.2 mpfr && \
tar -xf ../gmp-6.2.0.tar.xz && \
mv -v gmp-6.2.0 gmp && \
tar -xf ../mpc-1.1.0.tar.gz && \
mv -v mpc-1.1.0 mpc
#step 2: change default dl location (verbatim LFS 5.5)
for file in gcc/config/{linux,i386/linux{,64}}.h
do
cp -uv $file{,.orig}
sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \
-e 's@/usr@/tools@g' $file.orig > $file
echo '
#undef STANDARD_STARTFILE_PREFIX_1
#undef STANDARD_STARTFILE_PREFIX_2
#define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/"
#define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
touch $file.orig
done
#step 3: point to 64-bit libs (verbatim LFS 5.5)
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
#step 5:
mkdir -v build && cd build
#step 6:
../configure \
--target=$LFS_TGT \
--prefix=/tools \
--with-glibc-version=2.28 \
--with-sysroot=$LFS \
--with-newlib \
--without-headers \
--with-local-prefix=/tools \
--with-native-system-header-dir=/tools/include \
--disable-nls \
--disable-shared \
--disable-multilib \
--disable-decimal-float \
--disable-threads \
--disable-libatomic \
--disable-libgomp \
--disable-libquadmath \
--disable-libssp \
--disable-libvtv \
--disable-libstdcxx \
--enable-languages=c,c++
#step 7:
make -j$(nproc)
#step 8:
make install
#step 9: cleanup
cd $LFS/sources && rm -rf gcc

Actual build time: 5 minutes

Linux API Headers (< 1 minute estimated build time)

#step 1
tar -xf linux-5.5.3.tar.xz && cd linux-5.5.3
#step 2: clean the directory
make mrproper
#step 3:
make headers
#step 4: copy headers to /tools/include
cp -rv usr/include/* /tools/include
#step 5: cleanup
cd $LFS/sources && rm -rf linux-5.5.3

Glibc (5 minute estimated build time)

From here on I will omit the extraction step for conciseness.

There is a specific warning that this build might fail with parallel make.

mkdir -v build && cd build../configure                             \
--prefix=/tools \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--enable-kernel=3.2 \
--with-headers=/tools/include
make -j$(nproc)make install

Actual build time: 3 minutes.

Parallel make did not seem to fail. LFS 5.7.1 recommends a sanity check at this point that tries to compile a dummy C program. Our build passed.

Libstdc++ (< 1 minute estimated build time)

This is included in the gcc source code, which needs to be extracted again.

I will omit build directory creation, make, and install steps here forward.

../libstdc++-v3/configure           \
--host=$LFS_TGT \
--prefix=/tools \
--disable-multilib \
--disable-nls \
--disable-libstdcxx-threads \
--disable-libstdcxx-pch \
--with-gxx-include-dir=/tools/$LFS_TGT/include/c++/9.2.0

Binutils Pass 2 (1 minute estimated build time)

CC=$LFS_TGT-gcc                \
AR=$LFS_TGT-ar \
RANLIB=$LFS_TGT-ranlib \
../configure \
--prefix=/tools \
--disable-nls \
--disable-werror \
--with-lib-path=/tools/lib \
--with-sysroot
#after make and install, prepare the linkermake -C ld cleanmake -C ld LIB_PATH=/usr/lib:/libcp -v ld/ld-new /tools/bin

GCC Pass 2 (13 minute estimated build time)

Caveat: make sure you are in the extracted gcc-9.20 source folder

#create full version of internal headercat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
`dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include-fixed/limits.h
# point to appropriate linker
for file in gcc/config/{linux,i386/linux{,64}}.h
do
cp -uv $file{,.orig}
sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \
-e 's@/usr@/tools@g' $file.orig > $file
echo '
#undef STANDARD_STARTFILE_PREFIX_1
#undef STANDARD_STARTFILE_PREFIX_2
#define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/"
#define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
touch $file.orig
done
#adjust for 64 bit system
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
#pull in required files
tar -xf ../mpfr-4.0.2.tar.xz && \
mv -v mpfr-4.0.2 mpfr && \
tar -xf ../gmp-6.2.0.tar.xz && \
mv -v gmp-6.2.0 gmp && \
tar -xf ../mpc-1.1.0.tar.gz && \
mv -v mpc-1.1.0 mpc
#fix known issue
sed -e '1161 s|^|//|' \
-i libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc
#configure build
CC=$LFS_TGT-gcc \
CXX=$LFS_TGT-g++ \
AR=$LFS_TGT-ar \
RANLIB=$LFS_TGT-ranlib \
../configure \
--prefix=/tools \
--with-local-prefix=/tools \
--with-native-system-header-dir=/tools/include \
--enable-languages=c,c++ \
--disable-libstdcxx-pch \
--disable-multilib \
--disable-bootstrap \
--disable-libgomp
#after make and install, symlink gcc to ccln -sv gcc /tools/bin/cc

The actual build and install time was approximately 5 minutes. The build passed the recommended sanity check described in LFS 5.10.

Tcl (< 1 minute)

cd unix
./configure --prefix=/tools
#after make and install
chmod -v u+w /tools/lib/libtcl8.6.so
make install-private-headers
ln -sv tclsh8.6 /tools/bin/tclsh

Expect (< 1 minute)

cp -v configure{,.orig}sed 's:/usr/local/bin:/bin:' configure.orig > configure./configure --prefix=/tools       \
--with-tcl=/tools/lib \
--with-tclinclude=/tools/include
# different install
make SCRIPTS="" install

DejaGNU (< 1 minute)

./configure --prefix=/tools

M4 (< 1 minute)

sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' lib/*.c
echo "#define _IO_IN_BACKUP 0x100" >> lib/stdio-impl.h
./configure --prefix=/tools

Ncurses (< 1 minute)

sed -i s/mawk// configure./configure --prefix=/tools \
--with-shared \
--without-debug \
--without-ada \
--enable-widec \
--enable-overwrite
#after make and install
ln -s libncursesw.so /tools/lib/libncurses.so

Bash (< 1 minute)

./configure --prefix=/tools --without-bash-malloc#after make and install
ln -sv bash /tools/bin/sh

Bison (< 1 minute)

./configure --prefix=/tools

Bzip (< 1 minute)

make -f Makefile-libbz2_so
make clean
#to installmake PREFIX=/tools install
cp -v bzip2-shared /tools/bin/bzip2
cp -av libbz2.so* /tools/lib
ln -sv libbz2.so.1.0 /tools/lib/libbz2.so

Coreutils (< 1 minute)

./configure --prefix=/tools --enable-install-program=hostname

Diffutils (< 1 minute)

./configure --prefix=/tools

File (< 1 minute)

./configure --prefix=/tools

Findutils (< 1 minute)

./configure --prefix=/tools

Gawk (< 1 minute)

./configure --prefix=/tools

Gettext (~ 2 minutes)

./configure --disable-shared#to install
cp -v gettext-tools/src/{msgfmt,msgmerge,xgettext} /tools/bin

Grep (< 1 minute)

./configure --prefix=/tools

Gzip (< 1 minute)

./configure --prefix=/tools

Make (< 1 minute)

./configure --prefix=/tools --without-guile

Patch (< 1 minute)

./configure --prefix=/tools

Perl (~ 2 minutes)

sh Configure -des -Dprefix=/tools -Dlibs=-lm -Uloclibpth -Ulocincpth#to install
cp -v perl cpan/podlators/scripts/pod2man /tools/bin
mkdir -pv /tools/lib/perl5/5.30.1
cp -Rv lib/* /tools/lib/perl5/5.30.1

Python (~ 2 minutes)

Note: extract the (capital P) Python source archive.

sed -i '/def add_multiarch_paths/a \        return' setup.py./configure --prefix=/tools --without-ensurepip

Sed (< 1 minute)

./configure --prefix=/tools

Tar (< 1 minute)

./configure --prefix=/tools

Texinfo (< 1 minute)

./configure --prefix=/tools

Util-linux (~ 1 minute)

./configure --prefix=/tools                \
--without-python \
--disable-makeinstall-chown \
--without-systemdsystemunitdir \
--without-ncurses \
PKG_CONFIG=""

Xz (< 1 minute)

./configure --prefix=/tools

Final Preparations

We will skip the optional stripping step since we have tons of space.

Change the ownership of $LFS/tools to root.

chown -R root:root $LFS/tools

This concludes Part 2.

Up next in Part 3: Building the LFS System (LFS 6.1–6.80).

--

--