関連記事
概要
最近のLinuxカーネルでは、デフォルトで最適化オプションが付与されている。
そのため、kgdbやその他のデバッグツールを利用する際に、不都合が生じることがあり、デバッグの間だけが無効化したいことがある。
そこで、本記事では、Makefileを書き換える方法と関数にattributeを付与する方法により最適化を無効化して、kgdbで変数の値を確認する方法をまとめた。
はじめに
前回、VirtualBoxの仮想マシン同士でkgdbでデバッグできるようにした。
kgdbを使えば、指定した関数内の変数を参照したり、ステップ実行でコードフローを追っていくことができるのでソースコードを読むときにも役立つ。
しかし、Linuxカーネルはビルド時にデフォルトで最適化オプションを付与しているので、kgdbでソースコードリーディングをするにはいくつかの問題点がある。 そこで、kgdbでも比較的簡単にソースコードリーディングができるようにいくつかの施策をする。
現状の問題点
gdbを使用した人ならわかるが、最適化オプションを付与したオブジェクトファイルをgdbにかけると、以下の問題がある。
- 一部の変数が
と表示され参照できなくなる - ステップ実行とソースコードが対応しなくなる
そこで、ソースコードリーディングをしている間はカーネルのビルド時に最適化オプションを無効にしたい(-O0
)のだが、どうやらLinuxカーネルは-O0
でコンパイルできないらしい。
The kernel will not run with -O0, sorry, just live with the build optimization levels that is currently used and you should be fine.
Yes, it doesn't work :) How to compile Linux kernel with -O0 flag
そのため、別の方法で変数などを確認する必要がある。
ファイル単位で最適化を無効化する
ファイル単位で無効化する場合は、該当するMakefile
に下記の内容を追記することでできる。
CFLAGS_(オブジェクトファイル名) = コンパイルオプション
例えば、fs/fat/dir.c
の最適化のみ無効にする場合、fs/fat/Makefile
を下記のように修正する。
# SPDX-License-Identifier: GPL-2.0 # # Makefile for the Linux fat filesystem support. # obj-$(CONFIG_FAT_FS) += fat.o obj-$(CONFIG_VFAT_FS) += vfat.o obj-$(CONFIG_MSDOS_FS) += msdos.o CFALGS_dir.o = -O0 # 最適化を無効化するために追加した行 fat-y := cache.o dir.o fatent.o file.o inode.o misc.o nfs.o vfat-y := namei_vfat.o msdos-y := namei_msdos.o
Makefile
を修正後、再ビルドをして対象マシンに再インストールする。
関数単位で最適化を無効化する
関数単位で無効化する場合は、該当するソースコードを修正する必要がある。 最適化を無効にしたい関数に対して、下記の修正をすればよい。
型 __attribute__((optimize("コンパイルオプション"))) 関数名(引数)
例えば、ext4_readpages()
の最適化のみ無効にする場合、下記のように記述する。
// 3365: static int __attribute__((optimize("O0"))) ext4_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages)
ソースコードを修正後、再ビルドをして対象マシンに再インストールする。
おわりに
kgdbでカーネルのソースコードを読むために必要最低限、変数の値やフローを追うための準備をした。 まずファイル単位で最適化オプションを無効化する方法は、再ビルドが必要になるが、容易に解決できるためオススメである。
変更履歴
- 2019/11/10: 記事公開
- 2022/06/05: デザイン修正
参考
- https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
- LinuxカーネルをCLionでデバッグする - knqyf263's blog
- How to compile Linux kernel with -O0 flag
- debugging - How to avoid "optimized out" when debug linux kernel - Stack Overflow
- Debugging with GDB - ソース・ファイルの検査
- Debugging kernel and modules via gdb — The Linux Kernel documentation
補足
kgdbで特定の変数を参照したいだけであれば、ビルド最適化を必ずしも無効化する必要はない。
マシン命令に変換してレジスタを表示する
関数に対応するマシン命令とレジスタにより、特定の変数を確認することはできる。
これは、デバッグ時にも有効な手段で、指定範囲をマシン命令や、アセンブリコードを出力することでフローを追っていくことができる。
アセンブリコードの出力にはdisassemble
コマンド、命令単位で実行するにはsi
コマンドを実行していく。
その他のコマンドも含めてgdbの使い方をまとめてあるサイトはたくさん見つかるので一度目を通しておくことを推奨する。
例えば、fat_parse_short()
をマシン命令単位の実行フローを見てみる。
また、レジスタから引数のunsigned char *name
を参照する。
ここに関しては、プロセッサ毎のレジスタ規則を把握している必要がある。*1
これらの結果から、fat_parse_short()
ではA
というファイルのエントリをパースしていたようだ。
実際に、対象マシンの方の実行結果を見てみても同様のことがわかる。
vagrant@ubuntu-bionic:~$ ls /mnt A