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//sections/以下にあります。ここから各セクションの情報を取得します。
取得したアドレス情報は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経由でのデバッグもやってみようと思います。