Cygwin 上で Linux カーネルをクロスコンパイル

# 1. はじめに

Cygwin は Linux に似た環境ですが、Linux カーネルをコンパイルするためには、環境の違いからいくつかの作業が必要となります。ここでは、Cygwin 上で Linux カーネルをクロスコンパイルする方法を紹介します。

# 2. ファイルシステムの大文字小文字の区別を有効にする

デフォルトの Windows では、ファイル名の作成時には大文字と小文字を区別しますが、ファイルのアクセス時には区別をしません。従って、大文字と小文字だけが違うファイルを展開すると、最初のファイルが上書きされてしまいます。

一方 Linux カーネルのソースコードには、大文字と小文字だけが違うファイルが存在しています。例えば、以下の２つです。

* net/netfilter/xt_TCPMSS.c
* net/netfilter/xt_tcpmss.c

これらのファイルを Cygwin 上で正しく展開できるように、ファイルシステムで大文字小文字の区別を有効にします。Windows のファイルシステムである NTFS には、もともと大文字小文字を区別する機能がありますので、その機能をレジストリで有効にします。

具体的には、HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Kernel\obcaseinsensitive を 0 に設定します。以下のようなファイルを作成してダブルクリックしてインポートしても良いでしょう。

```registry:ntfs_casesensitive.reg
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel]
"obcaseinsensitive"=dword:00000000
```

ちなみに、最近の Windows 10 では fsutil コマンドを使うことで、フォルダ単位で大文字小文字の有効を設定できるようです。

```ps1
fsutil.exe file SetCaseSensitiveInfo foldername enable
```

しかし、この設定はサブフォルダには継承されないようなので、Cygwin の tar での展開時には使えないようです。

# 3. Cygwin のパッケージをインストール

クロスコンパイル環境の構築に必要な以下のパッケージを予めインストールしておきます。

* wget
* gcc-core
* gcc-g++
* make
* texinfo
* libmpc-devel
* bison
* flex
* libelf-devel
* bc
* openssl-devel

libmpc-devel は gcc のコンパイルに必要です。bison, flex, libelf-devel, bc, openssl-devel は Linux カーネルのコンパイルに必要です。

# 4. パラメータ設定

ターゲットのアーキテクチャやツールのバージョンなどのパラメータを設定します。必要に応じて変更してください。

```bash
$ TARGET=x86_64-pc-linux-gnu
$ ARCH=x86_64
$ BINUTILS=binutils-2.31
$ GCC=gcc-7.3.0
$ LINUX=linux-4.17
```

# 5. binutils をビルド＆インストール

ELF 形式を出力できるアセンプラやリンカなどが入った binutils をビルドしてインストールします。

```bash
$ wget http://ftpmirror.gnu.org/binutils/$BINUTILS.tar.xz
$ tar xvf $BINUTILS.tar.xz
$ cd $BINUTILS; mkdir build; cd build
$ ../configure --target=$TARGET
$ make -j $(nproc)
$ make install
$ cd ../..
```

# 6. gcc をビルド＆インストール

ELF 形式を出力できる gcc のクロスコンパイラをビルドしてインストールします。

```bash
$ wget http://ftpmirror.gnu.org/gcc/$GCC/$GCC.tar.xz
$ tar xvf $GCC.tar.xz
$ cd $GCC; mkdir build; cd build
$ ../configure --target=$TARGET --enable-languages=c --disable-shared --disable-threads --disable-libmudflap --disable-libssp --disable-libgomp --disable-libitm --disable-libquadmath --disable-multilib --disable-libmpx --disable-libatomic --with-dwarf2
$ make -j $(nproc)
$ make install
$ cd ../..
```

# 7. Linux カーネルのソースコード展開と修正

Linux カーネルのソースコードを展開して、Cygwin 上でのコンパイルに必要な修正をします。

```bash
$ wget https://cdn.kernel.org/pub/linux/kernel/v4.x/$LINUX.tar.xz
$ tar xvf $LINUX.tar.xz
$ cd $LINUX
$ sed -i 's|objtool/objtool|objtool/objtool.exe|' scripts/Makefile.build
$ sed -i 's|scripts/asn1_compiler|scripts/asn1_compiler.exe|' scripts/Makefile.build
$ sed -i 's|scripts/pnmtologo|scripts/pnmtologo.exe|' drivers/video/logo/Makefile
```

修正点は、ホスト側で動作する内部コマンドのバイナリのファイル名に Cygwin 上では .exe が付いてしまうため、Makefile での依存関係の解決ができなくなってしまう点です。修正方法は、単にファイル名に .exe を付けているだけです。


# 8. Linux カーネルのコンパイル

バイナリを格納するディレクトリ "build" を別途作って、クロスコンパイルのための指定をした上でコンパイルします。CROSS_COMPILE マクロはコンパイラなどのコマンド名のプリフィックスとして使われるので、末尾に "-" を付けるのを忘れないようにします。

```bash
$ mkdir build
$ make ARCH=$ARCH CROSS_COMPILE=$TARGET- O=build defconfig
$ make ARCH=$ARCH CROSS_COMPILE=$TARGET- O=build HOSTCFLAGS="-D_ISOC99_SOURCE -D__GLIBC__" -j $(nproc)
```

HOSTCFLAGS はホスト側で動作するコマンドをコンパイルするときに指定するものです。

\_ISO99_SOURCE を定義しているのは、Cygwin 上では fls() という BSD 由来の関数が /usr/include/strings.h で非静的宣言されているのに対し、Linux カーネル側では inline の静的関数として再定義されていて競合するのを防ぐためです。_ISO99_SOURCE を定義すると内部的に __BSD_VISIBLE が定義されなくなり、BSD由来の関数のプロトタイプ宣言がされなくなります。

\_\_GLIBC__ を定義しているのは、上記の\_ISO99_SOURCE を定義した副作用で \_\_GLIBC__ が定義されなくなり、BSD 由来の関数 strlcpy のプロトタイプ宣言がされなくなってしまうことを回避するためです。

最後に以下のような表示が出れば成功です。

```
Kernel: arch/x86/boot/bzImage is ready  (#1)
make[1]: ディレクトリ '/home/shina/linux-4.17/build' から出ます
```

"build" ディレクトリを消去すれば、コンパイルを最初からやり直せます。

# 参考文献
* [HOWTO: cross-compile the Linux kernel on Cygwin](https://cygwin.com/ml/cygwin/2012-06/msg00221.html)