LoginSignup
1
1

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-11-29

前回:3. メモリ、アドレス、ロード・ストア 次回:5. 分岐命令
目次(記事一覧)

※この記事はRoger Ferrer IbáñezさんのブログARM assembler in 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を確認してください。最初に学ぶべきコマンドは、

(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)を使用して操作できます。例えば、

(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と入力したらうまくいきました。)

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

(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. 分岐

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1