kgdbによるkernelデバッグ(2)
前回に引き続いてkgdbによるLinux kerenlのデバッグです。
実際にアタッチするのと、kernel moduleのシンボルの解決について記します。
アタッチ
remoteからのアタッチになるので、gdb実行側で、kernelのシンボル情報を読み込ませる必要があります。
vmlinuxファイルに入っているので、前回展開したものをgdbに読み込ませます。
その後、シリアル接続の情報を設定します。
# gdb vmlinux
(gdb) set remotebaud 115200
targetとなる端末では、アタッチするためにkerenlを一旦止める必要があります。目当ての動作中のデバッグをするには、breakpointを貼って、breakpointで止まったところを見る必要があります。
止めるには以下のコマンドを実行するか、ブートオプションのkgdbwaitを使います。
# echo g > /proc/sysrq-trigger
この状態で、gdbで
(gdb) target remote /dev/ttyS0
というようにシリアルからアタッチ出来ると思います。あとはいつものgdbです。continueコマンドでkernel実行を継続すれば、止めてたshellも復帰します。
kernel moduleのデバッグ
この状態だと、kernel coreのシンボル解決は出来ますが、moduleについては情報がないため、デバッグ出来ない状態です。
moduleのアドレス情報は起動ごとに異なるので、まずはターゲット上で目当てのmodule情報を収集します。
moduleの各セクションのアドレス情報は、/sys/modue/
取得したアドレス情報はgdbに登録します。gdbのヘルプを見ると、
(gdb) help add-symbol-file Load symbols from FILE, assuming FILE has been dynamically loaded. Usage: add-symbol-file FILE [-readnow | -readnever] [-o OFF] [ADDR] [-s SECT-NAME SECT-ADDR]... ADDR is the starting address of the file's text. Each '-s' argument provides a section name and address, and should be specified if the data and bss segments are not contiguous with the text. SECT-NAME is a section name to be loaded at SECT-ADDR. OFF is an optional offset which is added to the default load addresses of all sections for which no other address was specified.
とあるので、textのアドレスとそれ以外のアドレスを入れる感じですね。 これを手で入れるのは面倒なので、スクリプトを作成します。
(print-section.sh ターゲットに配置)
#!/bin/sh
cd /sys/module/$1/sections/
if [ $? -ne 0 ]; then
echo "cannot found module"
exit 1
fi
addr=`cat .text`
echo -n "$addr"
for A in .*
do
if [ "$A" = "." ]; then continue; fi
if [ "$A" = ".." ]; then continue; fi
if [ "$A" = ".text" ]; then continue; fi
addr=`cat $A`
echo -n " -s $A $addr"
done
echo
(kgdb-attach.sh リモートに配置)
#/bin/bash
gdbinit=".gdbinit"
dirdriver="lib/modules/4.16.7_D1+/kernel/drivers/"
vmlinux="boot/vmlinux-4.16.75_D1+"
declare -A modules
modules=(
["mod1"]="${dirdriver}/.../mod1.ko"
["mod2"]="${dirdriver}/.../mod2.ko"
)
echo "" > ${gdbinit}
echo "file $vmlinux" >> ${gdbinit}
echo "set remotebaud 115200" >> ${gdbinit}
for m in ${!modules[@]}; do
section="`ssh root@192.168.100.1 bash ./print-section.sh ${m}`"
echo "add-symbol-file ${modules[$m]} $section" >> ${gdbinit}
done
gdb
echo "" > ${gdbinit}
kgdb-attach.shの配列にデバッグしたい、もしくは関連するモジュールを入れて実行すると、アドレス情報を取ってきてgdbを実行します。
gdbの自動設定はgdbinitを使用しています。生成される.gdbinitの場所次第で警告が出ると思いますが、これを解消するには、homeディレクトリに、
# vi ~/.gdbinit
add-auto-load-safe-path /.../.gdbinit
というようにすれば大丈夫だと思います。
これでkerenl moduleに対してもデバッグできるようになりました。
kernelの開発だけでなく、kerenlの動きの理解や、ユーザランドのアプリの検証などにも使えると思うので気軽にkernelにアタッチしてみましょう!
最近はシリアルポートがない端末も増えてきてるので、ethernet経由でのデバッグもやってみようと思います。