Skip to main content.

Building Cygwin hosted Linux toolchain

Cygwin をホストとした Linux toolchain のビルド

この HOWTO は、Linux 開発 toolchain(ix86-pc-linux-gnu) を、 Cygwin(ix86-pc-cygwin)プラットフォームをホストとしてビルドする方法について記述します。 例として、Linux 2.4.0 カーネルをビルドする方法を示します。 質問の前に、いやいや、私は誰が何故そんなことをしたいかなんて知りませんよ :-)

目次:

%%howto-version: 1.0
%%title: Cygwin をホストとした Linux toolchain のビルド
%%url: http://www.nanotech.wisc.edu/~khan/software/gnu-win32/
%%category: cygwin
%%filename: cygwin-to-linux-cross-howto
%%author: Mumit Khan

Created: Tue Aug  3 17:34:57 CDT 1999
Last Modified: Thu Jan 25 11:10:11 CST 2001

背景

クロスコンパイル(単純なもの又は canadian なもの(原文: the simple kind or the canadian kind))を行うようになると、3 つの用語が非常に重要となります -- ホスト、ターゲットそしてビルドです。 ホストとは最終的なツールチェイン(the resulting toolchain)が実行されるマシン、 ビルドとは最終的なツールチェインが構築されたマシン、 ターゲットとは最終的なツールチェインが作成するバイナリです。 最も一般的なケースは、ホスト == ビルド == ターゲットです(即ち、Linux マシン上で作成された Linux マシン上の Linux のコンパイラ)。クロスコンパイラの場合は、ホスト == ビルドであり、 ターゲットは異なります(即ち、このツールチェインを使用して Linux マシン上でコンパイル / リンクを行い、i686-pc-cygwin 上で動作するバイナリを作成する場合は、ホストとビルドが Linux となり、ターゲットが i686-pc-cygwin となります)。canadian クロスコンパイラの場合は、ホスト、ビルドそしてターゲットが全て異なります(この場合についての説明は避け、読者の想像にお任せします)。

さて、それでは Windows 2000 と Cygwin が実行されている PC があるとし、この PC 上で Linux のバイナリをビルドできるようにしたいとしましょう。 そんなバカなことを、ということはわかっていますが、このようなことを行いたい人はいます。 この歪んだ要求を満足することは簡単に出来そうでしたし、すぐに出来ました。参考までに、Cygwin マシン上で簡単に Linux カーネルをビルド出来るようになりますよ。

CrossGCC の人々は様々なスキームを使用していますが、 個人的にはそれらはとても分かりにくいと思っています。 しかし手順書を読むには私はあまりにも怠惰なので、 大抵は自分自身のやり方で済ませています。

ここでは基本的な手順を挙げます:

追記には簡単な例が挙げられており、実行形式の名称に常に .exe が付与されるという事態からどのようにして GCC を避けるかについて記述しています(これを避けたければ、 その前にステップ 3 を読んで下さい)。作成された出来たてのクロス開発 toolchain を使用して、Cygwin マシン上で Linux 2.4.0 カーネルをビルドする方法についても、併せて紹介します。

複雑なステップは (2) だけですが、これは一回だけ行えばよい作業です。 クロスコンパイラ用の glibc2 をアップグレードしたいと思ったときにのみ、 この作業を再度行う必要があります。

この HOWTO の目的のために、以下のパッケージを使用します。

  1. Cygwin -- 1.1.7 (最新版を使用します)
  2. GCC -- 2.95.2-6 (Cygwin のソース配布の一部分)
  3. Binutils -- 2.10.1 (GNU によって配布されている通常のもの。たとえ Linux/GNU 用として使用されているものを使用したいと考えるにしても)
  4. glibc2 -- 2.1.3-21 (RedHat 6.2 の最新版を使用)

準備

タイプすることになるユーザーコマンドはこの文書のあちこちに散在しています。 特定のステップを実行すると考えられるホストマシンを示すために、 「cygwin$」又は「linux$」 をシェルプロンプトとして使用します。

Cygwin ホストでは、以下のものを使用します(私は bash を使用していますが、 もし csh / tcsh のようなシェルを使用しているのでしたら、それに合う形にして下さい)。

cygwin$ host=i686-pc-cygwin
cygwin$ build=i686-pc-cygwin
cygwin$ target=i686-pc-linux-gnu
cygwin$ prefix=/usr/local/linux
cygwin$ src_root=/usr/local/src/gnu

$prefix$src_root についてはあなたの環境に合うように変更して構いませんが、 $host$build そして $target は変更しないで下さい。$src_root ディレクトリが存在することを確かめて下さい。 勿論、これら全てのシェル変数を設定する必要はありませんが、 私のタイプの手間を省き、私のタイプミスからあなたを救うことにはなります。

PATH$prefix/bin もまた追加すべきですが、これは binutils をインストールした後ででも構いません(即ち、ステップ 3 の最後)。

ビルドのステップ

ステップ 1

必要なソースパッケージを集めます。最低限、コンパイラのソースとバイナリユーティリティです。

GNU のミラーから、以下のものを取得します:

Cygwin ホストでビルドを行うので、Cygwin ディストリビューション内にある、 幾つかの Cygwin 用のフィックスが施されている gcc-2.95.2 を取得してくるのが安全です。ネットワーク「setup」ユーティリティを使用して(或いは、 単にミラーのどれかから ftp を使用して) GCC のソースをダウンロードする方法については、 http://sources.redhat.com/cygwin/ にある Cygwin のホームページを参照して下さい。 この文書を書いている時点では、最新版は gcc-2.95.2-6 です(訳注: この文書を翻訳している時点での最新版は gcc-2.95.2-7 ですが、これはテストバージョンとして位置付けられています)。

これらを Cygwin マシンにダウンロードして展開します:

cygwin$ cd $src_root
cygwin$ tar zxf /tmp/gcc-2.95.2.tar.gz
cygwin$ tar zxf /tmp/binutils-2.10.1.tar.gz

ステップ 2

linux ターゲットランタイムを集めます。私が思うに、 クロス開発ツールを使用して glibc2 をビルドするよりも簡単なはずです。 一旦この過程が完了したなら(大きな問題はなく終わるはずですが)、 glibc2 のコンフィグレーションとビルドの方法について詳しくなるでしょう。

最初に、Linux マシンから持ってくる最小限必要なランタイムは何かを決定すべく、 ちょっとしたトリッキーな作業を行います。この文書の残りの部分で GNU/Linux について説明する場合、RedHat 6.2 を例に出していますので、 他のディストリビューションを使用している場合は気をつけて下さい。 最初に行うのは、以下のような些細な作業です。

** Linux マシン上で以下を行います **

linux$ mkdir -p /tmp/linux-target-runtime
linux$ cd /tmp/linux-target-runtime
linux$ (cd /usr; tar cf - include) | tar xf -
linux$ ln -s include sys-include

[ なぜ sys-include というシンボリックリンクを追加したか、 私に聞かないで下さい。その場合、私はあなたに、単に GCC コンフィグレーションファイルを見てくれと言うでしょう。 事実、include から sys-include へ名前を変えるだけでもよいのですが、include ディレクトリがどこにも見つからない場合に混乱するパッケージが出てきます。]

[ ある種の散らかったステップを回避するための、tar の --dereference オプションの使用に関する以下の注記も参照して下さい。]

linux$ (cd /usr; tar cf - lib) | tar xf -
linux$ (cd /; tar cf - lib) | tar xf -

[ /lib 及び /usr/lib 全体のコピーの回避方法と、必要なものだけを取り出す方法については以下を参照して下さい。

linux$ ls
include lib sys-include

しかし問題があります。後から全体をコピーしたインクルードに関する問題から始めましょう。 /usr/include 内には、二つのより重要なシンボリックリンクが存在することに気が付くでしょう。

/usr/include/linux --> ../src/linux/include/linux
/usr/include/asm   --> ../src/linux/include/asm

tar の --dereference オプションを使用していなければ、 これらはリンクのままで残っています。 ツリー全体のコピーでは実際のファイルはコピーされません。 即ち、これらはぶら下がったシンボリックリンクのまま残っています。

../src/linux は、 カーネルヘッダとおそらくはソースツリーが含まれている /usr/src/linux を指しています。こ れは、カーネルヘッダツリー全体を取得して関連するパスを修正するか、 シンボリックリンクの代わりにそれらをコピーするか、 のどちらかを行うことを意味します。しかし、些細な問題があります -- シンボリックリンク /usr/include/asm が指しているのはそれ自身がシン ボリックリンクであり、それは asm-i386 を指しています。

/usr/src/linux/include/asm --> asm-i386

それでは、早速リンクを解決しましょう。

linux$ cd /tmp/linux-target-runtime/include
linux$ rm linux asm
linux$ (cd /usr/src/linux/include; tar cf - linux asm-i386) | tar xf -
linux$ ln -s asm-i386 asm

ひゃあ、インクルードに関わる作業は終わりました。 質問の前に、そう、インクルードツリーをコピーする時点で tar の --dereference オプションを使っていればば簡単な話であり、 この厄介な問題を避けることが出来たのです。 でも、他の全てのリンクされたインクルードディレクトリをコピーしたくはなかったのですけどね。

それでは、ライブラリの筋書きを見てみましょう。以下は最も基本的なものであり、 lib ディレクトリ中で絶対に必要なものです。

linux$ cd /tmp/linux-target-runtime
linux$ ls -F lib
Mcrt1.o  gcrt1.o         libc.a            libc_stubs.a     libdl.so.2@
crt1.o   ld-2.1.3.so*    libc.so           libdl-2.1.3.so*  libm-2.1.3.so*
crti.o   ld-linux.so.2@  libc.so.6@        libdl.so.1@      libm.a
crtn.o   libc-2.1.3.so*  libc_nonshared.a  libdl.so.1.9.5*  libm.so.6@

[ 名前の後ろについている「*」は実行形式を、 「@」はシンボリックリンクを示しています ]

ld-linux.solibc.so のような、これらのファイルの幾つは /usr/lib 内ではなく /lib 内にあるので、コピーする必要があります。 もちろん、ツリー全体をコピーすることも出来ます。

ライブラリに関わる唯一の問題は、単なるリンカスクリプトでしかない libc.so を修正することです(そしてこれこそ、このファイルのサイズが数 100 バイトしかないことの理由です!)。 内部を見てみれば、このような内容を目にするでしょう。

linux$ cat lib/libc.so
/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )

最後の行に注意して下さい。明らかにこれらのパスは Cygwin ホスト中で同じように存在し得るものではありません。 そこで、両方のパスを Cygwin 環境に対応するように変更する必要があります。 Cygwin 上の Linux をターゲットとし Cygwin をホストとした toolchain のビルド時のプレフィックスとして /usr/local/linux を選んだのであれば、/lib を $prefix/i686-pc-linux-gnu/lib に変更して下さい。

私はこうしました。

cygwin$ cat /usr/local/linux/i686-pc-linux-gnu/lib/libc.so
/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
GROUP ( /usr/local/linux/i686-pc-linux-gnu/lib/libc.so.6 /usr/local/linux/i686-pc-linux-gnu/lib//libc_nonshared.a )

/usr/local/linux/i686-pc-linux-gnu 内の /usr/local/linux/ は $prefix の設定から、そして i686-pc-linux-gnu は準備で行った $target の設定から導かれたものです。

それでは、これらをまとめて Cygwin マシン上に送りましょう。

linux$ cd /tmp/linux-target-runtime
linux$ tar -zcvf /tmp/linux-target-runtime.tar.gz .
linux$ scp /tmp/linux-target-runtime.tar.gz user@cygwin_hostname:/tmp/
[ あるいは、Cygwin マシンにコピーするための他の方法を使用 ]

** Cygwin マシン上に戻ってきました **

Linux ターゲットランタイムを展開します。

cygwin$ mkdir -p $prefix/$target
cygwin$ tar zxvf /tmp/linux-target-runtime.tar.gz
cygwin$ ls
include lib sys-include

よろしい。ステップ 1 は完了しました。これは骨の折れる作業ですが、 数ヶ月ごとに一回行えばいいだけです(ちょうど歯医者に行くように)。

今や航海は順調です。

ステップ 3:

binutils をビルドし、インストールします。決してソースツリー中でビルドしないで下さい。 ビルドの最上位のディレクトリとして、ここでは単に $src_root/BUILD を選択しました。 この下で binutils と gcc の両方をビルドします。

cygwin$ mkdir -p $src_root/BUILD/binutils
cygwin$ cd $src_root/BUILD/binutils
cygwin$ $src_root/binutils-2.10.1/configure \
  --with-included-gettext \
  --target=$target --host=$host --build=$build \
  --prefix=$prefix -v
cygwin$ make > make.log 2>&1

全てがうまくいったなら、インストールします。

cygwin$ make install > install.log 2>&1

重要: これ以上先へ進む前に、$prefix/binPATH に追加して下さい。

cygwin$ export PATH=$PATH:$prefix/bin

チェックします:

cygwin$ $target-ld --version
GNU ld 2.10.1
Copyright 2000 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License.  This program has absolutely no warranty.
  Supported emulations:
   elf_i386
   i386linux

完了。順風満帆です。

ステップ 4:

GCC をビルドし、インストールします。

cygwin$ mkdir -p $src_root/BUILD/gcc
cygwin$ cd $src_root/BUILD/gcc
cygwin$ $src_root/gcc-2.95.2/configure \
  --enable-languages=c,c++ \
  --with-included-gettext --enable-shared --enable-threads \
  --target=$target --host=$host --build=$build \
  --prefix=$prefix -v

--enable-languages に渡す引数は自由に変更して構いません。 利用可能な言語を全てビルドするのであれば、全体をそのまま残しておきます。 --enable-shared--enable-threads のパラメータも同様です。残りの部分は変更しないで下さい。

cygwin$ make > make.log 2>&1

全てがうまくいったなら、インストールします。

cygwin$ make install > install.log 2>&1

追記

今や、バイナリを作成して Linux/GNU マシン上で実行できることが出来るはずです。全てがうまくいっていれば、こうなります。

$ cat > hello.c
#include <stdio.h>
int 
main()
{
  printf("hello world\n");
  return 0;
}
^D
$ $target-gcc -o hello hello.c
$ ls -l hello*
hello.c hello.exe

おや、なぜ拡張子 .exe が? Cygwin でホストしている gcc を使用したからですが、この自動的な .exe の付与を取り除くことは可能です。 $src_root/gcc-2.95.2-6/gcc/config/i386/xm-cygwin.h にある EXECUTABLE_SUFFIX マクロをコメントアウトして、gcc をリビルドするだけです。

これで Linux カーネルのビルドが可能です。もちろん、/sbin/depmod と /sbin/genksyms はないでしょうが、それはまた別の問題です。 mmap の実装に関するバグと、 Cygwin のコマンドライン引数の長さの制限を回避するための些細なパッチが必要でしょうが、それ以外では、通常は「make menuconfig; make dep; make clean; make bzImage」だけで全てが行えます。gcc-2.95.2 を使用したカーネルのビルドは安全ではないということだけは心に留めておいて下さい。詳細についてはカーネル付属のドキュメントを参照して下さい。

Cygwin でのクロスビルドに対する Linux 2.4.0 カーネルへのパッチ

以下のパッチは Cygwin マシン上でクロスコンパイラを使用して Linux 2.4.0 カーネルをビルドするために必要です。Windows 2000 上の Cygwin 1.1.7、 gcc-2.95.2-6 そして binutils-2.10.1 でしかテストしていません。

<top>/Makefile へのパッチはコマンドラインが長すぎることによって発生するエラーを回避するため、scripts/mkdep.c へのパッチは Cygwin の mmap へのバグを回避するためのものです。

パッチが完了したら、以下を実行すれば

$ make CROSS_COMPILE=i686-pc-linux-gnu- dep
$ make CROSS_COMPILE=i686-pc-linux-gnu- clean
$ make CROSS_COMPILE=i686-pc-linux-gnu- bzImage

カーネルが出来上がります。正しく動くはずです! 常に CROSS_COMPILE 変数を <top>/Makefile 内でセットしておけば、毎回の make 時に CROSS_COMPILE を設定する必要はなくなります。

2001-01-24  Mumit Khan  <khan@nanotech.wisc.edu>

    * Makefile (dep-files): Use xargs to reduce command line length.
    * scripts/mkdep.c (do_depend): Workaround for Cygwin mmap bug.
  
--- Makefile.~1 Wed Jan 24 22:22:16 2001
+++ Makefile    Wed Jan 24 22:23:19 2001
@@ -442,7 +442,7 @@ sums:
 
 dep-files: scripts/mkdep archdep include/linux/version.h
    scripts/mkdep init/*.c > .depend
-   scripts/mkdep `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend
+   find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print | xargs scripts/mkdep | cat > .hdepend
    $(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)"
 ifdef CONFIG_MODVERSIONS
    $(MAKE) update-modverfile
--- scripts/mkdep.c.~1  Wed Jan 24 17:41:19 2001
+++ scripts/mkdep.c Wed Jan 24 17:41:50 2001
@@ -471,7 +471,9 @@ cee_CONFIG_word:
 void do_depend(const char * filename, const char * command)
 {
    int mapsize;
+#ifndef __CYGWIN__
    int pagesizem1 = getpagesize()-1;
+#endif
    int fd;
    struct stat st;
    char * map;
@@ -490,7 +492,9 @@ void do_depend(const char * filename, co
    }
 
    mapsize = st.st_size;
+#ifndef __CYGWIN__
    mapsize = (mapsize+pagesizem1) & ~pagesizem1;
+#endif
    map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
    if ((long) map == -1) {
        perror("mkdep: mmap");

=== Cygwin でのクロスビルドに対する Linux 2.4.0 カーネルへのパッチの終わり

最後に

GNU コンパイラコレクション(GCC)に関する更なる情報については、 GCC のホームページを参照して下さい。

http://gcc.gnu.org/

Cygwin に関する更なる情報については、Cygnus の Cygwin プロジェクトのページを参照して下さい。

http://www.cygwin.com/

CrossGCC に関する更なる情報については、以下を参照して下さい。

http://www.objsw.com/CrossGCC/

binutils に関する更なる情報については、以下を参照して下さい。

http://sources.redhat.com/binutils/

本文書の最新版、及び様々な 32 ビット Windows システム上で動作する GNU ツールに関する他の情報は、私の gnu-win32 ページにあります。

http://www.nanotech.wisc.edu/~khan/software/gnu-win32/

作成日: Tue Aug  3 17:34:57 CDT 1999
最終更新日: Thu Jan 25 11:10:11 CST 2001
Mumit Khan <khan@nanotech.wisc.edu>

幸運を。

%%end-howto