土日の勉強ノート

AI、機械学習、最適化、Pythonなどについて、技術調査、技術書の理解した内容、ソフトウェア/ツール作成について書いていきます

QEMUに似たRenodeでSTM32をGDBを使ってデバッグする

前回から、Renode という QEMU に似たオープンソースのエミュレータを試してます。

前回は、インストールとSTM32のサンプルソースの実行までをやりました。

今回は、GDB で接続して、デバッグしてみます。

それでは、やっていきます。

参考文献

GDBハンドブック

GDBハンドブック

Amazon

はじめに

「QEMUを動かす」の記事一覧です。良かったら参考にしてください。

QEMUを動かすの記事一覧

まず、Renode の公式サイトは以下です。

https://renode.io/

Renode の公式のドキュメントは以下です。

https://renode.readthedocs.io/en/latest/

また、GitHub は以下です。

https://github.com/renode/renode

今回は、Renode を GDB を使って動かしていきます。

STM32のサンプルソースをデバッグする

それでは、早速、前回 の続きで、STM32F4 の Discovery を対象として Renode を起動します。

前回 とは異なり、Renode モニターで start はしません。Renode のドキュメントの GDB でデバッグ を見ると、Renode は、3種類のエミュレーションの開始方法が用意されていると書かれています。

1つ目は Renode から start で開始する方法で、2つ目は GDB から monitor start で開始する方法で、3つ目は GDB から接続されると自動的に開始する方法です。

今回は、2つ目の GDB から monitor start と入力して開始する方法でやってみたいと思います。

$ ./renode renode-config.resc

別のコンソールを立ち上げて、GDB を起動していきます。

$ arm-none-eabi-gdb renode-example.elf 
(gdb) target remote :3333
reset_handler () at ../../cm3/vector.c:67
67     for (src = &_data_loadaddr, dest = &_data;
(gdb) c

GDB で接続すると、エントリポイントで停止した状態になりました。c(continue)で実行を開始すると、「hello world!」が出力されました。

GDBから操作できました
GDBから操作できました

ですが、このエミュレーションの開始は、ドキュメントに書かれている、3つ目の方法のような挙動です。

renode-config.resc ファイルには、以下のようになっています。

machine StartGdbServer 3333

Renode のドキュメントには、3つ目の方法にするには、以下のようにするとあります。

machine StartGdbServer 3333 true

もしかすると、以前は、デフォルトが false だったけど、今は、デフォルトが true になったとかかもしれません。

とにかく、GDB で接続ができました。

以前作ったELFファイルでデバッグする

以前、Interface 2022年 7月号 のルーレットゲームのサンプルソースを使って、QEMU で動かしました。

この ELFファイルを使って、Renode で動かしていきたいと思います。

まずは、Renode のディレクトリに、以前に作った ELFファイルのシンボリックリンクを作ります。1つ目が普通の ELFファイルで、2つ目は、一度バイナリファイルに変換して、シンボル情報などのデバッグ情報を削除して、ELFファイルに再構築したファイルです。

$ ln -s ~/eclipse-workspace/stm32f4discovery_sample/Debug/stm32f4discovery_sample.elf
$ ln -s ~/eclipse-workspace/stm32f4discovery_sample/Debug/stm32f4discovery_sample_bin2elf_entry.elf

普通のELFファイルを動かしてみる

では、まずは、普通のELFファイルの方を、手動で Renode を起動していきます。

$ renode
Gtk-Message: 21:29:43.731: Failed to load module "canberra-gtk-module"
21:29:44.6876 [INFO] Loaded monitor commands from: /home/daisuke/svn_/renode/renode_1.15.0_portable/scripts/monitor.py
(monitor) mach create
21:30:27.2465 [INFO] System bus created.
(machine-0) machine LoadPlatformDescription @platforms/boards/stm32f4_discovery-kit.repl
21:30:49.7163 [INFO] Reading cache
21:30:50.1987 [INFO] sysbus: Loaded SVD: /tmp/renode-10320/b9a14342-eb23-457c-9175-699febb364f2.tmp. Name: STM32F40x. Description: STM32F40x.
(machine-0) sysbus LoadELF @stm32f4discovery_sample.elf
21:31:19.4952 [INFO] sysbus: Loading segment of 11000 bytes length at 0x8000000.
21:31:19.5196 [INFO] sysbus: Loading segment of 360 bytes length at 0x8002AF8.
21:31:19.5199 [INFO] sysbus: Loading segment of 256 bytes length at 0x2001F700.
(machine-0) machine StartGdbServer 3333
21:31:38.0812 [INFO] machine-0: GDB server with all CPUs started on port :3333

問題なく起動できていそうです。続いて、GDB で接続します。

$ arm-none-eabi-gdb stm32f4discovery_sample.elf
(gdb) target remote :3333
Remote debugging using :3333
Reset_Handler () at ../startup.S:119
119            mrs     r0,  control
(gdb) c
Continuing.
^C
Program received signal SIGTRAP, Trace/breakpoint trap.
main () at ../main.c:107
107          break;

普通のELFファイルをRenodeで動かしてみた
普通のELFファイルをRenodeで動かしてみた

GDB で接続すると、エントリポイントで停止した状態になりました。その後、c(continue)で実行開始しました。LED が見えないですが、Ctrl+C で、いったん停止すると main関数のどこかで止まったので、動いてそうです。

デバッグ情報の無いELFファイルを動かしてみる

普通の ELFファイルと同じように、手動で Renode で起動していきます。

$ renode
Gtk-Message: 21:31:59.406: Failed to load module "canberra-gtk-module"
21:32:00.3045 [INFO] Loaded monitor commands from: /home/daisuke/svn_/renode/renode_1.15.0_portable/scripts/monitor.py
(monitor) mach create
21:32:20.0797 [INFO] System bus created.
(machine-0) machine LoadPlatformDescription @platforms/boards/stm32f4_discovery-kit.repl
21:32:33.8938 [INFO] Reading cache
21:32:34.3756 [INFO] sysbus: Loaded SVD: /tmp/renode-10368/5f7902f5-f5f6-40d8-bda6-cc71e8ab32c4.tmp. Name: STM32F40x. Description: STM32F40x.
(machine-0) sysbus LoadELF @stm32f4discovery_sample.elf
21:33:04.9895 [INFO] sysbus: Loading segment of 11116 bytes length at 0x8000000.
(machine-0) machine StartGdbServer 3333
21:33:18.2970 [INFO] machine-0: GDB server with all CPUs started on port :3333

ELFファイルをロードしたときのログが普通の ELFファイルの場合と異なります。GDB を接続します。

$ arm-none-eabi-gdb stm32f4discovery_sample.elf
(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()

エントリポイントまでいかず、0番地で起動しました。

Renode 側のログは以下のようになっていました。

21:35:26.6557 [WARNING] sysbus: [cpu: 0x0] ReadDoubleWord from non existing peripheral at 0x4.
21:35:26.6559 [WARNING] sysbus: [cpu: 0x0] ReadDoubleWord from non existing peripheral at 0x0.
21:35:26.6588 [ERROR] cpu: PC does not lay in memory or PC and SP are equal to zero. CPU was halted.
21:35:26.6656 [INFO] cpu: Setting initial values: PC = 0x0, SP = 0x0.
21:35:26.6664 [WARNING] cpu: Patching PC 0x0 for Thumb mode.
21:35:26.6678 [INFO] machine-0: Machine started.

STM32 の場合、4番地はリセットハンドラのアドレスが格納されていて、0番地はスタックポインタの初期値が格納されています。そこから読み出せなかった、という警告が出ています。

このデバッグ情報の無い ELFファイルは、最低限のリンカスクリプトで ELFファイルを作っています。それが原因かもしれません。

デバッグ情報の無い raw binary のバイナリファイルで動かしてみる

Renode のドキュメントの CPU環境の構築 のところを見ると、Renode は、ELFファイルだけでなく、バイナリファイルのロードにも対応しているようです。やってみます。

まずは、バイナリファイルを同じように、シンボリックリンクを作ります。

$ ln -s ~/eclipse-workspace/stm32f4discovery_sample/Debug/stm32f4discovery_sample_objcopy.bin

では、このバイナリファイルを Renode で起動していきます。

$ renode
Gtk-Message: 21:46:48.764: Failed to load module "canberra-gtk-module"
21:46:49.6671 [INFO] Loaded monitor commands from: /home/daisuke/svn_/renode/renode_1.15.0_portable/scripts/monitor.py
(monitor) mach create
21:46:59.5845 [INFO] System bus created.
(machine-0) machine LoadPlatformDescription @platforms/boards/stm32f4_discovery-kit.repl 
21:48:45.2759 [INFO] Reading cache
21:48:45.7662 [INFO] sysbus: Loaded SVD: /tmp/renode-10451/b58a73de-2638-46a1-9d68-515741ec08ce.tmp. Name: STM32F40x. Description: STM32F40x.
(machine-0) sysbus LoadBinary @stm32f4discovery_sample_objcopy.bin
The following methods are available:
 - Void LoadBinary (ReadFilePath fileName, UInt64 loadPoint, ICPU cpu = null)
Usage:
 sysbus MethodName param1 param2 ...
There was an error executing command 'sysbus LoadBinary @/home/daisuke/svn_/renode/renode_1.15.0_portable/stm32f4discovery_sample_objcopy.bin'
Parameters did not match the signature

単純にバイナリファイルを指定しただけではエラーになりました。ロードするアドレスを指定する必要があるようです。その後ろの ICPU?は分からないので、とりあえずアドレスだけ指定してみます。

(machine-0) sysbus LoadBinary @stm32f4discovery_sample_objcopy.bin 0x08000000
(machine-0) machine StartGdbServer 3333 
21:57:01.2632 [INFO] machine-0: GDB server with all CPUs started on port :3333

エラーは出なくなりましたが、正常な場合は、ロードされたというログが出ていたので、うまくいってないかもしれません。とりあえず GDB を接続してみます。

$ arm-none-eabi-gdb stm32f4discovery_sample_objcopy.bin 
"/home/daisuke/svn_/renode/renode_1.15.0_portable/stm32f4discovery_sample_objcopy.bin": not in executable format: file format not recognized
(gdb) target remote :3333
Remote debugging using :3333
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x00000000 in ?? ()

Renode 側のログです。

21:58:16.4682 [WARNING] sysbus: [cpu: 0x0] ReadDoubleWord from non existing peripheral at 0x4.
21:58:16.4686 [WARNING] sysbus: [cpu: 0x0] ReadDoubleWord from non existing peripheral at 0x0.
21:58:16.4712 [ERROR] cpu: PC does not lay in memory or PC and SP are equal to zero. CPU was halted.
21:58:16.4781 [INFO] cpu: Setting initial values: PC = 0x0, SP = 0x0.
21:58:16.4786 [WARNING] cpu: Patching PC 0x0 for Thumb mode.
21:58:16.4798 [INFO] machine-0: Machine started.

先ほどと同じですね。

ELFファイルの場合は、情報が不足しているので、失敗しても仕方ないですが、バイナリファイルの場合は、情報が無いファイルなので、この結果は変だと思います。

おわりに

今回は、Renode で、GDB を使って動かしてみました。

GDB 自体は問題なく動きましたが、バイナリファイルについては Renode の動作が怪しい気がします。

次回は、バイナリファイルで動作しない原因を見ていこうと思います。

この記事が誰かの役に立てば嬉しいです。

最後になりましたが、エンジニアグループのランキングに参加中です。

気楽にポチッとよろしくお願いいたします🙇

今回は以上です!

最後までお読みいただき、ありがとうございました。