5.10. GCC-4.6.2 - 2回め

GCC パッケージは C コンパイラーや C++ コンパイラーなどの GNU コンパイラーコレクションを提供します。

概算ビルド時間: 7.0 SBU
必要ディスク容量: 1.5 GB

5.10.1. GCC のインストール

バージョン 4.3 以降の GCC を用いてここでのビルド作業を行うと、出来上がるのは再配置可能なコンパイラー (relocated compiler) であり、--prefix パラメータによって指定されたディレクトリからの起動ファイル (startfiles) の探索が行われないものになります。 しかしここで作り出すのは再配置可能なコンパイラーではなく、/tools ディレクトリにある起動ファイルは /tools ディレクトリ内のライブラリにリンクされたコンパイラーを作り出すことが必要であるため、以下のパッチを適用します。 このパッチは、部分的に GCC の古い機能を復活させるものです。

patch -Np1 -i ../gcc-4.6.2-startfiles_fix-1.patch

通常の利用環境において GCC が提供する fixincludes スクリプトは、根本的に不備のあるヘッダーファイルを修正する目的で利用されます。 しかしこの時点で GCC-4.6.2 と Glibc-2.14.1 を既にインストールしており、それぞれのヘッダーファイルは修正する必要がないことが分かっています。 つまり fixincludes スクリプトを利用する必要がありません。 もし実行してしまうと、ホストシステムに既に存在していたヘッダーファイルが修正され、それが GCC のプライベートなディレクトリへとインストールされることになり、ビルド環境を壊してしまうことになります。 そこで fixincludes スクリプトの実行を無効とするために以下を実行します。

cp -v gcc/Makefile.in{,.orig}
sed 's@\./fixinc\.sh@-c true@' gcc/Makefile.in.orig > gcc/Makefile.in

x86 マシンにおいてブートストラップビルドを行うと、コンパイラーフラグ -fomit-frame-pointer が設定されます。 しかしブートストラップではないビルドの場合はデフォルトではこのフラグが無効化されてしまいます。 ここで実現したいのは、ブートストラップビルドを行った場合とまったく同じコンパイラーをビルドすることです。 そこで以下の sed コマンドにより、強制的に上のフラグを利用するようにします。

cp -v gcc/Makefile.in{,.tmp}
sed 's/^T_CFLAGS =$/& -fomit-frame-pointer/' gcc/Makefile.in.tmp \
  > gcc/Makefile.in

以下のコマンドは GCC が利用するダイナミックリンカーの場所を変更して /tools ディレクトリにインストールしたものを用いるようにします。 同時に GCC が探索するインクルードファイルのパスから /usr/include を取り除きます。 インストールの後にスペックファイルを調整する方法もありますが、今ここでこのようにするのは GCC の実際のビルドにおいて新しいダイナミックリンカーを用いるようにするためです。 つまりここでのビルドを通じてすべての実行モジュール類を新しい Glibc に対してリンクするものです。 以下のコマンドによりそれを実現します。

for file in \
 $(find gcc/config -name linux64.h -o -name linux.h -o -name sysv4.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_INCLUDE_DIR
#define STANDARD_INCLUDE_DIR 0
#define STANDARD_STARTFILE_PREFIX_1 ""
#define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
  touch $file.orig
done

上のコマンドがよく分からない場合は一つ一つ読み下していってください。 まず gcc/config ディレクトリには linux.h, linux64.h, sysv4.h といったファイルのいずれかがあるはずです。 それらが存在したら、ファイル名称の末尾に「.orig」をつけたファイルとしてコピーします。 そして一つめの sed コマンドでは、そのファイル内にある「/lib/ld」, 「/lib64/ld」, 「/lib32/ld」という記述部分の頭に「/tools」を付与します。 また二つめの sed コマンドによってハードコーディングされている「/usr」という部分を書き換えます。 そしてここで加えるべき定義文をファイルの末尾に追加し、検索パスと startfile プリフィックスを変更します。 最後に touch によってコピーしたファイルのタイムスタンプを更新します。 cp -u を用いるのは、誤ってコマンドを二度起動したとしてもオリジナルファイルを壊さないようにするためです。

x86_64 では GCC の multilib スペックを無効化します。 これはホスト上のライブラリにリンクされないようにするためです。

case $(uname -m) in
  x86_64)
    for file in $(find gcc/config -name t-linux64) ; do \
      cp -v $file{,.orig}
      sed '/MULTILIB_OSDIRNAMES/d' $file.orig > $file
    done
  ;;
esac

GCC を初めてビルドする際には GMP、MPFR、MPC の各パッケージを必要とします。 tarball を解凍して、所定のディレクトリ名に移動させます。

tar -jxf ../mpfr-3.1.0.tar.bz2
mv -v mpfr-3.1.0 mpfr
tar -Jxf ../gmp-5.0.4.tar.xz
mv -v gmp-5.0.4 gmp
tar -zxf ../mpc-0.9.tar.gz
mv -v mpc-0.9 mpc

専用のディレクトリを再度生成します。

mkdir -v ../gcc-build
cd ../gcc-build

GCC のビルドに入る前に、デフォルトの最適化フラグを上書きするような環境変数の設定がないことを確認してください。

GCC をコンパイルするための準備をします。

CC="$LFS_TGT-gcc -B/tools/lib/" \
    AR=$LFS_TGT-ar RANLIB=$LFS_TGT-ranlib \
    ../gcc-4.6.2/configure --prefix=/tools \
    --with-local-prefix=/tools --enable-clocale=gnu \
    --enable-shared --enable-threads=posix \
    --enable-__cxa_atexit --enable-languages=c,c++ \
    --disable-libstdcxx-pch --disable-multilib \
    --disable-bootstrap --disable-libgomp \
    --without-ppl --without-cloog \
    --with-mpfr-include=$(pwd)/../gcc-4.6.2/mpfr/src \
    --with-mpfr-lib=$(pwd)/mpfr/src/.libs

configure オプションの意味:

--enable-clocale=gnu

このオプションはあらゆる状況において C++ ライブラリに対するロケールモデルが正しく設定されるようにします。 configure スクリプト実行時に de_DE ロケールがインストール済みであることが分かれば、正しい GNU ロケールモデルが設定されます。 しかし de_DE ロケールがインストールされていなかったら、誤った汎用ロケールモデルが設定されてしまうため、アプリケーションバイナリインターフェース (Application Binary Interface; ABI) とは非互換の C++ ライブラリが生成されてしまう可能性があります。

--enable-threads=posix

マルチスレッドコードを扱う C++ の例外処理を有効にします。

--enable-__cxa_atexit

このオプションは atexit を使用せず __cxa_atexit の使用を有効にします。 これによりローカルなスタティックオブジェクトおよびグローバルオブジェクトに対する C++ デストラクターを登録します。 このオプションは、標準に完全準拠したデストラクタ実装のために必要です。 またこれは C++ ABI に影響するものであり C++ 共有ライブラリ、C++ プログラムを作り出し、他の Linux ディストリビューションとの互換性を実現します。

--enable-languages=c,c++

C と C++ の両コンパイラーを生成することを指示します。

--disable-libstdcxx-pch

libstdc++ に対してプリコンパイルヘッダー (pre-compiled header; PCH) をビルドしないように指示します。 これを含めてしまうとサイズが増えることになり、そもそも利用する必要がありません。

--disable-bootstrap

GCC のネイティブビルドを行うには、デフォルトでは "ブートストラップ" ビルドを行ないます。 これは単に GCC をコンパイルするのではなく、数回のコンパイルを繰り返します。 つまり一回めにビルドされたプログラムを使って二回め、三回めのコンパイルを行うものです。 二回め、三回めとコンパイルを繰り返すのは、これによって自分自身を再生成して完璧なものを作り出すためです。 このことによってコンパイルが正確に行われたことを暗に示すことにもなります。 しかし LFS のビルドでは、何度もブートストラップを行う必要のない、手堅い(solid) コンパイラーを作り出します。

パッケージをコンパイルします。

make

パッケージをインストールします。

make install

最後にシンボリックリンクを作成します。 プログラムやスクリプトの中には gcc ではなく cc を用いるものが結構あります。 シンボリックリンクを作ることで各種のプログラムを汎用的にすることができ、通常 GNU C コンパイラーがインストールされていない多くの UNIX システムでも利用できるものになります。 cc を利用することにすれば、システム管理者がどの C コンパイラーをインストールすべきかを判断する必要がなくなります。

ln -vs gcc /tools/bin/cc
[注意]

注意

この時点で、構築したツールチェーンの基本的な (コンパイルやリンクなどの) 機能が正しく動作していることを確認する必要があります。 健全性検査 (sanity check) を行うために以下を実行してください。

echo 'main(){}' > dummy.c
cc dummy.c
readelf -l a.out | grep ': /tools'

問題なく動作した場合はエラーがなかったということで、最後のコマンドから出力される結果は以下のようになるはずです。

[Requesting program interpreter: /tools/lib/ld-linux.so.2]

ここでダイナミックリンカーのディレクトリが /tools/lib であることを確認してください。 あるいは 64 ビットマシンであれば /tools/lib64 であることを確認してください。

コマンドの出力結果が上と異なっていたり、あるいは何も出力されなかった場合は、何かがおかしいことを意味します。 どこに問題があるのか調査、再試行を行って解消してください。 解決せずにこの先に進まないでください。 cc ではなく gcc を使って再度健全性検査を行ってみてください。 これで解決したなら /tools/bin/cc のシンボリックリンクが正しくないということです。 正しく生成し直してください。 また環境変数 PATH が正しいかどうかも確認してください。 echo $PATH を実行して、実行パスリストの先頭が /tools/bin であるかどうか確認します。 PATH が間違っていたなら、実はあなたは lfs ユーザーでログインしていないのかもしれませんし 4.4.「環境設定」 での作業に間違いがあったのかもしれません。

すべてが終了したらテストファイルを削除します。

rm -v dummy.c a.out

本パッケージの詳細は 6.17.2.「GCC の構成」を参照してください。