8.29.1. GCC のインストール
x86_64 上でビルドしている場合は、64ビットライブラリのデフォルトディレクトリ名を "lib"にします。
case $(uname -m) in
x86_64)
sed -e '/m64=/s/lib64/lib/' \
-i.orig gcc/config/i386/t-linux64
;;
esac
GCC のドキュメントによると GCC のビルドにあたっては、専用のビルドディレクトリを作成することが推奨されています。
mkdir -v build
cd build
GCC をコンパイルするための準備をします。
../configure --prefix=/usr \
LD=ld \
--enable-languages=c,c++ \
--enable-default-pie \
--enable-default-ssp \
--enable-host-pie \
--disable-multilib \
--disable-bootstrap \
--disable-fixincludes \
--with-system-zlib
GCC では 7
つのコンピューター言語をサポートしていますが、それらのほとんどが必要としている依存パッケージは、まだこの時点でインストールしていません。
GCC がサポートする他のコンピューター言語の構築方法については
BLFS ブック の説明を参照してください。
configure パラメーターの意味
-
LD=ld
-
本パラメーターは、本章の初期段階でビルドした Binutils の ld プログラムを使うことを configure
スクリプトに指示します。 これを指定しなかった場合は、クロスビルド版のものが用いられることになります。
-
--disable-fixincludes
-
デフォルトにおいて、GCC のインストール中に GCC が利用するシステムヘッダーが「固定される」場合があります。
これは最近の Linux システムにおいては不要なことです。 また GCC
のインストール後に何かのパッケージをインストールすることを考えると、潜在的な危険を生み出すことになります。 本スイッチは
GCC がヘッダーファイルを 「固定
(fix)」 しないようにします。
-
--with-system-zlib
-
このオプションはシステムに既にインストールされている Zlib
ライブラリをリンクすることを指示するものであり、内部にて作成されるライブラリを用いないようにします。
注記
PIE (position independent executable; 位置独立実行形式)
とは、メモリ上のどこであっても、実行プログラムをロードできるようにします。 PIE がない場合には ASLR (Address
Space Layout Randomization; アドレス空間配置のランダム化)
という技術が適用されますが、適用先は共有ライブラリのみであって実行ファイルには適用されません。
共有ライブラリに加えて実行ファイルに対しても、PIE と ASLR
を有効にすれば、実行ファイル内にある機密コードやデータが、固定的なアドレスに存在することを前提とした攻撃を軽減できます。
SSP (Stack Smashing Protection) とは、パラメータースタックが破壊されないようにする技術です。
スタック破壊が起きると、たとえばサブルーチンから返されるアドレスが変化してしまいます。
そうなった場合には、危険なコード(プログラムや共有ライブラリに元からあるものや、攻撃者が何らかの方法によって挿入したもの)に制御が移ってしまうことにもなります。
パッケージをコンパイルします。
make
重要
本節における GCC のテストスイートは極めて重要なものです。 ただし相当な時間を要します。
初めてビルドを行う方には、必ず実施することをお勧めします。 テスト実行に要する時間は、make -k check コマンドに -jx
をつけることで、かなり削減できます。 ここに示す x には、システムの CPU コア数を指定するものです。
コンパイルするコードパターンが極端に複雑な場合に GCC はより多くのスタック領域を必要とする場合があります。
ホストディストロのスタック制限が厳しいかもしれないため、それを予防する意味でスタックサイズのハード上限を無制限に設定します。
ホストシステムのほとんど (そして最終的な LFS システム)
はデフォルトでハード上限は無制限としていますが、それを明示的に設定したところで何も問題はありません。
スタックサイズのソフト上限を変更する必要はありません。 これは GCC
が自動的に設定するものであり、その値がハード上限を超えない限りは適切に設定してくれます。
ulimit -s -H unlimited
テストスイートの不備をここで削除/修正します。
sed -e '/cpython/d' -i ../gcc/testsuite/gcc.dg/plugin/plugin.exp
sed -e 's/no-pic /&-no-pie /' -i ../gcc/testsuite/gcc.target/i386/pr113689-1.c
sed -e 's/300000/(1|300000)/' -i ../libgomp/testsuite/libgomp.c-c++-common/pr109062.c
sed -e 's/{ target nonpic } //' \
-e '/GOTPCREL/d' -i ../gcc/testsuite/gcc.target/i386/fentryname3.c
一般ユーザーにてテストを行います。 ただしエラーがあっても停止しないようにします。
chown -R tester .
su tester -c "PATH=$PATH make -k check"
テスト結果を確認するために以下を実行します。
../contrib/test_summary
テスト結果の概略のみ確認したい場合は、出力結果をパイプ出力して grep -A7 Summ
を実行してください。
テスト結果については https://www.linuxfromscratch.org/lfs/build-logs/development/
と https://gcc.gnu.org/ml/gcc-testresults/
にある情報と比較することができます。
テスト失敗は回避することができません。 その中には特定のハードウェアに起因するものもあります。 上記の URL
に示されている結果と大きく異なっていなかったら、問題はありませんので先に進んでください。
パッケージをインストールします。
make install
GCC のビルドディレクトリの所有者は tester
であるため、ヘッダーがインストールされるディレクトリ(とその内容)に対する所有権が不適切です。 そこでその所有権を
root
ユーザーとグループに変更します。
chown -v -R root:root \
/usr/lib/gcc/$(gcc -dumpmachine)/14.2.0/include{,-fixed}
FHS
の求めるところに応じてシンボリックリンクを作成します。 これは慣例によるものです
ln -svr /usr/bin/cpp /usr/lib
各種パッケージは C コンパイラーとして cc を呼び出しているものが数多くあります。 GCC 2回め においては、シンボリックリンクとして
cc をすでに生成しています。
同様にしてその man ページについてもシンボリックリンクとして生成することにします。
ln -sv gcc.1 /usr/share/man/man1/cc.1
リンク時の最適化 (Link Time Optimization; LTO)
によりプログラム構築できるように、シンボリックリンクを作ります。
ln -sfv ../../libexec/gcc/$(gcc -dumpmachine)/14.2.0/liblto_plugin.so \
/usr/lib/bfd-plugins/
最終的なツールチェーンが出来上がりました。 ここで再びコンパイルとリンクが正しく動作することを確認することが必要です。
そこで健全性テストをここで実施します。
echo 'int main(){}' > dummy.c
cc dummy.c -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'
問題なく動作するはずで、最後のコマンドから出力される結果は以下のようになるはずです。
(ダイナミックリンカーの名前はプラットフォームによって違っているかもしれません。)
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
ここで起動ファイルが正しく用いられていることを確認します。
grep -E -o '/usr/lib.*/S?crt[1in].*succeeded' dummy.log
上のコマンドの出力は以下のようになるはずです。
/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/../../../../lib/Scrt1.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/../../../../lib/crti.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/../../../../lib/crtn.o succeeded
作業しているマシンアーキテクチャーによっては、上の結果が微妙に異なるかもしれません。 その違いは、たいていは /usr/lib/gcc
の次のディレクトリ名にあります。 注意すべき重要な点は
gcc が crt*.o
という 3 つのファイルを /usr/lib
配下から探し出しているということです。
コンパイラーが正しいヘッダーファイルを読み取っているかどうかを検査します。
grep -B4 '^ /usr/include' dummy.log
上のコマンドは以下の出力を返します。
#include <...> search starts here:
/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/include
/usr/local/include
/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/include-fixed
/usr/include
もう一度触れておきますが、プラットフォームの「三つの組
(target triplet)」の次にくるディレクトリ名は CPU
アーキテクチャーにより異なる点に注意してください。
次に、新たなリンカーが正しいパスを検索して用いられているかどうかを検査します。
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
'-linux-gnu' を含んだパスは無視すれば、最後のコマンドの出力は以下となるはずです。
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64")
SEARCH_DIR("/usr/local/lib64")
SEARCH_DIR("/lib64")
SEARCH_DIR("/usr/lib64")
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");
32ビットシステムではディレクトリが多少異なります。 以下は i686 マシンでの出力例です。
SEARCH_DIR("/usr/i686-pc-linux-gnu/lib32")
SEARCH_DIR("/usr/local/lib32")
SEARCH_DIR("/lib32")
SEARCH_DIR("/usr/lib32")
SEARCH_DIR("/usr/i686-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");
次に libc が正しく用いられていることを確認します。
grep "/lib.*/libc.so.6 " dummy.log
最後のコマンドの出力は以下のようになるはずです。
attempt to open /usr/lib/libc.so.6 succeeded
GCC が正しくダイナミックリンカーを用いているかを確認します。
grep found dummy.log
上のコマンドの出力は以下のようになるはずです。 (ダイナミックリンカーの名前はプラットフォームによって違っているかもしれません。)
found ld-linux-x86-64.so.2 at /usr/lib/ld-linux-x86-64.so.2
出力結果が上と異なっていたり、出力が全く得られなかったりした場合は、何かが根本的に間違っているということです。
どこに問題があるのか調査、再試行を行って解消してください。 問題を残したままこの先には進まないでください。
すべてが正しく動作したら、テストに用いたファイルを削除します。
rm -v dummy.c a.out dummy.log
最後に誤ったディレクトリにあるファイルを移動します。
mkdir -pv /usr/share/gdb/auto-load/usr/lib
mv -v /usr/lib/*gdb.py /usr/share/gdb/auto-load/usr/lib