みねっちょのマイコン関係ブログ

組込開発系フリーソフトやハードの情報発信ブログ

ARM シミュレータ内蔵 GDB の使い方

サイト内 Google 検索:


最終更新:2023-04-11
GDB 内蔵の ARM CPU 用のシミュレータを TUI (Text User Interface) モードで使用して、ベアメタル用ソースコードデバッグを行う手順を紹介します。C 等の高級言語ソースコードアセンブリ言語の同時表示や、CPU レジスタの値の同時表示も出来ます。 尚、ARM 用シミュレータ内蔵 GDB を WSL の Ubuntu-20.04 上でビルドする手順は、【こちらの別記事】で紹介しています。

目次:

本記事の目標:

次の図の様に、ARM 用クロス開発環境を使用して、TUI モードで C 等の高級言語ソースコードアセンブリ言語を同時に表示して、任意の場所にブレークポイントを設定したり、シングルステップ実行したりしてデバッグする事を目標とします。

また、ARM CPU のレジスタの内容を表示しながら、アセンブリ言語の任意の場所にブレークポイントを設定したり、シングルステップ実行したりして動作を確認することも目標とします。

前提条件:

  • Windows 10 を使用していること
  • WSL 上に Ubuntu をインストールしていること
  • ARM シミュレータ内蔵 GDB をビルド済なこと (詳細は【こちらの別記事】に記載)
  • ベアメタル用の ARM クロスコンパイラをインストール済なこと (sudo apt install gcc-arm-none-eabi)


動作確認用プログラム:

動作確認用として次のプログラムと Makefile を使用します。
尚、ここで使用しているブートストラップは簡易的な物です。本来の C 言語の仕様 (スタティック変数のゼロ初期化やグローバル変数の使用) には対応していませんし、割り込みにも対応していません。

cat から ALLEOF までを Linux のターミナル上にコピー&ペーストすると、start.smain.ciadd.cisub.carith.hMakefile.tmp の6つのファイルが出来ます。
cat << "EOF" > start.s
.equ    STACK_TOP, 0x20010000

        .global _start
        .text

### Vector Table ###
        .align 2
vect_table:
        b  _start

### Main Routine Call ###
        .align 9
_start:
        ldr sp, =STACK_TOP
        bl main
        nop; nop; nop; nop
        b _start // Inf loop

### Padding ###
        .align 2
EOF
cat << "EOF" > main.c
#include <stdint.h>
#include "arith.h"

int32_t main() {
  volatile int32_t  a, b;
  int32_t  c;

  a = 4;
  b = 3;
  c = iadd(a, b);
  if (c != 7) return 1;
  c = isub(a, b);
  if (c != 1) return 1;
  return 0;
}
EOF
cat << "EOF" > iadd.c
#include <stdint.h>

int32_t iadd(int32_t a, int32_t b) {
    int32_t c;
    c = a + b;
    return c;
}
EOF
cat << "EOF" > isub.c
#include <stdint.h>

int32_t isub(int32_t a, int32_t b) {
  int32_t c;
  c = a - b;
  return c;
}
EOF
cat << "EOF" > arith.h
#include <stdint.h>
int32_t iadd(int32_t a, int32_t b);
int32_t isub(int32_t a, int32_t b);
EOF
cat << "ALLEOF" > Makefile.tmp
PROGRAM = main.out
DISASML = main.disa
INCS = arith.h
SRCS = start.s main.c iadd.c isub.c
OBJS = start.o main.o iadd.o isub.o

AS = arm-none-eabi-as
ASFLAGS = -g -gdwarf-2
TARGET_MACH = -march=armv5te+fp
CC = arm-none-eabi-gcc
CFLAGS = -g -gdwarf-2 -Og
TARGET_ARCH = -march=armv5te+fp
LD = arm-none-eabi-ld
LDFLAGS = -Bstatic -Ttext 0x0
LDLIBS = -lg_nano
OBJDUMP = arm-none-eabi-objdump
DISAFLAGS = --disassemble-all --source --section=.text

.PHONY: all
all: depend $(PROGRAM) $(DISASML)

#.s.o:
#       $(AS) $(ASFLAGS) $(TARGET_MACH) -o $@ $<

#.c.o:
#       $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<

#.c.o:
#       $(CC) -Og $(CPPFLAGS) $(TARGET_ARCH) -S -fverbose-asm -o tmp_$*.s $<
#       $(AS) $(ASFLAGS) $(TARGET_MACH) -o $@ tmp_$*.s

$(PROGRAM): $(OBJS)
        $(LD) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@

$(DISASML): $(PROGRAM)
        $(OBJDUMP) $(DISAFLAGS) $< > $@

.PHONY: clean
clean:
        $(RM) *.o $(PROGRAM) $(DISASML) tmp_*.s

.PHONY: depend
depend: $(INCS) $(SRCS)
ALLEOF

プログラムの make:

Makefile はタブキーが区切り記号となるので、8 カラム毎をタブキーに置き換え、make の動作確認を行います。尚、Mekefile 中の先頭が # で始まる行はコメントです。

unexpand -t8 Makefile.tmp > Makefile
make

次のメッセージが出力されれば、make は正常に動作しています。

arm-none-eabi-as -g -gdwarf-2 -march=armv5te+fp -o start.o start.s
arm-none-eabi-gcc -g -gdwarf-2 -Og  -march=armv5te+fp -c -o main.o main.c
arm-none-eabi-gcc -g -gdwarf-2 -Og  -march=armv5te+fp -c -o iadd.o iadd.c
arm-none-eabi-gcc -g -gdwarf-2 -Og  -march=armv5te+fp -c -o isub.o isub.c
arm-none-eabi-ld -Bstatic -Ttext 0x0 start.o main.o iadd.o isub.o  -lg_nano -o main.out
arm-none-eabi-objdump --disassemble-all --source --section=.text main.out > main.disa


GDB を TUI モードで使用したデバッグ

ARM 用シミュレータ内蔵でビルドした GDB を、TUI (Text User Interface) モードで立ち上げます。尚、--tui を指定し忘れた場合には Ctrl-x Ctrl-a で TUI モードのオン・オフを切り替えられます。あるいはコマンドで tui enable と指定すると TUI モードに入れます。

arm-none-eabi-gdb --tui

GDB が立ち上がり、(gdb) のプロンプトが表示されますので、次の通りコマンドを打ちます。

file main.out
target sim
load main.out
layout split

最初の file main.out コマンドで、シンボル情報を読み込みます。 target sim コマンドで、デバッガをシミュレータに接続します。 load main.out コマンドで、実行すべきファイルを main.out に指定します。 layout split コマンドで、TUI モードの上側に C 等の高級言語ソースコード、下側にアセンブリ言語を表示します。

以上でプログラムを実行する準備が出来ましたので、main() 関数の先頭にブレークポイントを張って実行し、以降をシングルステップ実行してみます。 尚、シングルステップは s コマンドで下階層のプログラム中に入りますが、n コマンドだと下の階層には入りません。Enter キーだけを押すと前回と同じコマンドが繰り返されます。 尚、ブートストラップ start.s プログラムの中で無限ループしていますので、main() 関数が繰り返し実行されます。

(gdb) b main
(gdb) r
(gdb) s
(gdb) 
(gdb) n
...

尚、b main の代わりに b n とすると、ソースリスト上の n 行目にブレークポイントを設定可能です。複数のブレークポイントを設定した場合、c (continue) コマンドを実行すると次のブレークポイントまでが実行されます。

(画像はクリックまはたピンチで拡大します)

この状態で tui reg general コマンドを実行すると、上側の C 等の高級言語が表示されていたフレームが、汎用レジスタ表示に切り替わります。更に、si または ni コマンドを実行すると、アセンブリ言語の1命令ずつを実行しながら汎用レジスタの内容を確認出来ます。2回目以降は Enter キーを押せば前回と同様のコマンドが実行されます。尚、レジスタを同時表示する場合には、表示領域が不足がちになりますので、ターミナルのフォントを小さめにするか横幅を広げた方が快適です。横幅を広げるとレジスタが複数列に表示されます。
また、アセンブリの中にブレークポイントを設定したい場合には、例えば b *main +20 の様に設定します。ここでのキーポイントは、指定する関数の前にアスタリスク * を付けることと、その後に指定する +n の修飾子のプラス記号と数字の間にスペースを入れない事です。


(gdb) tui reg general
(gdb) si
(gdb) 
(gdb) ...

(画像はクリックまはたピンチで拡大します)

終了は、q コマンドを実行すると、

Quit anyway? (y or n)
と表示されますので、y と入力します。

GDB の主要なコマンドの一覧:

GDB の TUI モードのキーバインドの一覧は、次の公式ページに記載されています。

GDB の主要なコマンドの一覧を示します。

コマンド 省略形 動作
help h ヘルプを表示する
help tui h tui TUI モード用コマンドのヘルプを表示する
file filename デバッグするファイルを指定し、シンボルをロードする
target sim シミュレータに接続する
load filename 実行するファイルをロードする
Ctrl-x Ctrl-a
または Ctrl-x a
- TUI (Text User Interface) モードのオン・オフ切り替え
tui enable TUI モードのオン
tui disable TUI モードのオフ
tui reg TUI 表示可能なレジスタ群の一覧を表示する
tui reg general TUI モードで汎用レジスタ群を表示する
layout asm TUI モードでアセンブリ表示を行う
layout src TUI モードで高級言語ソースコード表示を行う
layout split TUI モードをオンし表示の分割を行う
Ctrl-x o 分割した表示間のハイライトを入れ替える
refresh Ctrl-l 乱れた TUI 表示のリフレッシュを行う
break function b function 関数 function() の先頭にブレークポイントを設定する
break n b n 現在のスコープの n 行目にブレークポイントを設定する
break  *f  +n b  *f  +n 関数 f() の +n バイト目にブレークポイントを設定する
info i 各種情報を出力する (引数無しだとヘルプとなる)
info break i b 設定されたブレークポイントを表示する
run r プログラムを実行する (ブレークポイントまで走る)
continue c 次のブレークポイントまでを実行する
step s 1行ステップ実行する (下位の関数にステップインする)
next n 1行ステップ実行する (下位の関数には入らない)
stepi si アセンブリ命令を1つ実行する (下位関数にステップイン)
nexti ni アセンブリ命令を1つ実行する (下位の関数には入らない)
Enter のみ Enter 前回と同じコマンドを繰り返す
quit q 終了する


【ARM 関係の目次へ戻る】   【WSL 関係の目次へ戻る】