ラズパイでARM入門（4. GNU デバッガ (GDB)）

前回：[3. メモリ、アドレス、ロード・ストア](https://qiita.com/GoodOldLandLine/items/0b850a8b09c088d7d31d)　次回：[5. 分岐命令](https://qiita.com/GoodOldLandLine/items/a547cd98cb7d4eb5087f)
[目次（記事一覧）](https://qiita.com/GoodOldLandLine/items/bb17122fee602d1aedb9)

※この記事は[Roger Ferrer Ibáñez](https://rofi.roger-ferrer.org)さんのブログ[ARM assembler in Raspberry Pi – Chapter 4](https://thinkingeek.com/2013/01/12/arm-assembler-raspberry-pi-chapter-4/)の翻訳です。

ARMアセンブリコードの基礎を学んでいくと、サンプルコードがだんだん長くなっていきます。間違いやすくもなるので、GNU デバッガ（`gdb`）の使い方を学ぶ価値があると思います。LinuxでC/C++を開発しているが`gdb`を使用したことがないのなら、それは恥ずべきことです。`gdb`を知っているのなら、この短い章でアセンブリコードを直接デバッグする方法を知ることができます。

# gdb
第3章で作成した`store01`を使用します。`gdb`にデバッグするプログラムを指定しましょう。

```
$ gdb --args ./store01
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
For bug reporting instructions, please see:
...
Reading symbols from /home/roger/asm/chapter03/store01...(no debugging symbols found)...done.
(gdb)
```

このように`gdb`の対話モードが始まります。対話モードではコマンドを通じてgdbとやり取りすることができます。組み込みのヘルプコマンドは`help`です。詳しくは[GNU Debugger Documentation](http://sourceware.org/gdb/current/onlinedocs/gdb/)を確認してください。最初に学ぶべきコマンドは、

```
(gdb) quit
```

です。ではもう一度`gdb`を起動してください。この段階ではプログラムはまだ動いていません。コードにデバッグ情報が付いていないため`gdb`は多くのことを伝えられませんが、アセンブリコードをデバッグするのでデバッグ情報はあまり必要ありません。プログラムを開始してはじめの一歩を踏み出しましょう。

```
(gdb) start
Temporary breakpoint 1 at 0x8390
Starting program: /home/roger/asm/chapter03/store01 

Temporary breakpoint 1, 0x00008390 in main ()
```

`gdb`はプログラムを`main`まで動かしました。Cライブラリの初期化処理をすべてスキップして`main`関数の最初の命令を実行しようとしています。そこに何があるかを見てみましょう。

```
(gdb) disassemble
Dump of assembler code for function main:
=> 0x00008390 :	ldr	r1, [pc, #40]	; 0x83c0 
   0x00008394 :	mov	r3, #3
   0x00008398 :	str	r3, [r1]
   0x0000839c :	ldr	r2, [pc, #32]	; 0x83c4 
   0x000083a0 :	mov	r3, #4
   0x000083a4 :	str	r3, [r2]
   0x000083a8 :	ldr	r1, [pc, #16]	; 0x83c0 
   0x000083ac :	ldr	r1, [r1]
   0x000083b0 :	ldr	r2, [pc, #12]	; 0x83c4 
   0x000083b4 :	ldr	r2, [r2]
   0x000083b8 :	add	r0, r1, r2
   0x000083bc :	bx	lr
End of assembler dump.
```

（※訳者の環境だと
`0x000103d0 :	ldr	r1, [pc, #40]	; 0x10400 <addr_of_myvar1>`
のように`<ラベル名>`が行末に表示されました。）

もとのアセンブリコードと比べると`addr_of_myvarX`が別の表記になっていることがわかります。後ほどの章で解説するので今はとりあえず無視します。矢印`=>`はこれから実行しようとしている命令を指しています（実行はまだされていません）。では動かす前にレジスタを調べてみましょう。

```
(gdb) info registers r0 r1 r2 r3
r0             0x1	1
r1             0xbefff744	3204446020
r2             0xbefff74c	3204446028
r3             0x8390	33680
```

<!--直訳
レジスタは`p`（意味`print`）を使用して操作できますが、副作用も評価されます。例えば、
-->
<!--
副作用も評価されます？
-->
レジスタは`p`（意味`print`）を使用して操作できます。例えば、

```
(gdb) p $r0 = 2
$1 = 2
(gdb) info registers r0 r1 r2 r3
r0             0x2	2
r1             0xbefff744	3204446020
r2             0xbefff74c	3204446028
r3             0x8390	33680
```

`gdb`が表示した`$1`は結果を入れた識別子です。必要に応じて使用し、タイピングを省略するのに使います。今はあまり活用しませんが、複雑な式を表示させるのに便利です。

```
(gdb) p $1
$2 = 2
```

この時点で`$2`も使えるようになりました。では、最初の命令を実行します。

```
(gdb) stepi
0x00008394 in main ()
```

何が起こったか確認したいので`disassemble`を使います。

```
(gdb) disassemble
Dump of assembler code for function main:
   0x00008390 :	ldr	r1, [pc, #40]	; 0x83c0 
=> 0x00008394 :	mov	r3, #3
   0x00008398 :	str	r3, [r1]
   0x0000839c :	ldr	r2, [pc, #32]	; 0x83c4 
   0x000083a0 :	mov	r3, #4
   0x000083a4 :	str	r3, [r2]
   0x000083a8 :	ldr	r1, [pc, #16]	; 0x83c0 
   0x000083ac :	ldr	r1, [r1]
   0x000083b0 :	ldr	r2, [pc, #12]	; 0x83c4 
   0x000083b4 :	ldr	r2, [r2]
   0x000083b8 :	add	r0, r1, r2
   0x000083bc :	bx	lr
End of assembler dump.
```

`r1`に起こったことを確認します。

```
(gdb) info register r1
r1             0x10564	66916
```

予想通りに変化していますね。これは`myvar1`のアドレスです。本当にそうなのか、シンボリック名とC構文を使って確認しましょう。

```
(gdb) p &myvar1
$3 = ( *) 0x10564
```

（※訳者の環境では`$3 = (<data variable, no debug info> *) 0x21024`）

すごいです！この変数に入っている値は？

```
(gdb) p myvar1
$4 = 0
```

（※訳者がやってみたところ`'myvar1' has unknown type; cast it to its declared type`と出ました。
「型が不明だからキャストしろ」という意味のようですので、`(gdb) p (int)myvar1`と入力したらうまくいきました。）

完璧ですね。`myvar1`と`myvar2`の初期値にゼロを選んだので、この結果は期待通りです。では、次のステップです。

```
(gdb) stepi
0x00008398 in main ()
(gdb) disas
Dump of assembler code for function main:
   0x00008390 :	ldr	r1, [pc, #40]	; 0x83c0 
   0x00008394 :	mov	r3, #3
=> 0x00008398 :	str	r3, [r1]
   0x0000839c :	ldr	r2, [pc, #32]	; 0x83c4 
   0x000083a0 :	mov	r3, #4
   0x000083a4 :	str	r3, [r2]
   0x000083a8 :	ldr	r1, [pc, #16]	; 0x83c0 
   0x000083ac :	ldr	r1, [r1]
   0x000083b0 :	ldr	r2, [pc, #12]	; 0x83c4 
   0x000083b4 :	ldr	r2, [r2]
   0x000083b8 :	add	r0, r1, r2
   0x000083bc :	bx	lr
End of assembler dump.
```

`disasssemble`は省略して`disas`と書けます。`r3`の値を確認しましょう。

```
(gdb) info registers r3
r3             0x3	3
```

ここまで順調ですね。さらにもう一つステップを進めます。

```
(gdb) stepi
0x0000839c in main ()
(gdb) disas
Dump of assembler code for function main:
   0x00008390 :	ldr	r1, [pc, #40]	; 0x83c0 
   0x00008394 :	mov	r3, #3
   0x00008398 :	str	r3, [r1]
=> 0x0000839c :	ldr	r2, [pc, #32]	; 0x83c4 
   0x000083a0 :	mov	r3, #4
   0x000083a4 :	str	r3, [r2]
   0x000083a8 :	ldr	r1, [pc, #16]	; 0x83c0 
   0x000083ac :	ldr	r1, [r1]
   0x000083b0 :	ldr	r2, [pc, #12]	; 0x83c4 
   0x000083b4 :	ldr	r2, [r2]
   0x000083b8 :	add	r0, r1, r2
   0x000083bc :	bx	lr
End of assembler dump.
```

`r3`に起こったことを確認します。

```
(gdb) p myvar1
$5 = 3
```

（※さきほどと同じく`(gdb) p (int)myvar1`でうまくいきました。）

すごいですね。では、最後まで実行します。

```
(gdb) continue
Continuing.
[Inferior 1 (process 3080) exited with code 07]
```

今日はここまで。

次回：[5. 分岐](https://qiita.com/GoodOldLandLine/items/a547cd98cb7d4eb5087f)