Linux From Scratch v7.3
I Preliminaries
The prelims are quite uninteresting. Mostly introductory material on the project, required knowledge, and other concepts like creating a partition for the LFS build and packages and reasons why they were included -like I said introductory material.
Additionally, I have alternated between building 7.0 and 7.3 so many times I've lost count, a powerful motivating factor in the writing of this blog, so prelims is really not interesting.
The Concept
Building a Linux system revolves around building a set of temporary tools independent of our build machine. This is achieved by;
- Building a cross-compiled toolchain (binutils, gcc, [linux-headers], glibc)
- Building a temporary Linux system (gcc and all) using X-toolchain above
- Building a complete (minimal) final system using the temporary system.
binutils is the BINary UTILities package (collection of programs) that contains development tools like assembler and the linker. gcc is the Gnu Compiler Collection package, that contains the compilers for different programming languages (basically). The linux-headers is the collection of user visible system API that the C standard library glibc depends on to provide it's various services, it also contains the dynamic linker.
See Chapter 5.2 of the LFS book for details. Please Note that /tools is a symlink to /mnt/lfs/tools.
II Building The Temporary System
Note: The packages we discuss here are those that did not go according to the book for me, or I did not completely understand first time. Also the temporary toolchain will be installed to a separate location, /tools or /mnt/lfs/tools -refer to chapter 4 and 5 for more details on the reason for this.
Binutils PASS 1
../binutils-2.23.1/configure \
--prefix=/tools \
--with-sysroot=$LFS \
--with-lib-path=/tools/lib \
--target=$LFS_TGT \
--disable-nls \
--disable-werror
The configuration option --target=$LFS_TGT tells the build system we want to cross-compile binutils to build binaries for the $LFS_TGT architecture. --with-lib-path=/tools/lib specifies the library search part for ld, the linker, further isolating this binutils build from the host system.
Gcc PASS 1
for file in \
$(find gcc/config -name linux64.h -o -name linux.h -o -name sysv4.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
$(find gcc/config -name linux64.h -o -name linux.h -o -name sysv4.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
The above commands modify the gcc source files to look in /tools/* for the dynamic linker to use, for binaries to be built.
../gcc-4.7.2/configure \
--target=$LFS_TGT \
--prefix=/tools \
--with-sysroot=$LFS \
--with-newlib \
--without-headers \
--with-local-prefix=/tools \
--with-native-system-header-dir=/tools/include \
...
Glibc PASS 1
../glibc-2.17/configure \
--prefix=/tools \
--host=$LFS_TGT \
--build=$(../glibc-2.17/scripts/config.guess) \
...
Continuing with our breakaway plans we have cross-compile glibc. --host indicates that the binaries that are going to use our C libraries are going to run on the $LFS_TGT architecture, and we are building our glibc using the build tools on our current host the --build part. Critical in this step is that programs built with gcc above should use /tools/ld-linux.so.2 - our just compiled glibc, not /lib/ld-linux.so.2 from the host system.
At the end of the above set of installations, we have a cross-compiled toolchain which we will now use to build a complete native toolchain for $LFS_TGT machine, i.e, we will build binutils, gcc, and glibc to run natively (here natively means independent) on $LFS_TGT architecture, including testing packages such as expect, check and tcl, we also coreutils, findutils, file, etc. In sum we will be build a complete set of tools to build our final system, which is completely independent of our host system.
Please note that to do the native builds we use the toolchain installed above, for example $LFS_TGT-gcc, $LFS_TGT-ranlib.
At the end of the above set of installations, we have a cross-compiled toolchain which we will now use to build a complete native toolchain for $LFS_TGT machine, i.e, we will build binutils, gcc, and glibc to run natively (here natively means independent) on $LFS_TGT architecture, including testing packages such as expect, check and tcl, we also coreutils, findutils, file, etc. In sum we will be build a complete set of tools to build our final system, which is completely independent of our host system.
Please note that to do the native builds we use the toolchain installed above, for example $LFS_TGT-gcc, $LFS_TGT-ranlib.
Binutils PASS 2
CC=$LFS_TGT-gcc \
AR=$LFS_TGT-ar \
RANLIB=$LFS_TGT-ranlib \
../binutils-2.23.1/configure \
--prefix=/tools \
--disable-nls \
--with-lib-path=/tools/lib
Quoting the LFS book ...
CC=$LFS_TGT-gcc AR=$LFS_TGT-ar RANLIB=$LFS_TGT-ranlib
Because this is really a native build of Binutils, setting these variables ensures that the build system uses the
cross-compiler and associated tools instead of the ones on the host system.
--with-lib-path=/tools/lib
This tells the configure script to specify the library search path during the compilation of Binutils, resulting in
/tools/lib being passed to the linker. This prevents the linker from searching through library directories on
the host.
We also go to the trouble of building an ld properly configured to search for libraries in the /usr/lib:/lib path for later use during the build of our final system.
make -C ld clean
make -C ld LIB_PATH=/usr/lib:/lib
cp -v ld/ld-new /tools/bin
Gcc PASS 2
Again we change the gcc source files to point to /tools/* for our dynamic linker, see gcc PASS 1.
../gcc-4.7.2/configure \
--prefix=/tools \
--with-local-prefix=/tools \
--with-native-system-header-dir=/tools/include \
...
Notice that there is no glibc PASS 2? This is because glibc was built to be linked against by binaries for $LFS_TGT using the --host=$LFS_TGT option to configure, and installed to /tools/* so everything is set already for us to start building, no need to build glibc again. So we go ahead to build more the additional packages to build a more feature full temporary Linux system (toolchain) and install them under /tools.
Coreutils
As of version 8.17 coreutils no longer bundles su, su comes with the shadow-4.1.5 package now. This fact is mentioned because, earlier versions of LFS recommends building su and installing as /tools/bin/su-tools at this stage and we chose to use the more_control_helpers package management option (http://www.linuxfromscratch.org/hints/downloads/files/more_control_and_pkg_man.txt) with phinix which requires su to be installed. The shadow su does not work with our package system, so we download a custom su written by the inventor of the User Based management system from http://wiki.linuxfromscratch.org/hints/browser/trunk/PREVIOUS_FORMAT/more_control_and_pkg_man.txt?rev=904 and compile it and copy it to /tools/bin/;
# gcc -o /tools/bin/su su.c
Note: At this point the book advices us to backup our /tools directory. We can do that by;
# tar -cjvf lfs-temporary-20130524.tar.bz2 /tools
The catch however is restoring from our back up. It requires that you recreate the directory structure we've been using so far;
# mkdir -pv /mnt/lfs/tools
and then following the steps in the Part III of the book, Building the LFS system.
III Building The LFS System
At this stage we have a working albeit unbootable clean building environment, all contained in /tools. Our next task is to chroot into /tools, setup access to all devices, and start building our final system.
The package management style we chose is the User Based Package system, devised by Matthias Benkmann, mostly for experimentation and the sound arguments he makes for it.
Package Installation with User Based Package Management
After the preparation and before the installation of the first package in our LFS system, linux-libc-headers, we will now install the more_control_and_pkg package from http://www.linuxfromscratch.org/hints/downloads/files/ATTACHMENTS/more_control_and_pkg_man/. We just follow the installation instructions no more.
Note: The version of su we got in the section on coreutils above will fail if called with '-'. So we modify the /usr/bin/install_package script to call our installed /tools/su like this;
# Absolute path forces the call to fail in case We forget to change it back after chapter 6
/tools/bin/su $2
# su - $2
The environment is properly setup any ways so we don't risk breaking our build. To check this run;
# echo $PATH
The output should be something like this;
/usr/lib/pkgusr:/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin
Another thing we do is to run the list_package program and save it's output to a file, this acts like our package file listing, and it's fast compared to running list_package everytime we want a listing of files installed by package A.
list_package pkgname > /var/lib/pkgusr/pkgname.lst
Glibc
Glibc installation fails with;
/usr/lib/pkgusr/install -c -m 644 ../sysdeps/unix/sysv/linux/sys/vt.h /usr/include/sys/vt.h
/usr/lib/pkgusr/install -c -m 644 ../sysdeps/unix/sysv/linux/sys/quota.h /usr/include/sys/quota.h
/usr/lib/pkgusr/install -c -m 644 ../sysdeps/unix/sysv/linux/sys/fsuid.h /usr/include/sys/fsuid.h
/usr/lib/pkgusr/install -c -m 644 ../sysdeps/unix/sysv/linux/scsi/sg.h /usr/include/scsi/sg.h
/tools/bin/install: cannot create regular file '/usr/include/scsi/sg.h': Permission denied
make[2]: *** [/usr/include/scsi/sg.h] Error 1
make[2]: Leaving directory `/usr/src/glibc/glibc-2.17/misc'
make[1]: *** [misc/subdir_install] Error 2
make[1]: Leaving directory `/usr/src/glibc/glibc-2.17'
make: *** [install] Error 2
This is because the subdirectories under the /usr/include path belong to the linux-libc-headers and is not accessible to the glibc user, to fix this we change the group of these directories to install, and make them group writable with the following commands;
# find /usr/include -maxdepth 1 -type d -exec chgrp -v install {} \;
group of '/usr/include' retained as install
changed group of '/usr/include/linux' from linux-libc-headers to install
changed group of '/usr/include/asm-generic' from linux-libc-headers to install
changed group of '/usr/include/sys' from glibc to install
...
# find /usr/include -maxdepth 1 -type d -exec chmod -v ug=rwx,o=rxt {} \;
mode of '/usr/include' retained as 1775 (rwxrwxr-t)
mode of '/usr/include/linux' changed from 0755 (rwxr-xr-x) to 1775 (rwxrwxr-t)
mode of '/usr/include/asm-generic' changed from 0755 (rwxr-xr-x) to 1775 (rwxrwxr-t)
mode of '/usr/include/sys' changed from 0755 (rwxr-xr-x) to 1775 (rwxrwxr-t)
mode of '/usr/include/drm' changed from 0755 (rwxr-xr-x) to 1775 (rwxrwxr-t)
...
# chgrp install /var
# chmod ug=rwx,o=rxt /var
Now glibc will build and install correctly. Dont forget to run list_package on the glibc user.
Configuring glibc
We create /etc/nsswitch.conf file as root, and installed the timzone information as glibc. All files and directories creatd during the configuration of glibc apart from timezone information is owned by root.
Adjusting the Toolchain
After our final installation of glibc, we adjust the toolchain to point to our new glibc installation. This involves
- Using the /tools/bin/ld-new we built in PASS 2 of glibc as our new linker. Remember it's /tools/bin/ld-new is already built to look for library files in /usr/lib:/lib.
- Modifying the toolchain's gcc spec file to find correct headers and Glibc start files (those installed in Chapter 6 to /usr/include for headers, and /usr/lib for start files).
If all the tests we run in the Readjusting section is successful, then from now on all binaries we build be linked properly to our new LFS system. So we can go ahead and build the remainder of our packages.
Note however that some packages may install libraries and need to update /etc/ld.so.cache. These packages must be granted access so following the tip in the user based management document, we compile /usr/lib/pkgusr/ldconfig.c and assign the following permissions;
cd /usr/lib/pkgusr
gcc -O2 -W -Wall -o ldconfig ldconfig.c
chown root:install ldconfig
chmod u=rwxs,g=rxs,o= ldconfig
Despite the above precaution we notice that the zlib package changes the owner and group of /etc/ld.so.cache, so we change it back to root:install manually and a second build attempt fails to modify ownership of /etc/ld.so.cache, which is what we want.
On ownership of files created or manually edited by us, we assign the owner of those files to the root user as recommended by the author of the User Based Package management system. This way the aim of preventing package installers overwritten our manual configurations can be achieved.
Note however that some packages may install libraries and need to update /etc/ld.so.cache. These packages must be granted access so following the tip in the user based management document, we compile /usr/lib/pkgusr/ldconfig.c and assign the following permissions;
cd /usr/lib/pkgusr
gcc -O2 -W -Wall -o ldconfig ldconfig.c
chown root:install ldconfig
chmod u=rwxs,g=rxs,o= ldconfig
Despite the above precaution we notice that the zlib package changes the owner and group of /etc/ld.so.cache, so we change it back to root:install manually and a second build attempt fails to modify ownership of /etc/ld.so.cache, which is what we want.
On ownership of files created or manually edited by us, we assign the owner of those files to the root user as recommended by the author of the User Based Package management system. This way the aim of preventing package installers overwritten our manual configurations can be achieved.
Installing Packages Using the Build Script that Comes With more_control_and_package_helpers
The more_control_and_package_helper archives comes with a number of scripts written to simplify build, perform proper error logging, and package management actions like listing files installed by packages, suspicious files like those with setuid.
We make some changes to these scripts to suit our build setup, and this section discusses the rationale behind these changes, and where to get a copy of our changes.
Package Management Scripts
The only script we modify in the category is the /usr/sbin/install_package. The modification is listed below;
if [ $UID -ne 0 ]; then echo Please run this script as root. ; exit 1; fi
add_package_user "${1}" $2 10000 20000 $3 10000 20000 || exit 1
# Absolute path forces the call to fail in case I forget to change it back after chap 6
/tools/bin/su $2
# su - $2
This modification was done because prior to the installation of glibc, the su program that comes with the shadow program does not work, due to its dependency on glibc. But we needed a su utility to progress with the installation methods in the User Based Package management approach, so we download a custom copy from http://wiki.linuxfromscratch.org/hints/browser/trunk/PREVIOUS_FORMAT/more_control_and_pkg_man.txt?rev=904, compile it and install this temporary su to /tools/bin.
Then we modify /usr/sbin/install_package to use this /tools/bin/su to switch users. You may have noticed the fact that /tools/bin/su is called without '-', this is because this su is built that way, and there is no problem of not setting up our package user environment properly. This fact can be verified by running echo $PATH after the switch, and noting that the path begins with /usr/lib/pkgusr and ends with /tools/bin as is expected.
We must however remember to revert /usr/bin/install_package to it's original behaviour after installing and configuring shadow, by deleting the line; /tools/bin/su $2 and uncommenting the line; su - $2
Package Building Scripts












































