Linux From Scratch Version 10.0-systemdに挑戦する

概要

今回はLinux From Scratch(LFS)に挑戦します。経緯としては、Linuxディストリビューションはカーネル以外にどのようなソフトウェアから構成されているのか、より直接的に深く知りたかったからです。

基本LFSブック 日本語版 10.0-systemd の手順に沿ってやっているだけですが、実行したコマンドやオプションの意味や、必要となるパッケージと一つずつ向き合って、自分の言葉で噛み砕いてまとめたことを追記しています。しかし現時点では、実力不足でconfigureスクリプトのオプションの説明までは噛み砕けそうにないです。

今回は、クロスコンパイル&chroot環境への移行までやります。

Linux From Scratchとは?

Linux From Scratch(LFS)とは、Linux環境を一から自分自身で構築していくという、一種のディストリビューションです。

具体的には、Linux From Scratchの公式ブックの手順を参考にしながら、全ての必要なソフトウェアを一つずつソースコードからビルドして構築していきます。

一般的なLinuxディストリビューションは、グラフィックなインストーラが起動して、ポチポチとクリックして、待つだけでインストールが完了します。それらに比べるとLFSはとても手間がかかりますが、もっとLinuxの構成が知りたいと思っている人にはもってこいのディストリビューションです。

環境

  • ホストOS(macOS Catalina 10.15.7)
  • VirtualBox 6.1.16
  • 仮想マシン(CentOS8)
  • LFS 10.0-systemd

事前準備

仮想マシンの用意

LFSは、既にインストールされているLinuxディストリビューションの力を借りて構築していくので、ここではCentOS8が入った仮想マシンを用意します。

仮想マシンのスペックは以下の通りです。

  • CentOS8
  • メモリ 4GB
  • CPU 2コア
  • ハードディスク 30GB

仮想ハードディスクの用意

LFSを構築する用の仮想ハードディスクを新しく追加して、仮想マシンに割り当てます。

公式ブックによると、容量は30GBもあれば足りるそうですが、仮想ハードディスクということもあり適当に50GBにしました。

必須パッケージのインストール

LFSを構築していくにあたって、ホストマシンに必要なパッケージ群がインストールされているか、スクリプトを用いて確認してみます。

# bash version-check.sh 
---------not found以外省略-----------
version-check.sh: line 11: bison: command not found
yacc not found
version-check.sh: line 35: gcc: command not found
version-check.sh: line 36: g++: command not found
version-check.sh: line 41: m4: command not found
version-check.sh: line 42: make: command not found
version-check.sh: line 43: patch: command not found
version-check.sh: line 44: perl: command not found
version-check.sh: line 45: python3: command not found
version-check.sh: line 48: makeinfo: command not found

このように足りないものはパッケージ名を調べ、dnfコマンドでインストールします。

# dnf install bison byacc gcc gcc-c++ m4 make patch perl python3 
# dnf --enablerepo=PowerTools install texinfo

最後にまた同じスクリプトを実行して確認します。

# bash version-check.sh 

ここで登場したパッケージ群は後でまた出てくるので、その時詳しくまとめていこうと思います。

ビルド作業までの準備

パーティションの作成

今回のパーティション構成は、必要最低限にしています。というのも、今回の目的はインストールを通して、様々なソフトウェアを知ることであり、インストール完了後に使用しないからです。

# parted /dev/sdb
(parted) mklabel gpt
(parted) unit mib
(parted) mkpart grub 1 3
(parted) set 1 bios_grub on                                               
(parted) mkpart boot 3 203                                                
(parted) set 2 boot on
(parted) mkpart swap 203 4299
(parted) mkpart root 4299 -1                                           
(parted) print                                                            
---------(省略)----------
番号  開始     終了      サイズ    ファイルシステム  名前  フラグ
 1    1.00MiB  3.00MiB   2.00MiB                     grub  bios_grub
 2    3.00MiB  203MiB    200MiB                      boot  boot, esp
 3    203MiB   4299MiB   4096MiB                     swap
 4    4299MiB  51404MiB  47105MiB                    root
(parted) quit                                            

ファイルシステムの作成&マウント

UEFIブートにするので、/bootとなる/dev/sdb2はFAT32で作成し、あとは無難にext4で作成します。

# mkfs.fat -F 32 /dev/sdb2
# mkfs.ext4 /dev/sdb4

スワップ領域を作成&有効化します。

# mkswap -L swap /dev/sdb3
# swapon LABEL=swap

ルートパーティション(sdb4)のマウントポイントは/mnt/lfsとするので、今後のコマンドのディレクトリ指定で楽をするために環境変数に設定しておきます。

# export LFS=/mnt/lfs

マウントポイントを作成し、ルートパーティションをマウントします。

# mkdir -pv $LFS
# mount /dev/sdb4 $LFS

パッケージのダウンロード

LFS構築に必要なパッケージのダウンロード先アドレスがwget-listとしてまとめられているので、これをまずはダウンロードします。

# wget http://lfsbookja.osdn.jp/10.0-sysdja/wget-list

今のままだと、日本のミラーサイトが指定されていないので、ダウンロードに時間がかかる可能性があります。このwget-listをsedコマンドで編集するために、wl.sedというファイルを作成します。

# cat > wl.sed << "EOF"
s|ftp\.gnu\.org/gnu/|ftp.riken.jp/GNU/|g
s|https://www\.kernel\.org/pub/linux/|http://ftp.riken.jp/Linux/kernel.org/linux/|g
s|www\.cpan\.org|ftp.riken.jp/lang/CPAN|g
s|ftp\.vim\.org|ftp.jp.vim.org|g
EOF

ここにはsedコマンドで置換する箇所を一行ずつ記述しています。このファイルをsedコマンドの-fオプションで指定することで、ファイルを読み込んで一発で編集することができます。

# sed -f wl.sed -i.orig wget-list
# rm wl.sed 

これで日本のミラーサイトを見に行くようになったので、wgetでダウンロードします。

# wget --input-file=wget-list --continue --directory-prefix=$LFS/sources

ダウンロードされたものが本物であるかどうか、md5sumによるチェックサムを行います。

# wget -P $LFS/sources http://lfsbookja.osdn.jp/10.0-sysdja/md5sums

md5sumコマンドは、-cオプション(–check)で入手したチェックサムファイルを指定しています。

# pushd $LFS/sources
# md5sum -c md5sums
# popd

pushdコマンドは指定したディレクトリを保存&移動し、popdコマンドで保存したディレクトリに戻ってきます。cdコマンドよりもスマートに作業を行えます。

作業用ユーザの追加

rootユーザのままだとなんでもできてしまい、環境を破壊してまう可能性が少しでもあるので、作業用のlfsユーザを作成します。

# groupadd lfs
# useradd -s /bin/bash -g lfs -m -k /dev/null lfs

useraddコマンドのオプションの説明は以下の通りです。

オプション説明
-s /bin/bashデフォルトシェルを/bin/bashに指定
-g lfsプライマリグループをlfsに指定
-mホームディレクトリを作成(CentOSは指定なしでも作成される)
-k /dev/null/etc/skelディレクトリ内の設定ファイルをホームディレクトリにコピーしない。

/etc/skelは、ホームディレクトリの作成時に、自動的に配布したい基本的な設定ファイル等が置かれたディレクトリです。CentOS8のデフォルトでは以下の設定ファイルが配置されています。

# ls -la /etc/skel
drwxr-xr-x.  2 root root   62 12月  8 12:31 .
drwxr-xr-x. 97 root root 8192 12月 15 21:58 ..
-rw-r--r--.  1 root root   18 11月  9  2019 .bash_logout
-rw-r--r--.  1 root root  141 11月  9  2019 .bash_profile
-rw-r--r--.  1 root root  312 11月  9  2019 .bashrc

今回/etc/skelを利用しないのは、この後に自分でbash_profile等の設定ファイルを作成する&利用しない無駄な設定ファイルをコピーしたくないからではないかと推測します。

次にlfsユーザのパスワードを設定します。

# passwd lfs

/mnt/lfs以下に新環境で必要なディレクトリを作成し、lfsユーザ自身がアクセスできるように、所有ユーザ名を変更します。chownコマンドの-Rオプションで、再帰的に一度で変更します。

# mkdir -pv $LFS/{bin,etc,lib,lib64,sbin,usr,var,tools}
# chown -R lfs $LFS

ここからはlfsユーザにログインして作業を行います。

# su - lfs

環境設定

ここでは、lfsユーザのシェル環境を設定していきます。lfsユーザがログインした時は、/etc/profile、~/.bash_profileという順番で設定が適用されます。

/etc/profileの方は、CentOS8マシン上の全てのユーザがログインしたときに適用されるファイルで、lfsユーザ独自の設定をしたい場合は、~/.bash_profileを編集します。

では以下のようにヒアドキュメントを使用して、ファイルを作成&編集します。

$ cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF

execコマンドは引数に指定したenvコマンドを実行します。envコマンドの-iオプションは、何も環境変数が設定されていない状態にします。環境変数の意味は以下の通りです。

環境変数説明
HOMEユーザのホームディレクトリのパス
TERMユーザが使用している端末の種類
PS1プロンプトの表示フォーマット
\u … ログイン中のユーザ名を表示
\w … ホームディレクトリを基準にカレントディレクトリを表示
\$ … 一般ユーザは$、rootユーザは#を表示

ログインシェルではない、対話型シェルを起動させ、ユーザ独自の設定を適用させたい場合は、~/.bashrcを編集します。コメントで説明を加えます。

$ cat > ~/.bashrc << "EOF"       #ヒアドキュメント開始
# シェルのハッシュ機能を無効。
set +h

# umask値を022に指定(デフォルトでファイルは0644、ディレクトリは0755になる)。
umask 022

LFS=/mnt/lfs

# ロケールの全てのカテゴリでPOSIX(英語)を指定。
LC_ALL=POSIX

# lfsを構築するホスト名を指定(CentOS8が入っているホストではない)。
LFS_TGT=$(uname -m)-lfs-linux-gnu

PATH=/usr/bin

# /binがシンボリックリンクファイルではなかったら真。then以下を実行。
if [ ! -L /bin ]; then PATH=/bin:$PATH; fi

PATH=$LFS/tools/bin:$PATH

exportコマンドでシェル変数から環境変数にエクスポート
export LFS LC_ALL LFS_TGT PATH

EOF   #ヒアドキュメント終了

$ source ~/.bash_profile    #.bash_profileを読み込んで設定を反映。

ビルド時間を削減させるために同時並行で三つのプロセスを実行させます(CPUコア数+1)。この値を増やしすぎると、ビルドに失敗することがあるので、その時は数を減らします。

$ export MAKEFLAGS='-j3'

クロスコンパイラ&関連ツールのビルド

クロスコンパイラとは?

クロスコンパイラとは、現在コンパイラが動作しているマシンとは別のマシン向けに、実行ファイルを生成するコンパイラのことです。逆に同一のマシン上向けの実行ファイルを生成することしかできないコンパイラのことを、ネイティブコンパイラと呼びます。

最終的にはLFSマシンをCentOS8マシンと切り離しても、独立してコンパイルなどの動作が行えるように、LFSマシン上にネイティブコンパイラがあることが必須になります。

LFSマシン上にネイティブコンパイラをビルドしていく手順は以下の通りです。

  1. CentOS8マシン上のネイティブコンパイラで、CentOS8マシン上にクロスコンパイラをビルドする。
  2. CentOS8マシン上のクロスコンパイラで、LFSマシン上にネイティブコンパイラをビルドする。

ここではまず手順1の、CentOS8マシン上にクロスコンパイラをビルドしていきます。

ビルド実施前後のコマンド

これからいろんなソフトウェアをビルドしていく際は、ビルド前後に以下のコマンドを毎回実行します。毎回書くのは面倒なので、ここに書いて省略することにします。

まずビルド前はソースコードをダウンロードしたディレクトリに移動し、展開&パッケージに移動します。

$ cd $LFS/sources
$ tar -xvf <パッケージ名>.tar.xz
$ cd <パッケージ名>

各パッケージのビルド手順が終えたら、以下のコマンドで$LFS/sourcesディレクトリに戻り、展開したパッケージを削除します。

$ cd ..   or   cd ../..
$ rm -rf <パッケージ名>

Binutils-2.35

Binutils – GNU Project – Free Software Foundation

Binutilsは、リンカ、アセンブラ等のオブジェクトファイルを扱うツール類を提供するパッケージです(ldコマンド、asコマンド等)。コンパイラが動作するために必要なツール類が格納されています。

オブジェクトファイルは、ソースコードと実行ファイルの中間に位置する機械語のデータのことです。asコマンドによってオブジェクトファイルが生成され、そこからldコマンドによって、様々なオブジェクトファイルが組み合わさり、実行ファイルになります。

Binutilsは新しくbuildディレクトリを作成し、そこでビルドを行う流儀なので従うことにします。

$ mkdir build
$ cd build
$ time {
../configure --prefix=$LFS/tools \
             --with-sysroot=$LFS \
             --target=$LFS_TGT \
             --disable-nls \
             --disable-werror \
&& make && make install
}

---------(省略)----------
real	2m13.761s
user	1m42.688s
sys	0m31.194s

ここではビルドにどれくらい時間がかかるか把握するために、SBU(Standard Build Unit)値を測定しています。このBinutilsパッケージのビルドにかかった時間を1SBUとして扱うようです。

GCC-10.2.0

GCC, the GNU Compiler Collection

GCC(GNU Compiler Collection)はGNUプロジェクトが開発&配布している、様々なプログラミング言語用のコンパイラを集めたものです。今回のLFS構築にはCコンパイラとC++コンパイラのみ使用するため、configureスクリプトのオプションに–enable-languagesでその二つのみを指定しています。

このGCCをビルドするためには、まずMPFR、GMP、MPCパッケージをビルドしておかなくてはいけません。MPFRは倍精度演算のための関数、GMPは任意精度演算のための関数、MPCは複素数演算のための関数を提供するようです。つまりはGCCが必要とする数値演算用の関数を提供してくれるということでしょうか。GCCをビルドする際に自動的にこれらをビルドしてくれるので、発見できるように展開&名前変更します。

$ tar -xvf ../mpfr-4.1.0.tar.xz
$ mv mpfr-4.1.0 mpfr
$ tar -xvf ../gmp-6.2.0.tar.xz
$ mv gmp-6.2.0 gmp
$ tar -xvf ../mpc-1.1.0.tar.gz
$ mv mpc-1.1.0 mpc

ビルド用ディレクトリを作成してそこでビルドを行います。

$ mkdir build
$ cd build
$ ../configure \
    --target=$LFS_TGT \
    --prefix=$LFS/tool \
    --with-glibc-version=2.11 \
    --with-sysroot=$LFS \
    --with-newlib \
    --without-headers \
    --enable-initfini-array \
    --disable-nls \
    --disable-shared \
    --disable-decimal-float \
    --disable-threads \
    --disable-libatomic \
    --disable-libgomp \
    --disable-libquadmath \
    --disable-libssp \
    --disable-libvtv \
    --disable-libstdcxx \
    --enable-languages=c,c++
$ make
$ make install

今のままだとシステムヘッダファイルであるlimits.hは、$LFS/usr/include/limits.hという存在しないファイルとなっているので、以下のコマンドでパスを変更します。

$ cd ..
$ cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
 `dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/install-tools/include/limits.h

Linux-5.8.3 API ヘッダ

The Linux Kernel Archives

Linuxの標準Cライブラリであるglibcが、カーネルAPIを利用してカーネルと連携するために、カーネルヘッダファイルをビルドする必要があります。

まずmakeコマンドのターゲットにmrproperを指定して、.configやバックアップファイルなどの不要なファイルを削除してきれいな状態にします。

$ make mrproper

その後、カーネルヘッダファイルのみを抽出してコンパイルし、適切なディレクトリに配置します。

$ make headers
$ find usr/include -name '.*' -delete
$ rm usr/include/Makefile
$ cp -rv usr/include $LFS/usr

Glibc-2.32

Glibc – The GNU C Library

glibcはLinuxの標準Cライブラリで、Linux上の多くのプログラムで繰り返し使用される関数を集めたものです。Linux上で動作するプログラムにとって必要不可欠なものとなります。

リンクを行うローダが、LFSマシン上でも適切に動作するように、lnコマンドでシンボリックリンクを張ります。

$ case $(uname -m) in
    i?86)   ln -sfv ld-linux.so.2 $LFS/lib/ld-lsb.so.3
    ;;
    # x86_64アーキテクチャなのでこれが実行される。
    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

公式ブックが用意したパッチをあてます。

$ patch -Np1 -i ../glibc-2.32-fhs-1.patch

ビルド用ディレクトリを作成し、そこでビルドしていきます。

$ mkdir build
$ cd build
$ ../configure \
      --prefix=/usr \
      --host=$LFS_TGT \ 
      --build=$(../scripts/config.guess) \
      --enable-kernel=3.2 \
      --with-headers=$LFS/usr/include \
      libc_cv_slibdir=/lib
$ make
$ make DESTDIR=$LFS install

dummyファイルを使用して、クロスコンパイルが行えるかどうか、ローダが適切に認識されているかどうか確認しています。

$ echo 'int main(){}' > dummy.c
$ LFS_TGT-gcc dummy.c
$ readelf -l a.out | grep '/ld-linux'
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

$ rm dummy.c a.out

そして以下のコマンドを使用することで、先ほどのlimits.hのインストールが完了し、クロスコンパイル環境が完成します。

$ LFS/tools/libexec/gcc/$LFS_TGT/10.2.0/install-tools/mkheaders

Libstdc++

libstdc++は標準C++ライブラリで、C++で書かれているGCCの一部をコンパイルするために必要です。

$ mkdir build
$ cd build
$ ../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++/10.2.0
$ make
$ make DESTDIR=$LFS install

以上でクロスコンパイラ&関連ツールの環境が整いました。

基本的なツールのクロスコンパイル

ここでは、chrootでLFSマシンを切り離しても、そのマシン自身でコンパイラを利用し、システムを構築できるようにする準備を完了させます。つまり、最低限必要なコマンド類だったり、当然ネイティブコンパイラもビルドします。

M4-1.4.18

GNU M4 – GNU Project – Free Software Foundation

m4はC言語やアセンブリ言語のプログラムの前処理を行う際に使用するもので、文字列の置換や整数演算、ファイルの取り込みなどの処理をすることができます。

glibcとの連携で必要な修正を置換や追記で編集し、ビルドします。

$ sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' lib/*.c
$ echo "#define _IO_IN_BACKUP 0x100" >> lib/stdio-impl.h
$ ./configure --prefix=/usr   \
              --host=$LFS_TGT \
              --build=$(build-aux/config.guess)
$ make
$ make DESTDIR=$LFS install

Ncurses-6.2

Announcing ncurses 6.2 – GNU.org

ncursesはキー入力や画面表示など、TUI(GUIとCUIの中間のような存在)を実現させるためのライブラリ群を提供するパッケージです。

ビルドする際に、mawkではなくgawkを見つけられるように設定します。awkというテキストを編集するプログラムがあって、mawkとgawkといった流派があります。ディストリビューションによってデフォルトで採用されているものが違うので、ここではgawkで統一しています。

$ sed -i s/mawk// configure

ビルド用ディレクトリを作成し、ticというプログラムをビルドしてから、ncursesのビルドへ移ります。

$ mkdir build
$ pushd build
$ ../configure
$ make -C include
$ make -C progs tic
$ popd
$ ./configure --prefix=/usr \               
              --host=$LFS_TGT \              
             --build=$(./config.guess) \
             --mandir=/usr/share/man \  
             --with-manpage-format=normal \
             --with-shared \
             --without-debug \              
             --without-ada \            
             --without-normal \
             --enable-widec
$ make
$ make DESTDIR=$LFS TIC_PATH=$(pwd)/build/progs/tic install

共有ライブラリを移動&存在しなくなったシンボリックリンクを直します。

$ echo "INPUT(-lncursesw)" > $LFS/usr/lib/libncurses.so
$ mv -v $LFS/usr/lib/libncursesw.so.6* $LFS/lib
$ ln -sfv ../../lib/$(readlink $LFS/usr/lib/libncursesw.so) $LFS/usr/lib/libncursesw.so

Bash-5.0

Bash – GNU Project – Free Software Foundation

多くのLinuxディストリビューションで採用されているシェルです。シェルはユーザが打ち込んだコマンドをOSに伝えるという働きをします。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --build=$(support/config.guess) \
             --host=$LFS_TGT \
             --without-bash-malloc
$ make
$ make DESTDIR=$LFS install

bashを/bin/以下に移動し、shシェルが指定されたときにbashを動作させるために、シンボリックリンクを張ります。

$ mv $LFS/usr/bin/bash $LFS/bin/bash
$ ln -sv bash $LFS/bin/sh

Coreutils-8.32

Coreutils – GNU core utilities – GNU.org

coreutilsはシステムの基本的な情報表示や設定を行うためのコマンドがたくさん格納されたパッケージです。下の手順に出てくるように、cat、date、df、mkdirコマンド等、システムの操作に必須のコマンドを使用することができるようになります。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --host=$LFS_TGT \
             --build=$(build-aux/config.guess) \
             --enable-install-program=hostname \
             --enable-no-install-program=kill,uptime
$ make
$ make DESTDIR=$LFS install
$ mv -v $LFS/usr/bin/{cat,chgrp,chmod,chown,cp,date,dd,df,echo} $LFS/bin
$ mv -v $LFS/usr/bin/{false,ln,ls,mkdir,mknod,mv,pwd,rm}        $LFS/bin
$ mv -v $LFS/usr/bin/{rmdir,stty,sync,true,uname}               $LFS/bin
$ mv -v $LFS/usr/bin/{head,nice,sleep,touch}                    $LFS/bin
$ mv -v $LFS/usr/bin/chroot                                     $LFS/usr/sbin
$ mkdir -pv $LFS/usr/share/man/man8
$ mv -v $LFS/usr/share/man/man1/chroot.1                        $LFS/usr/share/man/man8/chroot.8
$ sed -i 's/"1"/"8"/'                                           $LFS/usr/share/man/man8/chroot.8

Diffutils-3.7

Diffutils – GNU Project – Free Software Foundation

diffutilsはファイルやディレクトリの差分を表示するためのプログラムが格納されたパッケージです。パッチを生成する際に必要となります。

ビルドしていきます。

$ ./configure --prefix=/usr --host=$LFS_TGT
$ make 
$ make DESTDIR=$LFS install

File-5.39

The Fine Free File Command – Ian Darwin

fileはファイルの種類を判別するプログラムを提供するパッケージです。このファイルはテキストファイルだとかバイナリファイルだとか判別します。他のパッケージのビルドスクリプトで必要とされているようです。

ビルドしていきます。

$ tar -xvf file-5.39.tar.gz 
$ cd file-5.39
$ ./configure --prefix=/usr --host=$LFS_TGT
$ make
$ make DESTDIR=$LFS install

Findutils-4.7.0

findutils – GNU Project – Free Software Foundation (FSF)

findutilsはファイル検索を行うプログラムを提供するパッケージで、find、updatedb、locateコマンド等が使用できるようになります。他パッケージのビルドのスクリプトに必要になります。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --host=$LFS_TGT \
             --build=$(build-aux/config.guess)
$ make
$ make DESTDIR=$LFS install
$ mv -v $LFS/usr/bin/find $LFS/bin
$ sed -i 's|find:=${BINDIR}|find:=/bin|' $LFS/usr/bin/updatedb

Gawk-5.1.0

Gawk – GNU Project – Free Software Foundation (FSF)

gawkは先ほども説明しましたが、テキストファイルを編集するプログラムを提供するパッケージです。他パッケージのビルド時のスクリプトで必要になります。

$ sed -i 's/extras//' Makefile.in
$ ./configure --prefix=/usr \
             --host=$LFS_TGT \
             --build=$(./config.guess)
$ make
$ make DESTDIR=$LFS install

Grep-3.4

Grep – GNU Project – Free Software Foundation – GNU.org

grepはファイル内を検索するプログラムを提供するパッケージで、これも他パッケージのビルド時のスクリプトで必要になるものです。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --host=$LFS_TGT \
             --bindir=/bin
$ make
$ make DESTDIR=$LFS install

Gzip-1.10

Gzip – GNU Project – Free Software Foundation

gzipはファイルの圧縮&解凍等のプログラムを提供するパッケージです。

ビルドしていきます。

$ tar -xvf gzip-1.10.tar.xz 
$ cd gzip-1.10
$ ./configure --prefix=/usr --host=$LFS_TGT
$ make
$ make DESTDIR=$LFS install
$ mv -v $LFS/usr/bin/gzip $LFS/bin

Make-4.3

Make – GNU Project – Free Software Foundation – GNU.org

makeはプログラムのビルド作業を自動化するためのツールを提供するパッケージです。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --without-guile \
             --host=$LFS_TGT \
             --build=$(build-aux/config.guess)
$ make
$ make DESTDIR=$LFS install

Patch-2.7.6

GNU patch – Summary [Savannah]

patchは特定のファイルの修正&生成を行うプログラムを提供するパッケージです。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --host=$LFS_TGT \
             --build=$(build-aux/config.guess)
$ make
$ make DESTDIR=$LFS install

Sed-4.8

GNU sed – GNU Project – Free Software Foundation

sedはファイル内の文字列を置換したりなどテキストを編集するプログラムを提供するパッケージです。

ビルドしていきます。

$ ./configure --prefix=/usr \
            --host=$LFS_TGT \
            --bindir=/bin
$ make
$ make DESTDIR=$LFS install

Tar-1.32

Tar – GNU Project – Free Software Foundation – GNU.org

tarはアーカイブの作成、一覧表示等の操作を行うプログラムを提供するパッケージです。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --host=$LFS_TGT \
             --build=$(build-aux/config.guess) \
             --bindir=/bin
$ make
$ make DESTDIR=$LFS install

Xz-5.2.5

XZ Utils – The Tukaani Project

xzはファイルの圧縮&解凍等のプログラムを提供するパッケージです。

ビルドしていきます。

$ ./configure --prefix=/usr \
             --host=$LFS_TGT \
             --build=$(build-aux/config.guess) \
             --disable-static \
             --docdir=/usr/share/doc/xz-5.2.5
$ make
$ make DESTDIR=$LFS install
$ mv -v $LFS/usr/bin/{lzma,unlzma,lzcat,xz,unxz,xzcat}  $LFS/bin
$ mv -v $LFS/usr/lib/liblzma.so.*                       $LFS/lib
$ ln -svf ../../lib/$(readlink $LFS/usr/lib/liblzma.so) $LFS/usr/lib/liblzma.so

Binutils-2.35

2回目なので説明は省略しますが、ここでビルドするbinutilsはLFSマシンのネイティブコンパイラ用です。

ビルドしていきます。

$ mkdir build
$ cd build
$ ../configure \
    --prefix=/usr \
    --build=$(../config.guess) \
    --host=$LFS_TGT \
    --disable-nls \
    --enable-shared \
    --disable-werror \
    --enable-64-bit-bfd
$ make
$ make DESTDIR=$LFS install

GCC-10.2.0

2回目なので説明は省略しますが、ここでビルドするコンパイラがLFSマシンのネイティブコンパイラとなります。

ビルドしていきます。

$ tar -xvf ../mpfr-4.1.0.tar.xz
$ mv mpfr-4.1.0 mpfr
$ tar -xvf ../gmp-6.2.0.tar.xz
$ mv gmp-6.2.0 gmp
$ tar -xvf ../mpc-1.1.0.tar.gz
$ mv mpc-1.1.0 mpc
$ case $(uname -m) in
  x86_64)
    sed -e '/m64=/s/lib64/lib/' -i.orig gcc/config/i386/t-linux64
  ;;
esac
$ mkdir build
$ cd build
$ ../configure \
     --build=$(../config.guess) \
     --host=$LFS_TGT \
     --prefix=/usr \
     CC_FOR_TARGET=$LFS_TGT-gcc \
     --with-build-sysroot=$LFS \
     --enable-initfini-array \
     --disable-nls \
     --disable-multilib \
     --disable-decimal-float \
     --disable-libatomic \
     --disable-libgomp \
     --disable-libquadmath \
     --disable-libssp \
     --disable-libvtv \
     --disable-libstdcxx \
     --enable-languages=c,c++
$ make
$ make DESTDIR=$LFS install
$ ln -sv gcc $LFS/usr/bin/cc

chroot環境への移行

chrootとは?

chrootとは、change rootという略で、文字通りルートディレクトリを変更する仕組みのことです。

今回の場合だと、/mnt/lfsをルートディレクトリに変更します。chrootを行うと、/mnt/lfs以下のディレクトリ以外には一切アクセスできなくなります。

こうすることで、CentOS8マシン上のプログラムと切り離すことができ、LFSマシン上のプログラムを独立して動かすことができるようになります。つまり、LFSマシン自身でシステムを安全に構築することができます。

しかし切り離すといっても、CentOS8のカーネルの機能は使用しないと動作することができません。なので、/procや/sysなどのカーネルとやり取りするための仮想ファイルシステムを、LFSマシン上のディレクトリにマウントしないといけません。

chroot環境の構築

ではchrootへ移行するための準備をしていきます。

現在のLFSマシン上のディレクトリの所有権は、CentOS8上のlfsユーザとなっています。それだと、いざLFSマシンを切り離した時、LFSマシンに存在しないユーザとなるので、rootに変更しておきます。

# chown -R root.root $LFS

CentOS8マシン上のカーネルと仮想的なファイルシステムを通してやりとりするので、必要なディレクトリを作成します。

# mkdir -pv $LFS/{dev,proc,sys,run}

次にmknodコマンドを用いて特殊なファイル(デバイスファイル)を作成します。カーネルがLFSマシンを起動させるために必要なファイルです。

# mknod -m 600 $LFS/dev/console c 5 1
# mknod -m 666 $LFS/dev/null c 1 3
  • /dev/console … アクセス権600、キャラクタデバイス、メジャー番号5、マイナー番号1
  • /dev/null … アクセス権666、キャラクタデバイス、メジャー番号1、マイナー番号3

以下仮想ファイルシステムをマウントしていきます。

# mount -v --bind /dev $LFS/dev
# mount -v --bind /dev/pts $LFS/dev/pts
# mount -vt proc proc $LFS/proc
# mount -vt sysfs sysfs $LFS/sys
# mount -vt tmpfs tmpfs $LFS/run

ではchrootを以下の環境変数の設定と一緒に行います。CentOS8マシン上の余計な環境変数に影響されように初期化して設定&chroot環境であることをわかりやすくしています。

# chroot "$LFS" /usr/bin/env -i \
    HOME=/root \
    TERM="$TERM" \
    PS1='(lfs chroot) \u:\w\$ ' \
    PATH=/bin:/usr/bin:/sbin:/usr/sbin \
    /bin/bash --login +h

FHSを考慮しながら、これからシステム構築するにあたって必要なディレクトリ群を一気に作成します。

# mkdir -pv /{boot,home,mnt,opt,srv}
# mkdir -pv /etc/{opt,sysconfig}
# mkdir -pv /lib/firmware
# mkdir -pv /media/{floppy,cdrom}
# mkdir -pv /usr/{,local/}{bin,include,lib,sbin,src}
# mkdir -pv /usr/{,local/}share/{color,dict,doc,info,locale,man}
# mkdir -pv /usr/{,local/}share/{misc,terminfo,zoneinfo}
# mkdir -pv /usr/{,local/}share/man/man{1..8}
# mkdir -pv /var/{cache,local,log,mail,opt,spool}
# mkdir -pv /var/lib/{color,misc,locate}

シンボリックリンクを張ったり、installコマンドでアクセス権を設定しながらディレクトリの作成やファイルのコピーを行います。

# ln -sfv /run /var/run
# ln -sfv /run/lock /var/lock
# install -dv -m 0750 /root
# install -dv -m 1777 /tmp /var/tmp

設定ファイルの作成

システムに重要な設定ファイルを作成していきます。

現在のディレクトリのマウント状況が記載される/etc/mtabが機能するように、システムの様々な情報が格納される/proc以下のディレクトリへのシンボリックリンクを張ります。

# ln -sv /proc/self/mounts /etc/mtab

これから/etc/hostsを参照するプログラムが出てくるということで、作成しておきます。

# echo "127.0.0.1 localhost $(hostname)" > /etc/hosts

ユーザ、グループが機能するように、/etc/passwdファイルと/etc/groupファイルを以下の内容で作成します。

# cat > /etc/passwd << "EOF"
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/dev/null:/bin/false
daemon:x:6:6:Daemon User:/dev/null:/bin/false
messagebus:x:18:18:D-Bus Message Daemon User:/var/run/dbus:/bin/false
systemd-bus-proxy:x:72:72:systemd Bus Proxy:/:/bin/false
systemd-journal-gateway:x:73:73:systemd Journal Gateway:/:/bin/false
systemd-journal-remote:x:74:74:systemd Journal Remote:/:/bin/false
systemd-journal-upload:x:75:75:systemd Journal Upload:/:/bin/false
systemd-network:x:76:76:systemd Network Management:/:/bin/false
systemd-resolve:x:77:77:systemd Resolver:/:/bin/false
systemd-timesync:x:78:78:systemd Time Synchronization:/:/bin/false
systemd-coredump:x:79:79:systemd Core Dumper:/:/bin/false
nobody:x:99:99:Unprivileged User:/dev/null:/bin/false
EOF
# cat > /etc/group << "EOF"
root:x:0:
bin:x:1:daemon
sys:x:2:
kmem:x:3:
tape:x:4:
tty:x:5:
daemon:x:6:
floppy:x:7:
disk:x:8:
lp:x:9:
dialout:x:10:
audio:x:11:
video:x:12:
utmp:x:13:
usb:x:14:
cdrom:x:15:
adm:x:16:
messagebus:x:18:
systemd-journal:x:23:
input:x:24:
mail:x:34:
kvm:x:61:
systemd-bus-proxy:x:72:
systemd-journal-gateway:x:73:
systemd-journal-remote:x:74:
systemd-journal-upload:x:75:
systemd-network:x:76:
systemd-resolve:x:77:
systemd-timesync:x:78:
systemd-coredump:x:79:
wheel:x:97:
nogroup:x:99:
users:x:999:
EOF

今後テストを行う際にユーザが必要になることがあるため、ここで作成しておきます。

# echo "tester:x:$(ls -n $(tty) | cut -d" " -f3):101::/home/tester:/bin/bash" >> /etc/passwd
# echo "tester:x:101:" >> /etc/group
# install -o tester -d /home/tester

新しいシェルを起動し、今までの設定が読み込まれていることを確認します。

# exec /bin/bash --login +h

システムへのログインに関するログが機能するように適切なアクセス権を設定します。

# chgrp -v utmp /var/log/lastlog
changed group of '/var/log/lastlog' from root to utmp

# chmod -v 664  /var/log/lastlog
mode of '/var/log/lastlog' changed from 0644 (rw-r--r--) to 0664 (rw-rw-r--)

# chmod -v 600  /var/log/btmp
mode of '/var/log/btmp' changed from 0644 (rw-r--r--) to 0600 (rw-------)

基本的なツールの追加ビルド

ここからはシステム構築に必要な基本的なツールをLFSマシンに追加ビルドしてきます。

Libstdc++

標準C++ライブラリです。chroot環境に入ったことで、LFSマシン上のネイティブコンパイラを使用してコンパイルをすることができるようになりました。

ビルドしていきます。

$ mkdir -v build
$ cd       build
$ ../libstdc++-v3/configure \
      CXXFLAGS="-g -O2 -D_GNU_SOURCE" \
     --prefix=/usr \
     --disable-multilib \
     --disable-nls \
     --host=$(uname -m)-lfs-linux-gnu \
     --disable-libstdcxx-pch
$ make
$ make install

Gettext-0.21

gettext – GNU Project – Free Software Foundation (FSF)

gettextは国際化と地域化に対応するためのライブラリが格納されているパッケージです。このライブラリを使用することで、その国、その地域に適した言語でメッセージを出力することができるようになります。

ビルドしていきます。

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

Bison-3.7.1

Bison – GNU Project – Free Software Foundation – GNU.org

bisonは構文解析ツールを提供するパッケージです。

ビルドしていきます。

# ./configure --prefix=/usr \
            --docdir=/usr/share/doc/bison-3.7.1
# make 
# make install

Perl-5.32.0

The Perl Programming Language – www.perl.org

perlは名前の通り、テキスト整形が強力なPerl言語を提供するパッケージです。

ビルドしていきます。

# sh Configure -des \
              -Dprefix=/usr \
              -Dvendorprefix=/usr \
              -Dprivlib=/usr/lib/perl5/5.32/core_perl \
              -Darchlib=/usr/lib/perl5/5.32/core_perl \
              -Dsitelib=/usr/lib/perl5/5.32/site_perl \
              -Dsitearch=/usr/lib/perl5/5.32/site_perl \
              -Dvendorlib=/usr/lib/perl5/5.32/vendor_perl \
              -Dvendorarch=/usr/lib/perl5/5.32/vendor_perl
# make
# make install

Python-3.8.5

Welcome to Python.org

python開発環境を提供するパッケージです。

ビルドしていきます。

# ./configure --prefix=/usr \
            --enable-shared \
            --without-ensurepip
# make
# make install

Texinfo-6.7

Texinfo – GNU Documentation System – GNU Project

texinfoはドキュメントによく使われている文章フォーマットツールを提供するパッケージです。このツールを使用すると、単一のソースコードファイルから、html形式やPDF形式など様々なファイル形式に変換することができます。

ビルドしていきます。

# ./configure --prefix=/usr
# make
# make install

Util-linux-2.36

util-linuxはlinuxの基本コマンドが格納されたパッケージです。例えば、fdisk、hwclock、mount等の基本コマンドが挙げられます。

hwclockコマンドがデータ保持に必要なディレクトリを作成してからビルドしていきます。

# mkdir -pv /var/lib/hwclock
# ./configure ADJTIME_PATH=/var/lib/hwclock/adjtime \
            --docdir=/usr/share/doc/util-linux-2.36 \
            --disable-chfn-chsh \
            --disable-login \
            --disable-nologin \
            --disable-su \
            --disable-setpriv \
            --disable-runuser \
            --disable-pylibmount \
            --disable-static \
            --without-python
# make
# make install

クリーンアップ&バックアップ

不要なファイルを削除しておきます。

# find /usr/{lib,libexec} -name \*.la -delete
# rm -rf /usr/share/{info,man,doc}/*

前半はここまでにするので、いったんchroot環境を抜けます。

# exit
# umount $LFS/dev{/pts,}
# umount $LFS/{sys,proc,run}

stripコマンドでデバッグ用のデータを削除して、その分実行ファイルのサイズを小さくします。

# strip --strip-debug $LFS/usr/lib/*
# strip --strip-unneeded $LFS/usr/{,s}bin/*
# strip --strip-unneeded $LFS/tools/bin/*

最後にバックアップをとって、今後の作業で環境を破壊してしまっても大丈夫なようにします。

# cd $LFS
# tar -cJpf $HOME/lfs-temp-tools-10.0-systemd.tar.xz .

最後に

ここまでですでに数多くのカーネル以外のソフトウェアが登場してきましたが、正直それがどういうものなのか理解が浅いところがあります。

例えば、m4のようなマクロプロセッサって何するの?とかbisonの構文分析って何しているの?とかです。

掘り下げていくのはきりがないとも思いますが、いずれは理解したいなと思います。

Comments

Copied title and URL