<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Mikan-Book on tomiy's blog</title><link>https://tomiylab.com/tag/mikan-book/</link><description>Recent content in Mikan-Book on tomiy's blog</description><generator>Hugo</generator><language>ja-JP</language><lastBuildDate>Tue, 07 Dec 2021 23:14:55 +0900</lastBuildDate><atom:link href="https://tomiylab.com/tag/mikan-book/index.xml" rel="self" type="application/rss+xml"/><item><title>mikanOSのデバッグの話</title><link>https://tomiylab.com/2021/12/debug-mikanos/</link><pubDate>Tue, 07 Dec 2021 23:12:00 +0900</pubDate><guid>https://tomiylab.com/2021/12/debug-mikanos/</guid><description>&lt;meta charset="utf-8"&gt;
&lt;title&gt;mikanosのデバッグの話&lt;/title&gt;
&lt;h2&gt;mikanOSのデバッグの話&lt;/h2&gt;
この記事は &lt;a href="https://adventar.org/calendars/6581"&gt;自作OS Advent Calendar 2021&lt;/a&gt;の「1桁の最大の素数」日目の記事として作成されました。
&lt;h3 id="whoami"&gt;whoami&lt;/h3&gt;
サイボウズ・ラボユース第11期生のtomiyです。
ラボユースの活動として内田公太さんご指導のもとmikanOSをいじっています。
その過程で得られたqemu上で動作する自作OSのカーネルやアプリをデバッグする方法等について僕がやらかした話も交えながら書きたいと思います。
この記事ではプロンプトは
&lt;pre&gt;&lt;code class="bash"&gt;&lt;/code&gt;# ターミナル
$ hoge
# gdbのシェル
(gdb) hoge
# qemuモニター
(qemu) hoge&lt;/pre&gt;
のように表記します。
&lt;h3&gt;デバッグのための準備&lt;/h3&gt;
最初にデバッグ対象の最適レベルをを-Ogにしましょう。 カーネルのデバッグであればmikanos/kernel/MakefileのCFLAGSとCXXFLAGSの-O2を -Ogにすればいいです。 もし最適レベルをを-Ogにすると問題が再現しなくなるならもとに戻すべきですが、 デバッグ時は最適化レベルを落としましょう。 最適化レベルが高いとソースコードとアセンブリ言語の対応関係がわかりにくかったり、 変数が見えなくなったりしてデバッグしづらくなります。
tmuxでターミナルを分割して左にqemu右にgdbというのが気に入っています。 これだとすぐにmikanOSを再起動できますし、bashのコマンドが使いたくなったら 右のペインを上下に分割してそこからbashのコマンドが使えます。 ウィンドウを何枚も開いてマウスでウィンドウ間を移動するのは面倒ですよね。
デバッガーはgdbを使用します。
mikanOSはClangでコンパイルされているのでlldbを使うべきかも知れませんが、
Clangをqemuに接続する方法がわからなかったのでgdbを使うことにしました。
gdbをqumuに接続する方法についてはこちらの記事を参照して下さい。
https://tomiylab.com/2021/09/gdb-mikanos/
↑ではgdbを起動するたびにqemuに接続するコマンドを打って、毎回同じ箇所にブレークポイントを貼ったり、逆アセンブル結果の表示設定やfileコマンででのkernel.elfの読み込みを手動で行なってますが、面倒なため自動化します。
gdbには起動時に-xオプションでファイルからgdbのコマンドを読み込んで実行する機能があるのでこれを利用します。
mikanosディレクトリに適当な名前のファイル(僕はgdb_initにしました)を作成してそこにコマンドを書き込みます。
先頭が#で始まる行はコメントです。行の途中に#を入れても、それ以降がコメントと解釈されないので注意してください。
&lt;pre&gt;&lt;code class="bash"&gt;# qemuに接続
target remote localhost:1234
file ./kernel/kernel.elf
# 逆アセンブル結果をintel形式に
set disassembly-flavor intel
# ブレークするごとに逆アセンブル結果を表示
disp/3i $pc&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;gdbのpコマンドxコマンドについて&lt;/h3&gt;
pコマンド: printの略 変数の値、数値や文字を表示
使用例: 数値を16進数に変換
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) p/x 307200&lt;/code&gt;&lt;/pre&gt;
使用例: 変数の表示
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) p hoge&lt;/code&gt;&lt;/pre&gt;
使用例: rspレジスタの値を表示
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) p $rsp&lt;/code&gt;&lt;/pre&gt;
レジスタ名の前には$をつける。
xコマンド: メモリダンプの表示
使用例: 0x10000から8Byte * 10だけ16進数としてダンプ
&lt;pre&gt;&lt;code class="bash"&gt;(gdb)x/10gx 0x10000&lt;/code&gt;&lt;/pre&gt;
pコマンドの出力フォーマットはhttps://flex.phys.tohoku.ac.jp/texi/gdb-j/gdb-j_40.html を参照
xコマンドの出力フォーマットはhttps://flex.phys.tohoku.ac.jp/texi/gdb-j/gdb-j_41.html を参照
pコマンドやxコマンドの引数にはC言語でいう式が入る
つまり四則演算できる
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) p/x 307200 + 0x10 
(gdb) x/3gx 0x8000000 + 0x1d8&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;間接参照演算子*について&lt;/h3&gt;
gdbのコマンドではC言語のように間接参照演算子*がつかえる。たとえば
&lt;pre&gt;&lt;code class="c"&gt;int hoge = 5;
int *phoge = &amp;amp;hoge;&lt;/code&gt;&lt;/pre&gt;
があったとき
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) p hoge
0x8000031
(gdb) p *hoge
5&lt;/code&gt;&lt;/pre&gt;
のようになる。 また、
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) p *0x8000031
5&lt;/code&gt;&lt;/pre&gt;
のようにも書ける。
&lt;h3&gt;僕がよく使うgdbコマンド&lt;/h3&gt;
バックトレースの表示
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) bt&lt;/code&gt;&lt;/pre&gt;
フレームの指定(フレーム番号は↑で確認)
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) frame 2&lt;/code&gt;&lt;/pre&gt;
現在のフレームのローカル変数を確認
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) info locals&lt;/code&gt;&lt;/pre&gt;
現在停止している周辺のソースコードを表示
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) list&lt;/code&gt;&lt;/pre&gt;
ブレークポイント一覧を表示
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) info breakpoints&lt;/code&gt;&lt;/pre&gt;
ブレークポイント削除(ブレークポイント番号は↑で確認)
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) delete 3&lt;/code&gt;&lt;/pre&gt;
qemuモニターのコマンドだけど、レジスタの値を確認
&lt;pre&gt;&lt;code class="bash"&gt;(qemu) info registers&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;16進数計算のテクニック？&lt;/h3&gt;
自明なので証明は省略するがn進数の数においてn^mを{かける|割る}とき、 m桁だけ{左|右}にシフトすれば良い。
例えばn = 10, m = 5のとき42に10^5をかけると4200000となる。
これを使用すると計算が桁をシフトするだけですむ。
１ページ4KiB=4 * 2^10 Bのときページ数からアドレスの大きさ？に直したり、アドレスの大きさ？からページ数に直したりするとき、
16進数に4 * 2^10=4096を掛けたり割ったりする機会は多い。
4 * 2^10 = 2^12 = 16^3なので16進数に4096を{かける|割る}とき、
3桁だけ{左|右}にシフトすれば良い。
ついでに２進数に4096を{かける|割る}とき、12桁だけ{左|右}にシフトすれば良い。
&lt;h3&gt;逆アセンブル&lt;/h3&gt;
&lt;pre&gt;&lt;code class="bash"&gt;$ objdump -C -M intel -d kernel.elf &amp;gt; kernel.dis&lt;/code&gt;&lt;/pre&gt;
逆アセンブルを行うにはobjdumpコマンドを使用します。詳しい仕様については
&lt;pre&gt;&lt;code class="bash"&gt;$ man objdump&lt;/code&gt;&lt;/pre&gt;
で調べて下さい。
ここでのオプションの意味は-Cがオブジェクト名をデマングル-M itelがアセンブラの形式をintel形式に-dが逆アセンブル(disasemmble)です。結果は標準出力に出力されるのでファイルにリダイレクトします。
結果をファイルに保存すると10MBを超えることがあるので注意して下さい。
LinuxのVSCodeではサイズがデカすぎて開けないのでVimで開きましょう。
Linuxコマンドに慣れている方はファイルに保存しなくても
&lt;pre&gt;&lt;code class="bash"&gt;$ objdump -C -M intel -d kernel.elf | less&lt;/code&gt;&lt;/pre&gt;
で十分かも知れません。
&lt;h3&gt;CPU例外のデバッグ&lt;/h3&gt;
ページフォルト(PF)や一般保護例外(GP)等のCPU例外のデバッグは例外ハンドラーにブレークポイントを設置するのが有効です。
しかし、gdbで
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) b IntHandlerPF&lt;/code&gt;&lt;/pre&gt;
のようにブレークポイントを設置するとgdbが気を使って例外ハンドラーの開始より少し後ろにブレークポイントが貼られてしまいます。関数呼び出し直後には
&lt;pre&gt;&lt;code class="x86asm"&gt;push rax
push rbp
mov rbp,rsp
push rax&lt;/code&gt;&lt;/pre&gt;
のような処理を行うのでこの処理の後にブレークポイントが貼られます。
しかし、これでは困ります。
&lt;pre&gt;&lt;code class="bash"&gt;$ nm -C kernel.elf | grep IntHandlerPF&lt;/code&gt;&lt;/pre&gt;
で例外ハンドラのアドレスを調べて
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) b *0x134420&lt;/code&gt;&lt;/pre&gt;
のようにしてもいいですが、カーネルに変更を加えるとこのアドレスがずれてしまうことがあります。
例外ハンドラにブレークポイントを貼っておくとデバッグに便利なので常にブレークポイントを貼っておきたいですが、
gdb_initにアドレスを書き込むとアドレスがずれてしまったときにブレークポイントが正しく貼れなくなってしまいます。
そこでシェルスクリプトを使ってgdb_initを動的に生成することで解決できました。
&lt;pre&gt;&lt;code class="bash"&gt;rm -rf ./gdb_init
echo “# qemuに接続
target remote localhost:1234
file ./kernel/kernel.elf
# 逆アセンブル結果をintel形式に
set disassembly-flavor intel
# ブレークするごとに逆アセンブル結果を表示
disp/3i $pc ”&amp;gt;&amp;gt; ./gdb_init
echo “# IntHandlerPF” &amp;gt;&amp;gt; ./gdb_init
nm -C ./kernel/kernel.elf | grep IntHandlerPF | awk ’{printf “b *0x%s”, $1}’ &amp;gt;&amp;gt; ./gdb_init 
echo “” &amp;gt;&amp;gt; ./gdb_init
echo “# IntHandlerGP” &amp;gt;&amp;gt; ./gdb_init
nm -C ./kernel/kernel.elf | grep IntHandlerGP | awk ’{printf “b *0x%s”, $1}’ &amp;gt;&amp;gt; ./gdb_init 
echo “” &amp;gt;&amp;gt; ./gdb_init
echo “# IntHandlerUD” &amp;gt;&amp;gt; ./gdb_init
nm -C ./kernel/kernel.elf | grep IntHandlerUD | awk ’{printf “b *0x%s”, $1}’ &amp;gt;&amp;gt; ./gdb_init 
echo “” &amp;gt;&amp;gt; ./gdb_init
echo “# IntHandlerBP” &amp;gt;&amp;gt; ./gdb_init
nm -C ./kernel/kernel.elf | grep IntHandlerBP | awk ’{printf “b *0x%s”, $1}’ &amp;gt;&amp;gt; ./gdb_init 
echo “” &amp;gt;&amp;gt; ./gdb_init
gdb -x gdb_init&lt;/code&gt;&lt;/pre&gt;
これで常に例外ハンドラーにブレークポイントを貼れるようになりました。 CPU例外のデバッグ準備完了です。
qemuを起動した状態で
&lt;pre&gt;&lt;code class="bash"&gt;$ ./gdb.sh&lt;/code&gt;&lt;/pre&gt;
でgdbをqemuに接続してカーネルの読み込み、ブレークポイントの設置まで自動でやってくれます。
PFの具体的なデバッグ方法を紹介します。
例外ハンドラは特殊な関数なのでバックトレースが使えません。
例外ハンドラでブレークしたらまずすることはスタックの確認です。
スタックを確認するには
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) x/6gx $rsp&lt;/code&gt;&lt;/pre&gt;
です。
rspレジスタの指すアドレスから6領域をジャイアント(8byte)単位*6で16進数でダンプしています。
これが何を意味しているのかはIntel SDM Vol.3 6.14 のFigure 6-9右側をみればわかります。
Intel SDMはOS自作erのバイブルなので必ずダウンロードしておきましょう！
実際の事例を紹介します。 アプリケーション実行中にページフォルトが起きてIntHandlerPFでブレークしました。
そこでスタックを確認すると
&lt;pre&gt;&lt;code class="bash"&gt;&lt;/code&gt;(gdb) x/6gx $rsp
0x8fd0: 0x0000000000000005 0x000000000004b765
0x8fe0: 0x0000000000000023 0x0000000000000246
0x8ff0: 0xffffffffffffeed8 0x000000000000001b&lt;/pre&gt;
となりました。
Intel SDM Vol.3 6.14 のFigure 6-9右側をみると
Error Code: 0x5
RIP: 0x4b765
CS: 0x23
RFLAGS: 0x246
RSP: 0xffffffffffffeed8
SS: 0x1bとなります。
ここでエラーコードの意味を調べてみましょう。
PFのエラーコードの意味はIntel SDM Vol.3 6.15 のFigure 6-11にあります。
0x5を2進数になおすとb101なので
エラーコード: 0x5
bit0:1 ページは存在してる
bit1:0 読み込みでPFが発生
bit2:1 ユーザーモードで発生
と解釈できます。アプリケーション実行中にPFが発生したので 確かにユーザーモードで発生したのであっていそうです。
これだけでは原因がわからないのでつぎは スタックにあるRIPを確認します。
あれ？アプリが0x4b765なんてアドレスに配置されてるわけないのにおかしいな。
とりあえず、0x4b765をダンプ
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) x/6gx 0x4b765
0x4b765: 0x0000000000000000 0x0000000000000000
0x4b775: 0x0000000000000000 0x0000000000000000
0x4b785: 0x0000000000000000 0x0000000000000000&lt;/code&gt;&lt;/pre&gt;
おかしい。
これならRIPが0x4b765になった時点でPFじゃなくてUDがおきるはず！
つまり、例外ハンドラーが呼ばれたときにメモリが壊れた。
ここから推測できるのは、
アプリ側で本来飛ぶべきじゃない場所に飛んでしまってる(RIPがありえない値だから)
その飛んだ先がたまたま機械語命令として解釈できた(UDじゃないから)
その機械語命令がカーネルモードじゃないと読めない領域を読もうとしてPF
とりあえず、アプリ呼び出し直前あたりでブレークして0x4b765をダンプしました。
記録が残ってなかったので以下は似た状況を再現したものです。
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) x/6gx 0x4b765
0x4b765: 0x0000000000012005 0x0000000000013005
0x4b775: 0x0000000000014005 0x0000000000015005
0x4b785: 0x0000000000016005 0x0000000000017005&lt;/code&gt;&lt;/pre&gt;
どうやらページテーブルっぽいです。
このとき僕はこの場所に飛んでしまう原因を調べるためにアプリをステップ実行しました。
この方法ではとんでもなく時間がかかりますが、このとき動かしていたアプリはhelloで、PFが起きるのがhelloが表示される前だったため
アプリ起動直後に異常がある可能性が高いと考えていました。なのでこの方法でもそんなに手間はかからないと考えてこの方法を使いました。
そしてこの場所にcll or jmpするす場所を特定できました。
これが使えない場合であれば、後で説明するint3命令をアプリに何箇所か入れてBP例外(ブレークポイント例外)を発生させ問題箇所を大まかに特定してからステップ実行したと思います。
0x4b765をcallしていた箇所は
&lt;pre&gt;&lt;code class="x86asm"&gt;call QWORD PTR [rbx+0x10]&lt;/code&gt;&lt;/pre&gt;
でrbx+0x10は0x80001d8でした。
0x80001d8はelfファイルから読み込んだものが置かれていた領域です。
ダンプしてみると
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) x/6gx 0x80001d8
0x80001d8: 0x000000000004b765 0x000000000004c0c5
0x80001e8: 0x0000000000000025 0x000000000004d775
0x80001f8: 0x000000000004e0cd 0x0000000000000025&lt;/code&gt;&lt;/pre&gt;
でした。
アプリを逆アセンブルしてみると、0x80001d8は本来0x8078760が書かれているはずだとわかりました。
0x4b765,0x4c0c5,0x4d775という増え方はページテーブルのエントリーっぽいです。
つまりPFの原因はelfファイルが読み込まれた領域にOSがページテーブルのエントリーを書き込んでしまってメモリ破壊が起きたことだと推測されます。
ここで0x80001d8に0x4b765を書き込んだ犯人を特定してこれが正しいのか検証してみましょう。
これにはwatchポイントを使用します。
&lt;h3&gt;watchポイント&lt;/h3&gt;
watchポイントは変数や指定したメモリ領域に読み込み/書き込みがあったときにブレークする機能です。 メモリ破壊が起こっている可能性があるときにメモリを破壊している犯人を探すのに役に立ちます。
最初にelfファイルをメモリに読み込んだ直後でブレークしてその後0x80001d8 にwatchポイントを仕込みます。
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) watch ＊0x80001d8&lt;/code&gt;&lt;/pre&gt;
すると
&lt;pre&gt;&lt;code class="bash"&gt;Old value = 134711136
New value = 309093
PageMapEntry::SetPointer (this=0x80001d8, p=0x4b000) at ./paging.hpp:89
89 }
1: x/3i $pc
=&amp;gt; 0x136564 &lt;pagemapentry::setpointer(pagemapentry*)+36&gt;: pop rbp
 0x136565 &lt;pagemapentry::setpointer(pagemapentry*)+37&gt;: ret 
 0x136566: int3
(gdb) p/x 134711136
$1 = 0x8078760
(gdb) p/x 309093
$2 = 4b765&lt;/pagemapentry::setpointer(pagemapentry*)+37&gt;&lt;/pagemapentry::setpointer(pagemapentry*)+36&gt;&lt;/code&gt;&lt;/pre&gt;
pop rbpはメモリに値を書き込んでいないのでおかしい？
watchポイントはメモリ値を書き込んだ次の命令でブレークするからlistコマンドで 周辺のソースコードを表示すればok
これで0x80001d8に0x4b765を書き込んだ犯人が特定できました。
たしかにページングのページエントリーを書き込む命令でした。
ちなみに、なぜelfファイルを読み込んだ領域にページングのページエントリーを書き込まれるのかは原因がわかっていないです。
現在調査中です。
&lt;h3&gt;asm(“int3”)&lt;/h3&gt;
ブレークポイント例外を発生させるCPU命令がint3です。 C言語やC++からインラインアセンブラで呼び出せます。
&lt;h3&gt;while(1) asm(“hlt”)&lt;/h3&gt;
例外ハンドラの設定前でasm(“int3”)が使えない、コンソールの描画も終わってなくてputString()も使えない状況でデバッグに使えるのがwhile(1) asm(“hlt”)です。
hlt命令はカーネルモードでしか使えないので、アプリの動作中にシステムコールでカーネルモードに移っているときか、カーネル内でしか使えません。
hlt命令はCPUの動作を停止させるCPU命令ですが、割り込み等によって停止状態が解除されてしまいます。
そのため、割り込みが入ってもそのタスクが停止し続けるためには無限ループの中に入れる必要があります。
&lt;h3&gt;階層ページング構造を辿る&lt;/h3&gt;
ページの設定を確認したい場合や仮想アドレスが どの物理アドレスに割り当てられているか調べたい場合等に階層ページング構造を確認したい場面があります。
4階層ページングについてはみかん本19.4を参照してください。
階層ページング構造を調べる方法
１. qemu monitorでinfo tlb
&lt;pre&gt;&lt;code class="bash"&gt;(qemu) info tlb&lt;/code&gt;&lt;/pre&gt;
2. CR3からたどる
&lt;pre&gt;&lt;code class="bash"&gt;(qemu) info registers
CR3=hoge
(qemu) x/4gx hoge
…&lt;/code&gt;&lt;/pre&gt;
1. はqemu上だからlessコマンドがつかえなくて辛く
件数が多くなりすぎて目的のアドレスを探し出すのが困難なので2.を使います。
&lt;p&gt;仮想アドレスから各階層の配列の添字を調べるにはosdev.jpのツールが便利です。
&lt;a href="https://osdev.jp/tools/x86_64_vaddr_composer.html"&gt;https://osdev.jp/tools/x86_64_vaddr_composer.html&lt;/a&gt;
ここでは0x8000000がどの物理アドレスに割り当てられているかと、ページの設定を確認してみます。
0x8000000を分解すると
PML4 0x000=0
PDP 0x000=0
PD 0x040=64
PT 0x000=0
Offset 0x000=0
となります。
CR3を調べてPML4の先頭アドレスを調べます。&lt;/p&gt;</description></item><item><title>qemu上のmikanOSをgdbでデバッグする方法</title><link>https://tomiylab.com/2021/09/gdb-mikanos/</link><pubDate>Wed, 22 Sep 2021 15:56:44 +0900</pubDate><guid>https://tomiylab.com/2021/09/gdb-mikanos/</guid><description>&lt;p&gt;qemu上のmikanOSにgdbを接続してデバッグする方法を備忘録として残しておきます&lt;/p&gt;
&lt;h2&gt;qemu上のmikanOSをgdbでデバッグする方法&lt;/h2&gt;
&lt;h3&gt;gdbをqemuに接続&lt;/h3&gt;
gdbを接続するためにqemuの起動オプションに-s -Sをつけます。
-sは-gdb tcp::1234の意味は意味です。
-Sはデバッガからコマンドを受け取るまでCPUを起動させないオプションです。
mikanos/build.shからmikanOSを起動している場合osbook/devenv/run_image.shでqemuを
起動しているのでrun_image.shを編集してください。
&lt;p&gt;mikanOSを起動したらgdbを起動してqemuに接続します。
以下シェルのプロンプトを&amp;rsquo;$&amp;rsquo;、gdbのプロンプトを&amp;rsquo;(gdb)&amp;lsquo;として表示します。&lt;/p&gt;
&lt;pre&gt;&lt;code class="bash"&gt;$ gdb
(gdb) target remote localhost:1234&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;gdbの表示設定&lt;/h3&gt;
逆アセンブル結果をintel形式に設定
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) set disassembly-flavor intel&lt;/code&gt;&lt;/pre&gt;
ブレークするごとにその後の5命令を表示するように設定
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) disp/5i $pc&lt;/code&gt;&lt;/pre&gt;
ブレークするごとにフラグレジスタの値を表紙するよに設定
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) disp $eflags&lt;/code&gt;&lt;/pre&gt;
その他目的に応じてお好みの表示設定をしてください
&lt;h3&gt;ブレークポイントの設置&lt;/h3&gt;
まず、ブレークポイントを関数名、シンボル名で設置できるようにkernel.elfをgdbに読み込みます。
アプリのデバッグをしたい場合はkernel.elfではなくそのアプリのelfファイルを読み込みます。
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) file /path/to/kernel.elf&lt;/code&gt;&lt;/pre&gt;
以下のように聞かれるのでyと入力してください。
&lt;pre&gt;&lt;code class="bash"&gt;A program is beging debagged already.
Are you sure you want to change the file? (y or n)&lt;/code&gt;&lt;/pre&gt;
ブレークポイントを設置するのは'b'コマンドです。
hogeにはシンボル名や関数名を入れます。
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) b hoge&lt;/code&gt;&lt;/pre&gt;
アドレスを直接指定してブレークポイントを設置する場合は以下のようにします。
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) b *0x88888&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;デバッグ&lt;/h3&gt;
ブレークポイントを設置したらqemuを動かします。
&lt;pre&gt;&lt;code class="bash"&gt;(gdb) c&lt;/code&gt;&lt;/pre&gt;
cはcontinueの短縮形です。次のブレークポイントまでqemuを動かします。
&lt;p&gt;ステップ実行したい場合はsiコマンドを実行します。&lt;/p&gt;</description></item></channel></rss>