クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点

## はじめに

[https://github.com/ikwzm/FPGA-SoC-Linux](https://github.com/ikwzm/FPGA-SoC-Linux) では Zynq(ARMv7+FPGA)用の Debian GNU/Linux を用意しています。このリポジトリで用意している Linux Kernel や Debian Package は、Ubuntu(x86-64)上でクロスコンパイルしたものです。この記事では、デバイスドライバをセルフコンパイル(ターゲットシステムの上でコンパイル)しようとしたときに遭遇した問題点と(暫定的な)解決方法について説明します。

## 2019年10月17日 追記

ここで説明しているのは Linux Kernel 4.x の場合です。Linux Kernel 5.x ではもう一工夫必要でした。詳細は以下の記事を参照してください。

  * [「クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点(Linux Kernel 5.x編)」@Qiita](https://qiita.com/ikwzm/items/0aa165fa21fa9cca15c0)


## ヘッダーパッケージのインストール

ターゲット上でデバイスドライバをセルフコンパイルするためには、ターゲットにヘッダーパッケージがインストールされている必要があります。[https://github.com/ikwzm/FPGA-SoC-Linux](https://github.com/ikwzm/FPGA-SoC-Linux) では ```linux-headers-4.14.21-armv7-fpga_4.14.21-armv7-fpga-1_armhf.deb``` 等のように ```linux-headers-*_*_?_armhf.deb``` のようなファイル名で用意しています(```*``` には Linux Kernel のバージョン番号を含んだタグ、```?``` にはパッケージのバージョン番号が入ります)。

まずはこのヘッダーパッケージをインストールします(すでにインストールされている場合もあります)。

<details><summary>dpkg -i linux-headers-4.14.21-armv7-fpga_4.14.21-armv7-fpga-1_armhf.deb のログ</summary><div>

```console
fpga@debian-fpga:~/debian$ sudo dpkg -i linux-headers-4.14.21-armv7-fpga_4.14.21-armv7-fpga-1_armhf.deb
Selecting previously unselected package linux-headers-4.14.21-armv7-fpga.
(Reading database ... 62256 files and directories currently installed.)
Preparing to unpack linux-headers-4.14.21-armv7-fpga_4.14.21-armv7-fpga-1_armhf.deb ...
Unpacking linux-headers-4.14.21-armv7-fpga (4.14.21-armv7-fpga-1) ...
Setting up linux-headers-4.14.21-armv7-fpga (4.14.21-armv7-fpga-1) ...
```

</div></details>

## セルフコンパイルの失敗例

### ダウンロード

ここで udmabuf (カーネル空間に確保したバッファをユーザー空間から使えるようにするデバイスドライバ)を例としてコンパイルしてみます。

<details><summary>git clone https://github.com/ikwzm/udmabuf.git のログ</summary><div>

```console
root@debian-fpga:~/work# git clone https://github.com/ikwzm/udmabuf.git
Cloning into 'udmabuf'...
remote: Counting objects: 212, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 212 (delta 0), reused 3 (delta 0), pack-reused 206
Receiving objects: 100% (212/212), 160.13 KiB | 0 bytes/s, done.
Resolving deltas: 100% (115/115), done.
root@debian-fpga:~/work# cd udmabuf
```

</div></details>

### 失敗時のログ

udmabuf を make コマンドでビルドすると、次のように失敗します。

```console
root@debian-fpga:~/work/udmabuf# make
make -C /lib/modules/4.14.21-armv7-fpga/build ARCH=arm CROSS_COMPILE= M=/root/work/udmabuf modules
make[1]: Entering directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
  CC [M]  /root/work/udmabuf/udmabuf.o
/bin/sh: 1: scripts/basic/fixdep: Exec format error
scripts/Makefile.build:326: recipe for target '/root/work/udmabuf/udmabuf.o' failed
make[2]: *** [/root/work/udmabuf/udmabuf.o] Error 2
Makefile:1508: recipe for target '_module_/root/work/udmabuf' failed
make[1]: *** [_module_/root/work/udmabuf] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
Makefile:19: recipe for target 'all' failed
make: *** [all] Error 2
```

### 失敗の理由

ログを見ると、どうやら ```scripts/basic/fixdep``` の実行フォーマットが違うと言っているようです。```scripts/basic/fixdep``` は ```/usr/src/linux-headers-*/scripts/basic/fixdep``` にあります。ファイルの型を確認すると、ホストコンピュータのアーキテクチャ(ここでは x86-64)のバイナリファイルになっています。

<details><summary>file /usr/src/linux-headers-4.14.21-armv7-fpga/scripts/basic/fixdep のログ</summary><div>

```console
root@debian-fpga:~/work/udmabuf# file /usr/src/linux-headers-4.14.21-armv7-fpga/scripts/basic/fixdep
/usr/src/linux-headers-4.14.21-armv7-fpga/scripts/basic/fixdep: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped
```

</div></details>

### 暫定的な対処方法

そこで、次のように ```/usr/src/linux-headers-*/``` にインストールした各種スクリプトをターゲットのアーキテクチャ用に再ビルドします。

<details><summary>make scripts 実行時のログ</summary><div>

```console
root@debian-fpga:~/work/udmabuf# cd /usr/src/linux-headers-4.14.21-armv7-fpga/
root@debian-fpga:/usr/src/linux-headers-4.14.21-armv7-fpga# make scripts
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/bin2c
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
scripts/kconfig/conf  --silentoldconfig Kconfig
  HOSTCC  scripts/dtc/dtc.o
  HOSTCC  scripts/dtc/flattree.o
  HOSTCC  scripts/dtc/fstree.o
  HOSTCC  scripts/dtc/data.o
  HOSTCC  scripts/dtc/livetree.o
  HOSTCC  scripts/dtc/treesource.o
  HOSTCC  scripts/dtc/srcpos.o
  HOSTCC  scripts/dtc/checks.o
  HOSTCC  scripts/dtc/util.o
  HOSTCC  scripts/dtc/dtc-lexer.lex.o
  HOSTCC  scripts/dtc/dtc-parser.tab.o
  HOSTLD  scripts/dtc/dtc
  HOSTCC  scripts/genksyms/genksyms.o
  HOSTCC  scripts/genksyms/parse.tab.o
  HOSTCC  scripts/genksyms/lex.lex.o
  HOSTLD  scripts/genksyms/genksyms
  CC      scripts/mod/empty.o
  HOSTCC  scripts/mod/mk_elfconfig
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  CC      scripts/mod/devicetable-offsets.s
  CHK     scripts/mod/devicetable-offsets.h
  HOSTCC  scripts/mod/file2alias.o
  HOSTCC  scripts/mod/sumversion.o
  HOSTLD  scripts/mod/modpost
  HOSTCC  scripts/kallsyms
  HOSTCC  scripts/conmakehash
  HOSTCC  scripts/sortextable
scripts/sortextable.c:31:32: fatal error: tools/be_byteshift.h: No such file or directory
 #include <tools/be_byteshift.h>
                                ^
compilation terminated.
scripts/Makefile.host:102: recipe for target 'scripts/sortextable' failed
make[1]: *** [scripts/sortextable] Error 1
Makefile:558: recipe for target 'scripts' failed
make: *** [scripts] Error 2
```

</div></details>

```scripts/sortextable``` でコンパイルエラーが出ます。どうやら ```<tools/be_byteshift.h>``` が見つからないと言っているようです。ですが、とにかく、```scripts/basic/fixdep``` のビルドは出来ているようです。再度、デバイスドライバをビルドしてみます。

```console
root@debian-fpga:/usr/src/linux-headers-4.14.21-armv7-fpga# cd ~/work/udmabuf
root@debian-fpga:~/work/udmabuf# make
make -C /lib/modules/4.14.21-armv7-fpga/build ARCH=arm CROSS_COMPILE= M=/root/work/udmabuf modules
make[1]: Entering directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
  CC [M]  /root/work/udmabuf/udmabuf.o
  Building modules, stage 2.
  MODPOST 1 modules
WARNING: modpost: Found 1 section mismatch(es).
To see full details build your kernel with:
'make CONFIG_DEBUG_SECTION_MISMATCH=y'
  CC      /root/work/udmabuf/udmabuf.mod.o
  LD [M]  /root/work/udmabuf/udmabuf.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
```

今度は正常にビルド出来ました。

## ヘッダーパッケージの修正

本来ならばターゲットアーキテクチャ用にコンパイルした実行ファイルをヘッダーパッケージに含めるようにするのが正しい方法だと思います。しかし、私の調べた範囲では、このようにヘッダーパッケージをビルドする方法が判りませんでした。どなたか詳しい方がいらっしゃれば、教えてくださると有難いです。

次善の策として、ターゲットにヘッダーパッケージをインストールした際に自動的に各種スクリプトを再ビルドするように、ヘッダーパッケージを作り直します。具体的には、ヘッダーパッケージを生成するスクリプトに次のようなパッチを当てます。

<details><summary>linux-4.14.21-armv7-fpga-patch-builddeb.diff</summary><div>

```diff:linux-4.14.21-armv7-fpga-patch-builddeb.diff
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index 0bc8747..515963f 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -313,7 +313,7 @@ fi
 
 # Build kernel header package
 (cd $srctree; find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl) > "$objtree/debian/hdrsrcfiles"
-(cd $srctree; find arch/*/include include scripts -type f) >> "$objtree/debian/hdrsrcfiles"
+(cd $srctree; find arch/*/include include tools/include scripts -type f) >> "$objtree/debian/hdrsrcfiles"
 (cd $srctree; find arch/$SRCARCH -name module.lds -o -name Kbuild.platforms -o -name Platform) >> "$objtree/debian/hdrsrcfiles"
 (cd $srctree; find $(find arch/$SRCARCH -name include -o -name scripts -type d) -type f) >> "$objtree/debian/hdrsrcfiles"
 if grep -q '^CONFIG_STACK_VALIDATION=y' $KCONFIG_CONFIG ; then
@@ -330,6 +330,15 @@ mkdir -p "$destdir"
 (cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be
 ln -sf "/usr/src/linux-headers-$version" "$kernel_headers_dir/lib/modules/$version/build"
 rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles"
+mkdir -m 755 -p "$kernel_headers_dir/DEBIAN"
+cat <<EOF >> $kernel_headers_dir/DEBIAN/postinst
+#!/bin/sh -e
+
+make -C /usr/src/linux-headers-$version scripts
+
+EOF
+
+chmod 755 $kernel_headers_dir/DEBIAN/postinst
 
 cat <<EOF >> debian/control
 





```

</div></details>

このパッチファイルは builddeb に対して、```tools/include``` 以下のファイルを含めるようにしています。これにより ```scripts/sortextable``` のビルドが正常に終了します。

そして、スクリプトをターゲット用に再ビルドするための postinst スクリプトをヘッダーパッケージに追加しています。これにより、ヘッダーパッケージをインストールした際、自動的にスクリプトを再ビルドします。

ヘッダーパッケージを昔のバージョンからアップグレードする際は、そのまま新しいヘッダーパッケージをインストールします。

<details><summary>dpkg -i linux-headers-4.14.21-armv7-fpga_4.14.21-armv7-fpga-2_armhf.deb のログ</summary><div>

```console
root@debian-fpga:/home/fpga/debian# dpkg -i linux-headers-4.14.21-armv7-fpga_4.14.21-armv7-fpga-2_armhf.deb
(Reading database ... 81503 files and directories currently installed.)
Preparing to unpack linux-headers-4.14.21-armv7-fpga_4.14.21-armv7-fpga-2_armhf.deb ...
Unpacking linux-headers-4.14.21-armv7-fpga (4.14.21-armv7-fpga-2) over (4.14.21-armv7-fpga-1) ...
Setting up linux-headers-4.14.21-armv7-fpga (4.14.21-armv7-fpga-2) ...
make: Entering directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/bin2c
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
scripts/kconfig/conf  --silentoldconfig Kconfig
  HOSTCC  scripts/dtc/dtc.o
  HOSTCC  scripts/dtc/flattree.o
  HOSTCC  scripts/dtc/fstree.o
  HOSTCC  scripts/dtc/data.o
  HOSTCC  scripts/dtc/livetree.o
  HOSTCC  scripts/dtc/treesource.o
  HOSTCC  scripts/dtc/srcpos.o
  HOSTCC  scripts/dtc/checks.o
  HOSTCC  scripts/dtc/util.o
  HOSTCC  scripts/dtc/dtc-lexer.lex.o
  HOSTCC  scripts/dtc/dtc-parser.tab.o
  HOSTLD  scripts/dtc/dtc
  HOSTCC  scripts/genksyms/genksyms.o
  HOSTCC  scripts/genksyms/parse.tab.o
  HOSTCC  scripts/genksyms/lex.lex.o
  HOSTLD  scripts/genksyms/genksyms
  CC      scripts/mod/empty.o
  HOSTCC  scripts/mod/mk_elfconfig
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  CC      scripts/mod/devicetable-offsets.s
  CHK     scripts/mod/devicetable-offsets.h
  HOSTCC  scripts/mod/file2alias.o
  HOSTCC  scripts/mod/sumversion.o
  HOSTLD  scripts/mod/modpost
  HOSTCC  scripts/kallsyms
  HOSTCC  scripts/conmakehash
  HOSTCC  scripts/sortextable
make: Leaving directory '/usr/src/linux-headers-4.14.21-armv7-fpga'
```

</div></details>

## 参考

 * [https://github.com/ikwzm/FPGA-SoC-Linux](https://github.com/ikwzm/FPGA-SoC-Linux)