Seeker's Memo

個人的で技術的かもしれないメモがメインのブログです。http://seekers.hatenablog.jp/about

クロスGCC(on Linux Host for Targeting Mingw-w64 Windows)のビルド

色々試行錯誤重ねた結果、とりあえず出来上がったので記事にして残しておく。
以下のコンフィグレーション及び手順は、”本当”にとりあえずビルド通る&動くことを目標にしたやり方なので、あしからず。

1.用意するもの
GCC-4.8.1のソースコード
mpc-1.0.1, mpfr-3.1.2, gmp-5.1.2の各ソースコード
・isl-0.11.1 cloog-0.18.0のソースコード
・libiconv-1.14のソースコードと、libiconv-1.14-gets-undefined-in-C11パッチと有志によるja対応パッチ
Mingw-w64-v3.0.0のソースコード
LFS(Linux-From-Scratch)ブックを始め、各種公式サイトや参考文献などを閲覧できる環境
・何でもいいので、GCCと各種ビルド用ツールを走らせることができるLinuxが入ったx86_64マシン
・気合と根性とたっぷりの時間

2.確認
 ・Target:CPU=Core-i7 875K(x86_64/i686), Mem=16GB ※(OS=Linuxは今回Fedora19-amd64KDE-Spinを使用しました)
 ・Host:同上。OS=Windows 7 64bit Professional
・使用GCC=x86_64-redhat-linux 4.8.1
・これから作るクロスコンパイラ(GCC)セット=x86_64-w64-mingw32 4.8.1(出来上がったコンパイラの-m32オプションで32bitコード=i686-w64-mingw32タイプの吐き出しがおそらく可能)

3.準備する
まずはさくっとLinux環境を構築します。
KDEを選んだのでそのまま日本語を選択して各種設定しながらインストール後、KDEの日本語対応パッケージを入れる。
さらに、Linuxネイティヴのgcc,g++、それからautotools(automake,autoconf,m4)や、flex,bison,patchパッケージ等必要なものをインストールします。各種tarballに対応するために追加で必要な圧縮展開ツールを入れる可能性があるかも?

4.作業開始
ホームディレクトリに、packageディレクトリ(別にどこでもいいけど)を作成し、そこにダウンロードした各種ソースコードとパッチファイルを放り込む
~/.bashrcに以下を追記。(いやまあ、書かなくてもいいんだけど、スクリプトに書いてもいいし、そのまま端末で打ってもいいし、とにかく下記の環境変数を設定しておく。MAKEFLAGSとかCFLAGSとかは好きなようにしてください)

export CFLAGS="-O2 -pipe -march=corei7 -mtune=generic"
export CXXFLAGS="${CFLAGS}"
export MAKEFLAGS="-j 8"
export TARGET=x86_64-w64-mingw32
export CHOST=x86_64-redhat-linux
export PACKAGE=$HOME/package

export BUILDS=$HOME/Builds
export CROSS_SYSROOT=$HOME/cross-mingw64

export PATH=$PATH:$CROSS_SYSROOT/bin

CFLAGS: コンパイル時にGCCに指示するCソース向けフラグ
CXXFLAGS:同じくC++向けフラグ。上記のように書いて、同じフラグを用いることにする。
MAKEFLAGS:makeコマンド時に適用する、パラレル実行数(CPUコア数*2程度)
TARGET:今回の場合、作成するクロスコンパイラの名前や配置ディレクトリなどに使用する。またクロスコンパイラでビルドしたバイナリの実行環境を示すものでもある。i686-w64-mingw32でも可
CHOST=名前は何でもいいが、今回ビルドに使用するクライアントの定義 x86_64-pc-linux-gnuなど。詳しくは他のサイトでどうぞ。
CROSS_SYSROOT(ここにビルド成果物を配置)やBUILDS(ビルド作業ディレクトリ)は適当に読み替えてください。
それができたら、souce .bash_profileする。
LFS(Linux-From-Scratch)ブックに従い、専用のユーザーを作成してから作業するのもあり。その場合の手順の一部はそれに従う。


binutilsのビルド&インストール

tar xf $PACKAGE/binutils-2.23.2.tar.bz2
cd binutils-2.23.2
echo '##Texinfo-5.1 でドキュメントをビルドする際の文法の誤りを訂正します。'
sed -i -e 's/@colophon/@@colophon/' \
       -e 's/doc@cygnus.com/doc@@cygnus.com/' bfd/doc/bfd.texinfo
mkdir -v ../binutils-build
cd ../binutils-build
../binutils-2.23.2/configure  \
    --build=$CHOST \
    --host=$CHOST \
   --target=$TARGET \
   --enable-targets=x86_64-w64-mingw32,i686-w64-mingw32 \
    --prefix=$CROSS_SYSROOT            \
    --with-sysroot=$CROSS_SYSROOT      \
    --with-lib-path=$CROSS_SYSROOT/lib \
    --target=$TARGET    \
    --enable-64bit-bfd	\
    --with-windres      \
    --disable-shared    \
    --disable-werror
	
make && make install
cd $BUILDS
rm -rf build-binutils

こんな感じでどうぞ。
修正部分は、必要かわからんかったけどLFSブックを参考にしました。

    • enable-targetsでクロスコンパイラが吐き出すバイナリが2種類対応することになる?

必要ならインストール前にmake checkしてください。

次に、mingw-w64-v3.0.0のヘッダファイルのインストール

cd $BUILDS
tar xf $PACKAGE/mingw-w64-v3.0.0.tar.bz2
mkdir build-mingw-w64-headers
cd build-mingw-w64-headers
../mingw-w64-v3.0.0/configure \
  --host=$TARGET \
  --prefix=$CROSS_SYSROOT/$TARGET	\
  --enable-sdk=all		\
  --enable-secure-api		\
  --without-crt \
  --enable-wchar_t \
  --enable-unicode 
make && make install

ヘッダーファイルのみのインストールです。とるあえずはGCCのコア部分をビルドするのに必要です。
SDKとセキュアAPIを有効にしてます。
wchar_tとunicodeのフラグはわかんないけど入れてみた。必要ないかも?
ついでに以下の作業をこなしておきます。

cd $CROSS_SYSROOT
echo '## ※Windowsの場合は以下のところを、リンクの代わりにコピーで対応.もちろんそうでなくてもコピーで対応可能かも'
ln -sv $CROSS_SYSROOT/$TARGET $CROSS_SYSROOT/mingw
mkdir -pv $CROSS_SYSROOT/$TARGET/lib
ln -sv $CROSS_SYSROOT/$TARGET/lib $CROSS_SYSROOT/$TARGET/lib64 

上記は、成果物のディレクトリ以下のところに、リンクとディレクトリを作成しますです。(じゃないと以降の作業が失敗します)

続いて、クロスGCC-4.8.1のビルドです。
MinGW-CRTがまだできていないので、いきなりの完成は無理です。
まずはコア部分のみビルド&インストールします。

tar xf $PACKAGE/gcc-4.8.1.tar.bz2
cd gcc-4.8.1
tar xf $PACKAGE/mpfr-3.1.2.tar.bz2
mv mpfr-3.1.2 mpfr
tar xf $PACKAGE/mpc-1.0.1.tar.gz
mv mpc-1.0.1 mpc
tar xf $PACKAGE/gmp-5.1.3.tar.bz2
mv gmp-5.1.3 gmp
tar xf $PACKAGE/isl-0.12.tar.bz2
mv isl-0.12 isl
tar xf $PACKAGE/cloog-0.18.0.tar.gz
mv cloog-0.18.0 cloog
tar xf $PACKAGE/libiconv-1.14.tar.gz
mv libiconv-1.14 libiconv
cd libiconv
patch -p1 < $PACKAGE/libiconv-1.14-gets-undefined-in-C11.patch
patch -p1 < $PACKAGE/libiconv-1.14-ja-1.patch
cd ..
# Fix chk isl version
sed -i.orig 's/0.10/0.12/g' configure
# Fix make-temp-file
#patch -p1 < $PACKAGE/make-temp-file.diff
# Fix libstdc++-v3-chk-sleep-func
sed -i.orig 's/nono/no/g' libstdc++-v3/configure
# Fix Hardcoding
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@/$CROSS_SYSROOT/$TGT&@g' \
	-e 's@/usr@/$CROSS_SYSROOT/$TGT@g' $file.orig > $file
    echo '                                                                                                                                                                                                                                     
  #undef STANDARD_STARTFILE_PREFIX_1                                                                                                                                                                                                           
  #undef STANDARD_STARTFILE_PREFIX_2                                                                                                                                                                                                           
  #define STANDARD_STARTFILE_PREFIX_1 "/x86_64-w64-mingw32/lib/"                                                                                                                                                                               
  #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file
    touch $file.orig
done
echo '### GCCコアをビルド'
mkdir -p $BUILDS/build-gcc
cd $BUILDS/build-gcc
../gcc-4.8.1/configure   \
  --target=$TARGET       \
  --enable-targets=all	\
  --enable-languages=c,c++  \
  --enable-__cxa_atexit \
  --disable-win32-registory \
  --enable-threads=win32 \
  --enable-wchar_t \
  --enable-unicode \
  --disable-libgomp \
  --disable-libatomic   \
  --disable-libitm      \
  --disable-libmudflap  \
  --disable-libquadmath \
  --disable-libsanitizer  \
  --disable-libssp        \
  --disable-install-libiberty \
  --with-sysroot=$CROSS_SYSROOT \
  --prefix=$CROSS_SYSROOT \
  --with-gxx-include-dir=$CROSS_SYSROOT/$TARGET/include/c++/4.8.1 \
  --enable-version-specific-runtime-libs \
  --enable-lto \
  --with-libs=$CROSS_SYSROOT/mingw/lib \
  --with-headers=$CROSS_SYSROOT/mingw/include \
  --enable-libstdcxx-allocator \
  --enable-libstdcxx-pch \
  --enable-libstdcxx-threads \
  --enable-libstdcxx-wchar_t \
  --enable-libstdcxx-unicode 
    • targetでクロスGCCが作るよ!って指定します。※今回の場合buildは$CHOST、hostは$TARGETですが指定しません。
    • enable-threads=win32Win32スレッドモデルを選択。

--enable-targets=all でi686x86_64を両対応。
--enable-wchar_t --enable-unicode --enable-libstdcxx-unicode で--enable-libstdcxx-wchar_t ユニコードに対応したかも?
--with-sysroot=$CROSS_SYSROOT --prefix=$CROSS_SYSROOT 忘れずに指定してください。

    • with-gxx-include-dir=$CROSS_SYSROOT/$TARGET/include/c++/4.8.1 C++ヘッダを配置する場所の指定です。
    • with-libs=$CROSS_SYSROOT/mingw/lib --with-headers=$CROSS_SYSROOT/mingw/include 今回はMinGWなので、MinGWヘッダとランタイムの(これから配置するものも含め)場所指定です。

--enable-libstdcxx-allocator --enable-libstdcxx-pch ほかサイト様似説明を任せます。
--enable-libstdcxx-threads => 私の解釈だと、libstdc++のスレッド部分を実装してビルドさせます。
その他の-disable-lib***はそうしないと今回のようなクロスコンパイラのビルドに失敗するみたいです。試しにenableに変えてみてください。

以下のコマンドで、とりあえずのGCCコア部分をインストールします。

make all-gcc  && make install-gcc 

順調ならば、次のステップへ。
MinGW-w64-v3.00のCRTをビルド&インストールします

cd $BUILDS
mkdir -p $BUILDS/build-mingw-w64-crt
cd $BUILDS/build-mingw-w64-crt
../mingw-w64-v3.0.0/mingw-w64-crt/configure \
  --host=$TARGET \
  --enable-lib32 \
  --prefix=$CROSS_SYSROOT/x86_64-w64-mingw32	 \
  --with-sysroot=$CROSS_SYSROOT \
  --enable-wchar_t \
  --enable-unicode 
make && make install

最後に、残りのGCC部分をビルド&インストールして終了です。

cd $BUILDS/build-gcc
make && make install
ln -sv $CROSS_SYSROOT/lib/gcc/x86_64-w64-mingw32/lib/libgcc_s.a $CROSS_SYSROOT/lib/libgcc_s.a

上記のリンクを貼るのは、実際にクロスコンパイラでなにかビルドした時に、libgcc_s.aがねえよ!って怒られるからです。
貼れば、問題がなくなります。

お疲れ様でした。
最後に、$TARGET-gccや、$TARGET-g++を -v して確認したり、実際にコンパイルしてみたりして動くかどうか確認してください。
できたEXEを実行する際は「WINE」を入れておくとスムーズです。

※最後に、色々な団体、個人さまに謝辞を。

※以下サンプルのっけておきます。

#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <windows.h>
int main(int argc, char *argv[]){
  _tsetlocale(LC_ALL, _T(""));
  _tprintf(_T("Hello, MinGW!\n日本語で「こんにちは世界」を表示"));
  MessageBoxW(NULL, _T("This is a test.\n日本語でメッセージ表示!!"), _T("Test-MessageBox"), MB_OK | MB_ICONINFORMATION);
  return 0;
}
$ x86_64-w64-mingw32-g++ Main.cpp -static -D_UNICODE  
$ wine a.exe
Hello, MinGW!
日本語で「こんにちは世界」を表示
$ x86_64-w64-mingw32-objdump ./a.exe -h

a.exe:     ファイル形式 pei-x86-64

セクション:
索引名          サイズ      VMA               LMA               File off  Algn

※お使いのデスクトップ環境に応じたメッセージボックスが出ます。