5.2.1. Installation of Cross Binutils Note
Go back and re-read the notes in the section titled General Compilation Instructions. Understanding the notes labeled important can save you a lot of problems later.
It is important that Binutils be the first package compiled because both Glibc and GCC perform various tests on the available linker and assembler to determine which of their own features to enable.
The Binutils documentation recommends building Binutils in a dedicated build directory:
Note
In order for the SBU values listed in the rest of the book to be of any use, measure the time it takes to build this package from the configuration, up to and including the first install. To achieve this easily, wrap the commands in a time command like this: time { ../configure ... && make && make install; } .
Now prepare Binutils for compilation:
../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
Note
Contrary to other packages, not all the options listed below appear when running ./configure --help . For example, to find the --with-sysroot option, you have to run ld/configure --help . All the options can be liste at once with ./configure --help=recursive.
The meaning of the configure options:
--prefix=$LFS/tools
This tells the configure script to prepare to install the Binutils programs in the $LFS/tools directory.
--with-sysroot=$LFS
For cross compilation, this tells the build system to look in $LFS for the target system libraries as needed.
--target=$LFS_TGT
Because the machine description in the LFS_TGT variable is slightly different than the value returned by the config.guess script, this switch will tell the configure script to adjust binutil's build system for building a cross linker.
--disable-nls
This disables internationalization as i18n is not needed for the temporary tools.
--enable-gprofng=no
This disables building gprofng which is not needed for the temporary tools.
--disable-werror
This prevents the build from stopping in the event that there are warnings from the host's compiler.
--enable-new-dtags
This makes the linker use the “runpath” tag for embedding library search paths into executables and shared libraries, instead of the traditional “rpath” tag. It makes debugging dynamically linked executables easier and works around potential issues in the test suite of some packages.
--enable-default-hash-style=gnu
By default, the linker would generate both the GNU-style hash table and the classic ELF hash table for shared libraries and dynamically linked executables. The hash tables are only intended for a dynamic linker to perform symbol lookup. On LFS the dynamic linker (provided by the Glibc package) will always use the GNU-style hash table which is faster to query. So the classic ELF hash table is completely useless. This makes the linker only generate the GNU-style hash table by default, so we can avoid wasting time to generate the classic ELF hash table when we build the packages, or wasting disk space to store it.
Continue with compiling the package:
make
Install the package:
make install
Details on this package are located in Section 8.20.2, “Contents of Binutils.”
5.3.1. Installation of Cross GCC
GCC requires the GMP, MPFR and MPC packages. As these packages may not be included in your host distribution, they will be built with GCC. Unpack each package into the GCC source directory and rename the resulting directories so the GCC build procedures will automatically use them:
Note
There are frequent misunderstandings about this chapter. The procedures are the same as every other chapter, as explained earlier (Package build instructions). First, extract the gcc-15.2.0 tarball from the sources directory, and then change to the directory created. Only then should you proceed with the instructions below.
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
On x86_64 hosts, set the default directory name for 64-bit libraries to “lib”:
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
Note
This example demonstrates the use of the -i.orig switch. It makes the sed copy the t-linux64 file to t-linux64.orig , and then edit the original t-linux64 file inplace. So you may run diff -u gcc/config/i386/t-linux64{.orig,} to visualize the change done by the sed command afterwards. We'll simply use -i (which just edits the original file inplace without copying it) for all other packages in the book, but you can change it to -i.orig in any case you want to keep a copy of the original file.
The GCC documentation recommends building GCC in a dedicated build directory:
Prepare GCC for compilation:
../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++
The meaning of the configure options:
--with-glibc-version=2.42
This option specifies the version of Glibc which will be used on the target. It is not relevant to the libc of the host distro because everything compiled by pass1 GCC will run in the chroot environment, which is isolated from libc of the host distro.
--with-newlib
Since a working C library is not yet available, this ensures that the inhibit_libc constant is defined when building libgcc. This prevents the compiling of any code that requires libc support.
--without-headers
When creating a complete cross-compiler, GCC requires standard headers compatible with the target system. For our purposes these headers will not be needed. This switch prevents GCC from looking for them.
--enable-default-pie and --enable-default-ssp
Those switches allow GCC to compile programs with some hardening security features (more information on those in the note on PIE and SSP in chapter 8) by default. They are not strictly needed at this stage, since the compiler will only produce temporary executables. But it is cleaner to have the temporary packages be as close as possible to the final ones.
--disable-shared
This switch forces GCC to link its internal libraries statically. We need this because the shared libraries require Glibc, which is not yet installed on the target system.
--disable-multilib
On x86_64, LFS does not support a multilib configuration. This switch is harmless for x86.
--disable-threads,
--disable-libatomic,
--disable-libgomp,
--disable-libquadmath,
--disable-libssp,
--disable-libvtv,
--disable-libstdcxx
These switches disable support for threading, libatomic, libgomp, libquadmath, libssp, libvtv, and the C++ standard library respectively. These features may fail to compile when building a cross-compiler and are not necessary for the task of cross-compiling the temporary libc.
--enable-languages=c,c++
This option ensures that only the C and C++ compilers are built. These are the only languages needed now.
Compile GCC by running:
make
Install the package:
make install
This build of GCC has installed a couple of internal system headers. Normally one of them, limits.h , would in turn include the corresponding system limits.h header, in this case, $LFS/usr/include/limits.h . However, at the time of this build of GCC $LFS/usr/include/limits.h does not exist, so the internal header that has just been installed is a partial, self-contained file and does not include the extended features of the system header. This is adequate for building Glibc, but the full internal header will be needed later. Create a full version of the internal header using a command that is identical to what the GCC build system does in normal circumstances:
Note
The command below shows an example of nested command substitution using two methods: backquotes and a $() construct. It could be rewritten using the same method for both substitutions, but is shown this way to demonstrate how they can be mixed. Generally the $() method is preferred.
cd ..
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
`dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include/limits.h
Details on this package are located in Section 8.29.2, “Contents of GCC.”
5.4.1. Installation of Linux API Headers
The Linux kernel needs to expose an Application Programming Interface (API) for the system's C library (Glibc in LFS) to use. This is done by way of sanitizing various C header files that are shipped in the Linux kernel source tarball.
Make sure there are no stale files embedded in the package:
make mrproper
Now extract the user-visible kernel headers from the source. The recommended make target “headers_install” cannot be used, because it requires rsync, which may not be available. The headers are first placed in ./usr , then copied to the needed location.
make headers
find usr/include -type f ! -name '*.h' -delete
cp -rv usr/include $LFS/usr
5.4.2. Contents of Linux API Headers
Installed headers:
/usr/include/asm/*.h,
/usr/include/asm-generic/*.h,
/usr/include/drm/*.h,
/usr/include/ linux/*.h,
/usr/include/misc/*.h, /usr/include/mtd/*.h,
/usr/include/rdma/*.h,
/usr/ include/scsi/*.h,
/usr/include/sound/*.h,
/usr/include/video/*.h, and
/usr/include/xen/*.h
Installed directories:
/usr/include/asm,
/usr/include/asm-generic,
/usr/include/drm,
/usr/include/linux,
/usr/include/misc,
/usr/include/mtd,
/usr/include/rdma,
/usr/include/scsi,
/usr/include/sound,
/usr/include/video, and
/usr/include/xen
Short Descriptions
/usr/include/asm/*.h The Linux API ASM Headers
/usr/include/asm-generic/*.h The Linux API ASM Generic Headers
/usr/include/drm/*.h The Linux API DRM Headers
/usr/include/linux/*.h The Linux API Linux Headers
/usr/include/misc/*.h The Linux API Miscellaneous Headers
/usr/include/mtd/*.h The Linux API MTD Headers /usr/include/rdma/*.h
The Linux API RDMA Headers /usr/include/scsi/*.h The Linux API SCSI Headers
/usr/include/sound/*.h The Linux API Sound Headers
/usr/include/video/*.h The Linux API Video Headers
/usr/include/xen/*.h The Linux API Xen Headers
5.5.1. Installation of Glibc
First, create a symbolic link for LSB compliance. Additionally, for x86_64, create a compatibility symbolic link required
for proper operation of the dynamic library loader:
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
Note
The above command is correct. The ln command has several syntactic versions, so be sure to check info coreutils ln and ln(1) before reporting what may appear to be an error.
Some of the Glibc programs use the non-FHS-compliant /var/db directory to store their runtime data. Apply the following patch to make such programs store their runtime data in the FHS-compliant locations:
patch -Np1 -i ../glibc-2.42-fhs-1.patch
The Glibc documentation recommends building Glibc in a dedicated build directory:
Ensure that the ldconfig and sln utilities are installed into /usr/sbin :
echo "rootsbindir=/usr/sbin" > configparms
Next, prepare Glibc for compilation:
../configure \
--prefix=/usr \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--disable-nscd \
libc_cv_slibdir=/usr/lib \
--enable-kernel=5.4
The meaning of the configure options:--host=$LFS_TGT, --build=$(../scripts/config.guess) The combined effect of these switches is that Glibc's build system configures itself to be cross-compiled, using the cross-linker and cross-compiler in $LFS/tools .
--enable-kernel=5.4
This tells Glibc to compile the library with support for 5.4 and later Linux kernels. Workarounds for older kernels are not enabled.
libc_cv_slibdir=/usr/lib This ensures that the library is installed in /usr/lib instead of the default /lib64 on 64-bit machines.
--disable-nscd
Do not build the name service cache daemon which is no longer used.
During this stage the following warning might appear:
configure: WARNING:
*** These auxiliary programs are missing or
*** incompatible versions: msgfmt
*** some features will be disabled.
*** Check the INSTALL file for required versions.
The missing or incompatible msgfmt program is generally harmless. This msgfmt program is part of the Gettext package, which the host distribution should provide.
Note
There have been reports that this package may fail when building as a “parallel make.” If that occurs, rerun the make command with the -j1 option.
Compile the package:
make
Install the package:
Warning
If LFS is not properly set, and despite the recommendations, you are building as root , the next command will install the newly built Glibc to your host system, which will almost certainly render it unusable. So double- check that the environment is correctly set, and that you are not root , before running the following command.
make DESTDIR=$LFS install
The meaning of the make install option:DESTDIR=$LFS The DESTDIR make variable is used by almost all packages to define the location where the package should be installed. If it is not set, it defaults to the root ( / ) directory. Here we specify that the package is installed in $LFS , which will become the root directory in Section 7.4, “Entering the Chroot Environment.”
Fix a hard coded path to the executable loader in the ldd script:
sed '/RTLDLIST=/s@/usr@@g' -i $LFS/usr/bin/ldd
Now that our cross toolchain is in place, it is important to ensure that compiling and linking will work as expected.
We do this by performing some sanity checks:
echo 'int main(){}' | $LFS_TGT-gcc -x c - -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'
There should be no errors, and the output of the last command will be (allowing for platform-specific differences in the dynamic linker name):
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
Note that this path should not contain /mnt/lfs (or the value of the LFS variable if you used a different one). The path is resolved when the compiled program is executed, and that should only happen after we enter the chroot environment where the kernel would consider $LFS as the root directory ( / ).
Now make sure that we're set up to use the correct start files:
grep -E -o "$LFS/lib.*/S?crt[1in].*succeeded" dummy.log
The output of the last command should be:
/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
This command should return the following 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
Again, the directory named after your target triplet may be different than the above, depending on your system architecture.
Next, verify that the new linker is being used with the correct search paths:
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
References to paths that have components with '-linux-gnu' should be ignored, but otherwise the output of the last command should be:
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");
A 32-bit system may use a few other directories, but anyway the important facet here is all the paths should begin with an equal sign ( = ), which would be replaced with the sysroot directory that we've configured for the linker.
Next make sure that we're using the correct libc:
grep "/lib.*/libc.so.6 " dummy.log
The output of the last command should be:
attempt to open /mnt/lfs/usr/lib/libc.so.6 succeeded
Make sure GCC is using the correct dynamic linker:
grep found dummy.log
The output of the last command should be (allowing for platform-specific differences in dynamic linker name):
found ld-linux-x86-64.so.2 at /mnt/lfs/usr/lib/ld-linux-x86-64.so.2
If the output does not appear as shown above or is not received at all, then something is seriously wrong. Investigate and retrace the steps to find out where the problem is and correct it. Any issues should be resolved before continuing with the process.
Once everything is working correctly, clean up the test files:
rm -v a.out dummy.log
Note
Building the packages in the next chapter will serve as an additional check that the toolchain has been built properly. If some package, especially Binutils-pass2 or GCC-pass2, fails to build, it is an indication that something has gone wrong with the preceding Binutils, GCC, or Glibc installations.
Details on this package are located in Section 8.5.3, “Contents of Glibc.”
5.6.1. Installation of Target Libstdc++ Note
Libstdc++ is part of the GCC sources. You should first unpack the GCC tarball and change to the gcc-15.2.0 directory.
Create a separate build directory for Libstdc++ and enter it:
Prepare Libstdc++ for compilation:
../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
The meaning of the configure options:
--host=...
Specifies that the cross-compiler we have just built should be used instead of the one in /usr/bin .
--disable-libstdcxx-pch
This switch prevents the installation of precompiled include files, which are not needed at this stage.
--with-gxx-include-dir=/tools/$LFS_TGT/include/c++/15.2.0
This specifies the installation directory for include files. Because Libstdc++ is the standard C++ library for LFS, this directory should match the location where the C++ compiler ( $LFS_TGT-g++ ) would search for the standard C++ include files. In a normal build, this information is automatically passed to the Libstdc++ configure options from the top level directory. In our case, this information must be explicitly given. The C++ compiler will prepend the sysroot path $LFS (specified when building GCC-pass1) to the include file search path, so it will actually search in $LFS/tools/$LFS_TGT/include/c++/15.2.0 . The combination of the DESTDIR variable (in the make install command below) and this switch causes the headers to be installed there.
Compile Libstdc++ by running:
make
Install the library:
make DESTDIR=$LFS install
Remove the libtool archive files because they are harmful for cross-compilation:
rm -v $LFS/usr/lib/lib{stdc++{,exp,fs},supc++}.la
Details on this package are located in Section 8.29.2, “Contents of GCC.”