Windows で GCC 6.2.0 をビルドするメモ
これは 自作 OS Advent Calendar 2016 の 8 日目の記事です。
概要
現時点で最新バージョンの GCC 6.2.0 を Windows 上でビルドするメモです。 このメモでは、i386-elf をターゲットにした GCC を作ります。 コンパイラ本体のみが対象で、標準ライブラリ等はビルドしません。 (将来、標準ライブラリも自作 OS 向けにビルドできるようになったら、また記事を書こうかと思います。っていつだよ!)
Windows で動く x86 ターゲットな GCC と言えば Cygwin や MinGW が有名です。 このメモでも MinGW を使って GCC をビルドします。
しかし MinGW に付属の GCC にはちょっとした問題があったりします。
後者は我慢することもできますが、前者はいかんともしがたい問題です。 フラットバイナリ(ベタバイナリ)を生成することができないと、独自フォーマットの実行可能ファイルを生成することができません。
例えば 30 日でできる!OS 自作入門 の「はりぼて OS」で登場する .hrb 形式は完全に独自のフォーマットで、標準的には著者(川合氏)が製作した bim2hrb というソフトウェアで生成することになっています。 bim2hrb を使わずに GNU リンカ LD で .hrb 形式の実行可能ファイルを作ることもできるのですが、そのためにはフラットバイナリを生成できる LD が必要なのです。
ソースコードの入手
以下、MinGW がすでにインストールされていることを前提として話を進めます。 Windows のくせに、手順が Linux っぽいのはそのためです。
必要なのは GNU Binutils と GCC に加え、GCC が依存するライブラリ GNU MP、GNU MPFR、GNU MPC のソースコードです。
作業ディレクトリは /c/Users/uchan/gcc-6.2.0
として進めますが、任意のディレクトリで大丈夫です。
$ mkdir /c/Users/uchan/gcc-6.2.0
$ cd /c/Users/uchan/gcc-6.2.0
$ wget wget http://ftp.gnu.org/gnu/binutils/binutils-2.27.tar.bz2
$ wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-6.2.0/gcc-6.2.0.tar.bz2
$ wget http://ftp.tsukuba.wide.ad.jp/software/gcc/infrastructure/gmp-6.1.0.tar.bz2
$ wget http://ftp.tsukuba.wide.ad.jp/software/gcc/infrastructure/mpfr-3.1.4.tar.bz2
$ wget http://ftp.tsukuba.wide.ad.jp/software/gcc/infrastructure/mpc-1.0.3.tar.gz
展開と配置
ソースコードを入手したら展開して、GCC の依存ライブラリを GCC ディレクトリに移動しておきます。
$ cd /c/Users/uchan/gcc-6.2.0
$ tar xf binutils-2.27.tar.bz2
$ tar xf gcc-6.2.0.tar.bz2
特に GCC は巨大なのでかなり待たされます。コーヒーでも飲んで休憩しましょう。
GCC の依存ライブラリは、GCC のコードツリーの直下にバージョン番号を取り除いたディレクトリ名(gmp
mpfr
mpc
)で配置しておくと、GCC のビルド時に自動的に認識されます。
Linux 等のシンボリックリンクが使える環境では、ディレクトリを移動せずリンクを張るだけでもいいです。
$ tar xf gmp-6.1.0.tar.bz2
$ mv gmp-6.1.0 gcc-6.2.0/gmp
$ tar xf mpfr-3.1.4.tar.bz2
$ mv mpfr-3.1.4 gcc-6.2.0/mpfr
$ tar xf mpc-1.0.3.tar.gz
$ mv mpc-1.0.3 gcc-6.2.0/mpc
GNU Binutils
GCC に先立って GNU Binutils をビルドしてインストールします。 次の設定でビルドすることにします。
設定項目 | 設定値 |
---|---|
インストール先 | /c/Users/uchan/gcc-6.2.0/i386-elf-gcc |
ターゲットアーキテクチャ | i386-elf |
実行ファイルのプレフィクス | なし(gcc.exe とか ld.exe などになる) |
日本語化 | しない |
configure して make します。
$ export PATH=/g/MinGW/bin:$PATH
$ cd /c/Users/uchan/gcc-6.2.0
$ mkdir build-binutils
$ cd build-binutils
$ ../binutils-2.27/configure --prefix=/c/Users/uchan/gcc-6.2.0/i386-elf-gcc/ --program-prefix= --target=i386-elf --disable-nls
$ make -j4
$ make install
MinGW の MSYS 上でビルド作業をする場合、ネイティブの GCC をパスの先頭に追加しておかないとビルドがうまくいきませんでした。
そのため、先頭で export PATH=/c/MinGW/bin:$PATH
を実行します。
実際のパスは皆さんの MinGW のパスに修正してくださいね。
GCC
いよいよ GCC をビルドしましょう。 インストール先などは GNU Binutils と同じ設定にします。 その他の設定は次のようにしました。
設定項目 | 設定値 |
---|---|
プログラミング言語 | C と C++ |
ターゲットシステム用のサポートライブラリ | ビルドしない |
configure して make します。
$ export PATH=/c/Users/uchan/gcc-6.2.0/i386-elf-gcc/bin:$PATH
$ cd /c/Users/uchan/gcc-6.2.0
$ mkdir build-gcc
$ cd build-gcc
$ ../gcc-6.2.0/configure --prefix=/c/Users/uchan/gcc-6.2.0/i386-elf-gcc/ --program-prefix= --target=i386-elf --disable-nls --enable-languages=c,c++ --disable-multilib
$ make all-gcc
$ make install-gcc
実際のところ、--disable-multilib
を指定しないとビルドできないかは実験していません。
ビルドターゲットに all-gcc
を指定すれば、もしかしたら --disable-multilib
は不要かもしれません。
make
のときに、分割ビルドのための -j
オプションを付けたくなるかもしれません。
しかし、実験したところ -j
オプションを付けるとビルドの途中でフリーズし、最後までビルドできませんでした。
そこで、遅くはなりますが分割ビルドはしない手順としました。
確認
さて、正しくインストールされたか確認を込めて、Hello World バイナリを作ってみます。 実験用のソースコードを用意します。
$ cat main.cpp
int main()
{
int puts(const char*);
[](const char* s){ puts(s); }("hello, world!\n");
}
そうしたら、先ほどインストールした GCC までパスを通します。
ビルドした GCC は MinGW の DLL に依存しているので MinGW/bin
にもパスを通さないと起動できません。
$ export PATH=/c/Users/uchan/gcc-6.2.0/i386-elf-gcc/bin:/c/MinGW/bin:$PATH
最後にビルドします。
GCC 6 からはデフォルトの言語規格が -std=gnu++14
ですので、オプション無しでラムダ式などの先進的な言語機能を使えます。
$ g++ -c main.cpp
readelf
や objdump
等のツールも GNU Binutils をインストールしたおかげで導入されています。
バイナリの中身を見てみましょう。
$ readelf -a main.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
...
おお、ちゃんと ELF バイナリが生成できています! 逆アセンブラは動くでしょうか…?
$ objdump -d main.o
main.o: file format elf32-i386
Disassembly of section .text:
00000000 <_ZZ4mainENKUlPKcE_clES0_>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: 83 ec 0c sub $0xc,%esp
9: ff 75 0c pushl 0xc(%ebp)
大丈夫ですね。完璧です。
以上で、アドベントカレンダー 8 日目は終了です!