2023-12-23

Linux kernel の自前ビルドに再挑戦

Firefox の自前ビルドに失敗したため、なぜか次は Linux kernel の自前ビルドに挑戦してみました。

なんで Linux kernel の自前ビルドを?

Firefox の失敗についてはこちらに記載してあります。

Firefox の自前ビルドに挑戦してみた

Debian 12 ベースの LMDE 6 でコンパイルしているのですが、Firefox は Rust 1.70 以上を要求してくるのです。Debian 12 の標準リポジトリには Rust 1.63 までの提供で、ちょっと面倒になってあきらめてしまいました。

せっかく盛り上がった自前ビルドの流れを止めるわけにはいきません。toolchain が最新でなくてもできることはないかと考えたところ、以前に一度だけ試してみた Linux kernel の自前ビルドであればやれるだろうということです。

Firefox の自前ビルドは -march=native を指定して最適化を強化したかったのですが、OS 自体の最適化を強化すると Firefox 以外にも効果が出るのではないか、という目論見です。

 

いざ準備を

前回の Linux kernel の自前ビルド手順が記録されていて助かりました。これを参考にもう一度挑戦してみます。

Linux kernel を自分仕様に再コンパイルしてみた

前回は Ubuntu ベースの Linux Mint 21.1 の時に試したのですが、ビルド後の Linux kernel を使ってもあまり違いが感じられませんでした。今回は CPU も Zen2 から Zen3 に変わっていて、OS も Debian ベースの LMDE 6 に変わっているので変化があるのかがポイントです。

・ソースのダウンロード

 「アップデートマネージャー」の「ソフトウェアソース」から「ソースコードリポジトリ」を有効化します。

apt install linux-source-6.5

/usr/src 配下に linux-source-6.5.tar.xz がダウンロードされました。140MB くらいです。これを別なディレクトリに展開しました。すると 5GB くらいです。コンパイルが完了すると kernel-image など大きめのファイルが生成されるので、空きディスク容量に余裕のあるディレクトリを用意しましょう。

 

・開発ツールのインストール

今回は LLVM と Clang でコンパイルする予定です。

apt install clang-16
apt install llvm-16

apt install clang とすると、ちょっと古い clang-14 がインストールされてしまいます。LLVM も同様で、apt install llvm では llvm-14 がインストールされてしまいます。

 

・コンフィグファイルの用意

ひとまず設定は変えずに単純に再コンパイルしてみたかったので、既存の Debian のコンフィグファイルをそのままコピーしてみました。

cp /boot/config-$(uname -r) ~/linux-source-6.5/.config

 

コンパイル(その一) 

ではコンパイルしてみましょう。

time KCFLAGS="-march=native -pipe" KCPPFLAGS="-march=native -pipe" make -j 8 LLVM=1 bindeb-pkg LOCALVERSION=-zen3

ん、失敗です。

clang が見つからないとのことです。

sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-16 1
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-16 1

これで大丈夫なはずです。

make clean でお掃除してから再実行したところ、見事にコンパイルが正常終了しました。1時間 30分ほどで完了した気がします。

linux-headers-6.5.10-zen3_6.5.10-2_amd64.deb(9.0MB)

linux-image-6.5.10-zen3-dbg_6.5.10-2_amd64.deb(632.1MB)

linux-image-6.5.10-zen3_6.5.10-2_amd64.deb(76.7MB)

linux-libc-dev_6.5.10-2_amd64.deb(1.3MB)

.config で CONFIG_DEBUG_INFO=n を指定した気もしますが、なぜか巨大な dbg も生成されてますね。

あと、-O3 を付け忘れました。

せっかくなので、できあがった自前 kernel を使ってみることにします。

sudo dpkg -i linux-headers-6.5.10-zen3_6.5.10-2_amd64.deb
sudo dpkg -i linux-libc-dev_6.5.10-2_amd64.deb
sudo dpkg -i linux-image-6.5.10-zen3_6.5.10-2_amd64.deb

そして PC を再起動します。

・・・速い。

LMDE 6 の Cinnamon にログオンしたあとの動作が全然違います。目に見えて速いです。なんなんですか、これは!!Firefox さえも少し軽快に動作しているように感じます。-O2 と -march=native を指定したコンパイルですが、こんなにも違うんですね。-O3 も指定したらどうなっちゃうんでしょう。


コンパイル(その二)

気をよくしたので -O3 を付けてもう一度コンパイルしてみます。今度は make menuconfig やら make nconfig で .config も少し調整してみました。make oldconfig も試した気がします。

time KCFLAGS="-O3 -march=native -pipe" KCPPFLAGS="-O3 -march=native -pipe" make -j 8 LLVM=1 bindeb-pkg LOCALVERSION=-zen3o3

ん、コンパイルエラーですね。

llvm-ar が見つからないと言っています。

apt install llvm

ちょっと不本意ですが、Debian 12 標準の llvm-14 をインストールしました。

さて、もう一度コンパイルです。

ld.lld: error: arch/x86/events/amd/power.o: Opaque pointers are only supported in -opaque-pointers mode (Producer: 'LLVM16.0.6' Reader: 'LLVM 14.0.6')
make[9]: *** [scripts/Makefile.build:248: arch/x86/events/amd/power.o] エラー 1
make[9]: *** ファイル 'arch/x86/events/amd/power.o' を削除します
make[8]: *** [scripts/Makefile.build:485: arch/x86/events/amd] エラー 2
make[7]: *** [scripts/Makefile.build:485: arch/x86/events] エラー 2
make[7]: *** 未完了のジョブを待っています....

なんか難しいエラーに遭遇しました。コンパイルしたオブジェクトのリンク処理の際、LLVM 16 で生成したものを LLVM 14 で処理しようとして処理できなかった、ということでしょうか。

確かに、Clang は Clang 16 が使わえていて、readlink コマンドの結果から LLVM 16 のレベルが使わていることが確認できました。

readlink -f /usr/bin/clang
/usr/lib/llvm-16/bin/clang

llvm-ar も update-alternatives でシンボリックリンクを張り直してあげるとこのエラーは回避できそうですが、他にもまだ潜んでいるとトライアンドエラーであと何回もコンパイルしないといけないので途方に暮れそうです。

そう言えば、今回の make 実行時に LTO はどうするかい?と聞かれて yes と応答した気がしました。Link Time Optimization をかけると速くなるらしいので yes としたのですが、これがいけなかったようです。

LTO_NONE=y で再挑戦です。

$ time KCFLAGS="-O3 -march=native -pipe" KCPPFLAGS="-O3 -march=native -pipe" make -j 8 LLVM=1 bindeb-pkg LOCALVERSION=-zen3o3 
  GEN     debian
dpkg-buildpackage --build=binary --no-pre-clean --unsigned-changes -r'fakeroot -u' -a$(cat debian/arch)
dpkg-buildpackage: info: source package linux-upstream
dpkg-buildpackage: info: source version 6.5.10-5
dpkg-buildpackage: info: source distribution faye
dpkg-buildpackage: info: source changed by 
dpkg-architecture: warning: specified GNU system type x86_64-linux-gnu does not match CC system type x86_64-pc-linux-gnu, try setting a correct CC environment variable
dpkg-buildpackage: info: host architecture amd64
 dpkg-source --before-build .
 debian/rules binary
make -f ./Makefile ARCH=x86 	KERNELRELEASE=6.5.10-zen3o3 	KBUILD_BUILD_VERSION=5 	olddefconfig all
  HOSTCC  scripts/basic/fixdep

  INSTALL debian/linux-libc-dev/usr/include
dpkg-deb: building package 'linux-image-6.5.10-zen3o3' in '../linux-image-6.5.10-zen3o3_6.5.10-5_amd64.deb'.
dpkg-deb: building package 'linux-libc-dev' in '../linux-libc-dev_6.5.10-5_amd64.deb'.
dpkg-deb: building package 'linux-headers-6.5.10-zen3o3' in '../linux-headers-6.5.10-zen3o3_6.5.10-5_amd64.deb'.
 dpkg-genbuildinfo --build=binary -O../linux-upstream_6.5.10-5_amd64.buildinfo
 dpkg-genchanges --build=binary -O../linux-upstream_6.5.10-5_amd64.changes
dpkg-genchanges: info: binary-only upload (no source code included)
 dpkg-source --after-build .
dpkg-buildpackage: info: binary-only upload (no source included)

real	48m5.233s
user	316m29.076s
sys	30m9.836s

うまく行きました。なんかコンパイル時間が短くなっています。

linux-headers-6.5.10-zen3o3_6.5.10-5_amd64.deb(9.1MB)

linux-image-6.5.10-zen3o3_6.5.10-5_amd64.deb(609.0MB)

linux-libc-dev_6.5.10-5_amd64.deb(1.3MB)

dbg が生成されなくなったのですが、なんか image がやたらと大きくなってしまいました。これは -O3 を指定したから?

でもまあ、-O3 でコンパイルした kernel ができあがったので、インストールして試してみましょう。インストール完了後に PC を再起動してみます。

ん、速いですが -O2 に比べると違いがあまりわかりません。ただ、アイドル状態の CPU 温度が下がりました。-O2 の状態でもこうだったのかもしれませんが、-O3 の時に気づきました。Firefox で「艦これ」をプレイ中の温度も少し下がった気がします。


コンパイル(その三)

ここまで来るとコンパイルに対する抵抗感がだいぶ薄れてきています。寝る前に make を走らせて翌朝に結果を確認したり、朝に仕事に出かける前に make を走らせて帰宅後に結果を確認したりという生活が続いています。

最終形としてキレイな自前 kernel を作ってみたいということで、もう一度コンパイルに挑戦することにしました。

-O3、-march=native、dbg なし、ちょっとだけ .config を調整(CPU が Ryzen なので Intel microcode なんて読み込みません、とか)しての挑戦です。

いざコンパイル。

  CC      arch/x86/boot/compressed/misc.o
  ZSTD22  arch/x86/boot/compressed/vmlinux.bin.zst
/bin/sh: 1: zstd: not found
make[7]: *** [arch/x86/boot/compressed/Makefile:145: arch/x86/boot/compressed/vmlinux.bin.zst] エラー 127
make[7]: *** ファイル 'arch/x86/boot/compressed/vmlinux.bin.zst' を削除します
make[7]: *** 未完了のジョブを待っています....
  CC [M]  crypto/md4.mod.o
  CC [M]  crypto/rmd160.mod.o

  CC [M]  crypto/authencesn.mod.o
  CC [M]  crypto/lz4.mod.o
make[6]: *** [arch/x86/boot/Makefile:114: arch/x86/boot/compressed/vmlinux] エラー 2
make[5]: *** [arch/x86/Makefile:283: bzImage] エラー 2
make[5]: *** 未完了のジョブを待っています....

そして死亡・・・。

2つ目のビルドで image のファイルサイズが 600MB 超でビックリしてしまったので、もしや kernel の圧縮ルーチンがダメダメなものを選んでしまったのではないかと思い、CONFIG_KERNEL_ZSTD=y を指定してみたのでした。

すると zstd が見つからないというエラーに。

apt install zstd

普通に zstd のパッケージを追加でインストールしなければならなかったようです。libzstd-dev とか lizstd1 とかはインストール済みなんですがね。

そして(恐らく)最後の make コマンドを投入してみます。

$ time KCFLAGS="-O3 -march=native -pipe" KCPPFLAGS="-O3 -march=native -pipe" make -j 8 LLVM=1 bindeb-pkg LOCALVERSION=-zen3o3d 
  GEN     debian
dpkg-buildpackage --build=binary --no-pre-clean --unsigned-changes -r'fakeroot -u' -a$(cat debian/arch)
dpkg-buildpackage: info: source package linux-upstream
dpkg-buildpackage: info: source version 6.5.10-8
dpkg-buildpackage: info: source distribution faye
dpkg-buildpackage: info: source changed by 
dpkg-architecture: warning: specified GNU system type x86_64-linux-gnu does not match CC system type x86_64-pc-linux-gnu, try setting a correct CC environment variable
dpkg-buildpackage: info: host architecture amd64
 dpkg-source --before-build .
 debian/rules binary
make -f ./Makefile ARCH=x86 	KERNELRELEASE=6.5.10-zen3o3d 	KBUILD_BUILD_VERSION=8 	olddefconfig all
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  LEX     scripts/kconfig/lexer.lex.c
  YACC    scripts/kconfig/parser.tab.[ch]
  HOSTCC  scripts/kconfig/menu.o
  HOSTCC  scripts/kconfig/preprocess.o
  HOSTCC  scripts/kconfig/symbol.o
  HOSTCC  scripts/kconfig/util.o
  HOSTCC  scripts/kconfig/lexer.lex.o
  HOSTCC  scripts/kconfig/parser.tab.o
  HOSTLD  scripts/kconfig/conf
#
# No change to .config
#
  GEN     arch/x86/include/generated/asm/orc_hash.h

  INSTALL debian/linux-libc-dev/usr/include
dpkg-deb: building package 'linux-image-6.5.10-zen3o3d' in '../linux-image-6.5.10-zen3o3d_6.5.10-8_amd64.deb'.
dpkg-deb: building package 'linux-libc-dev' in '../linux-libc-dev_6.5.10-8_amd64.deb'.
dpkg-deb: building package 'linux-headers-6.5.10-zen3o3d' in '../linux-headers-6.5.10-zen3o3d_6.5.10-8_amd64.deb'.
 dpkg-genbuildinfo --build=binary -O../linux-upstream_6.5.10-8_amd64.buildinfo
 dpkg-genchanges --build=binary -O../linux-upstream_6.5.10-8_amd64.changes
dpkg-genchanges: info: binary-only upload (no source code included)
 dpkg-source --after-build .
dpkg-buildpackage: info: binary-only upload (no source included)

real	38m30.573s
user	285m5.639s
sys	27m42.752s

できあがったみたいです。

 

linux-headers-6.5.10-zen3o3d_6.5.10-8_amd64.deb(9.1MB)

linux-image-6.5.10-zen3o3d_6.5.10-8_amd64.deb(69.9MB)

linux-libc-dev_6.5.10-8_amd64.deb(1.3MB)

 

うん、image も小さくなってますね。では、PC を再起動してみます。

image が小さくなったからか起動時間が短くなりました。これで完成です。

 

最後に

どこかの誰かが llvm-ar など一式を update-alternatives してくれるスクリプトファイルをシェアしてくれているのを見つけました。これを使えば LTO でのコンパイルができそうです。

junkdog/update-alternatives-clang.sh


ただ、LTO はリンク処理の際にもう一度コンパイルするようなので、ビルドが完了するまでの時間も長くなりそうです。

ひとまず満足できる自前 kernel を生成できたので、LTO はまたの機会に試してみようかと思います。