Linux Kernel Dev Blog 8 - Linux From Scratch (LFS) Compiling
4/7/2020
determine target triplet and dynamic linker
Before continuing, be aware of the name of the working platform, often referred to as the target triplet.
Unpack the bin utils and run the config.guess
script
$ tar -xf binutils-2.34.tar.xz
$ bash binutils-2.34/config.guess
x86_64-pc-linux-gnu
x86_64-pc-linux-gnu
is target triplet (which is different than our LFS_TGT which is x86_64-lfs-linux-gnu
for some reason?)
We also need to determine the Dynamic Linker used by host Glibc that finds and loads shared libraries of a program
A sure-fire way to determine the name of the dynamic linker is to inspect a random binary from the host system by running: readelf -l
| grep interpreter and noting the output
ELF - format of Executable and Linking Format (ELF) files
An executable file using the ELF file format consists of an ELF header, followed by a program header table or a section header table, or both.
With readelf
we can look at a compiled library to determine the dynamic linker used
$ readelf -a /usr/bin/x86_64-linux-gnu-cpp-7 | grep interpreter
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
Looks like the dynamic linker is /lib64/ld-linux-x86-64.so.2
?
cross compiling
The act of compiling code on one host for another target architecture
We will adjust the source of gcc to cross compile to our target architecture (which is the same as our current architecture?)
Double checking requirements:
- lfs is the user
- source is in /mnt/lfs/source
- bash is the shell in use
- /bin/sh points to bash
- /usr/bin/awk is a symbolic link to gawk
- /usr/bin/yacc is a symbolic link to bison
For each package:
- Using the tar program, extract the package to be built. In Chapter 5, ensure you are the lfs user when extracting the package.
- Change to the directory created when the package was extracted.
- Follow the book's instructions for building the package.
- Change back to the sources directory.
- Delete the extracted source directory unless instructed otherwise.
Installation of Cross Binutils
$ tar -xf binutils-2.34.tar.xz
$ cd binutils-2.34
$ mkdir build
$ cd build
$ ../configure --prefix=/tools \
--with-sysroot=$LFS \
--with-lib-path=/tools/lib \
--target=$LFS_TGT \
--disable-nls \
--disable-werror
$ time { make -j2; } # this will create our SBU to estimate build times
It took 2m26.212s
to build the binutils
If building on x86_64, create a symlink to ensure the sanity of the toolchain
$ mkdir -v /tools/lib && ln -sv lib /tools/lib64
$ make -j2 install # took 3 seconds
$ ls -l /tools/
total 16
drwxr-xr-x 2 lfs lfs 4096 Apr 7 18:52 bin
drwxr-xr-x 2 lfs lfs 4096 Apr 7 18:52 lib
lrwxrwxrwx 1 lfs lfs 3 Apr 7 18:52 lib64 -> lib
drwxr-xr-x 4 lfs lfs 4096 Apr 7 18:52 share
drwxr-xr-x 4 lfs lfs 4096 Apr 7 18:52 x86_64-lfs-linux-gnu
Installation of Cross GCC
$ tar -xf gcc-9.2.0.tar.xz
$ cd gcc-9.2.0
GCC requires three other packages we downloaded: GMP, MPFR and MPC
$ 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
This command will change GCC's default linker to use the one we installed in /tools (/mnt/lfs/tools) and removes /usr/include from GCCs search path
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
output ``` 'gcc/config/linux.h' -> 'gcc/config/linux.h.orig' 'gcc/config/i386/linux.h' -> 'gcc/config/i386/linux.h.orig' 'gcc/config/i386/linux64.h' -> 'gcc/config/i386/linux64.h.orig'
For x86_64 we need to change the 64 bit libs to "lib"
```shell
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
HOLD ON TO YOUR BUTTS
$ mkdir -v build
$ cd build
$ ../configure \
--target=$LFS_TGT \
--prefix=/tools \
--with-glibc-version=2.11 \
--with-sysroot=$LFS \
--with-newlib \
--without-headers \
--with-local-prefix=/tools \
--with-native-system-header-dir=/tools/include \
--disable-nls \
# make sure we dont link to host libs
--disable-shared \
--disable-multilib \
# most of this stuff wont cross compile and we dont need it
--disable-decimal-float \
--disable-threads \
--disable-libatomic \
--disable-libgomp \
--disable-libquadmath \
--disable-libssp \
--disable-libvtv \
--disable-libstdcxx \
--enable-languages=c,c++
$ make -j2 # eta 20-30 minutes
$ make install
linux-5.5.3
To expose Application Programming Interface (API) for the system's C library for the Linux Kernel
$ make mrproper
$ make headers
$ cp -rv usr/include/* /tools/include
Glibc-2.31
The Glibc package contains the main C library for allocating memory, searching directories, opening and closing files, reading and writing files, string handling, pattern matching, arithmetic, and so on
$ mkdir build; cd build
$ ../configure \
--prefix=/tools \
--host=$LFS_TGT \
--build=$(../scripts/config.guess) \
--enable-kernel=3.2 \
# make sure it uses our headers we just brought in from the linux API headers for GCC before
--with-headers=/tools/include
$ make -j2
$ make install
Test our compiler and linker
$ echo 'int main(){}' > dummy.c
$ $LFS_TGT-gcc dummy.c
$ readelf -l a.out | grep ': /tools'
[Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
In good working order...
Libstdc++ from GCC-9.2.0
Its in GCC but has not been compiled yet, apparently part of GCC is written in C++
$ cd /mnt/lfs/sources/gcc-9.2.0/
$ mkdir build; cd build # alread existed from previous build
$ ../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
$ make -j2
$ make install
?? I think make recognizes that now C++ is now included and configured so it builds it into the compiler too?
!! This I think was a problem, revisiting the technical notes I was supposed to delete the entire source directory after each build so on round 2 for this I should have compiled it differently
I started over and killed and rebuilt this
Binutils-2.34 - Pass 2
mkdir -v build
cd build
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
make -j2
make install
make -C ld clean
make -C ld LIB_PATH=/usr/lib:/lib
cp -v ld/ld-new /tools/bin
GCC-9.2.0 - Pass 2
in /mnt/lfs/sources/gcc-9.2.0
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
`dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include-fixed/limits.h
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
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
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
sed -e '1161 s|^|//|' \
-i libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc
mkdir -v build
cd 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
make -j2
make install
ln -sv gcc /tools/bin/cc
# test
echo 'int main(){}' > dummy.c
cc dummy.c
readelf -l a.out | grep ': /tools' # /tools/lib64/ld-linux-x86-64.so.2
rm -v dummy.c a.out