Jordan Savant // Software Engineer

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