みかん日記 (6) - らんだむな記憶 の補足として、lldb
を使って中身を追いかけてみる。
まず、
sudo apt install lldb-7
で lldb
をインストールしておく。
lldb-7 foo
みたいな感じでプロセスにアタッチした状態で lldb
を起動する。
今回はソースコードの 5 行目に関数 foo
の処理の先頭が来るようにしているのでここにブレークポイントを設定する。
(lldb) b foo.cpp:5 Breakpoint 1: where = foo`foo() + 4 at foo.cpp:5, address = 0x00000000004004a4 (lldb) r Process 13891 stopped * thread #1, name = 'foo', stop reason = step in frame #0: 0x00000000004004a4 foo`foo() at foo.cpp:5 2 3 void foo() 4 { -> 5 int i = 42; 6 int* p = &i; 7 int r1 = *p; 8 *p = 1;
の時点で
(lldb) p/x $rsp (unsigned long) $0 = 0x00007fffffffe0d0 (lldb) p/x $rbp (unsigned long) $1 = 0x00007fffffffe0d0
なので、レジスタ rsp
と rbp
を見ると、確かに一致している。
(lldb) s Process 14189 stopped * thread #1, name = 'foo', stop reason = step in frame #0: 0x00000000004004ab foo`foo() at foo.cpp:6 3 void foo() 4 { 5 int i = 42; -> 6 int* p = &i; 7 int r1 = *p; 8 *p = 1; 9 int r2 = i;
に移動すると、int i = 42;
が実行されたはずである。これを確認すると、
(lldb) p *(int*)($rbp - 4) (int) $2 = 42
となり、確かにそうなっている。さらに進めて、
(lldb) s Process 14189 stopped * thread #1, name = 'foo', stop reason = step in frame #0: 0x00000000004004b3 foo`foo() at foo.cpp:7 4 { 5 int i = 42; 6 int* p = &i; -> 7 int r1 = *p; 8 *p = 1; 9 int r2 = i; 10 uintptr_t addr = reinterpret_cast<uintptr_t>(p);
まで来る。int* p = &i;
が実行されたはずである。これを確認すると、
(lldb) p &i (int *) $3 = 0x00007fffffffe0cc
より、変数 i
のアドレスは 0x00007fffffffe0cc
である。
(lldb) p/x $rax (unsigned long) $4 = 0x00007fffffffe0cc
により、このアドレスが確かにレジスタ rax
に入っていることが分かる。
(lldb) p/x *(uint64_t*)($rbp - 16) (uint64_t) $5 = 0x00007fffffffe0cc
により、レジスタ rbp
の手前 16 の位置に変数 i
のアドレスが書き込まれていることが分かる。
さらに進めて、
(lldb) s Process 14189 stopped * thread #1, name = 'foo', stop reason = step in frame #0: 0x00000000004004bc foo`foo() at foo.cpp:8 5 int i = 42; 6 int* p = &i; 7 int r1 = *p; -> 8 *p = 1; 9 int r2 = i; 10 uintptr_t addr = reinterpret_cast<uintptr_t>(p); 11 int* q = reinterpret_cast<int*>(addr);
まで来る。int r1 = *p;
が実行されたはずである。これを確認すると、
(lldb) p p (int *) $6 = 0x00007fffffffe0cc (lldb) p/x $rax (unsigned long) $7 = 0x00007fffffffe0cc
なので、レジスタ rax
にはポインタ p
の指しているアドレスが入っていることが分かる。
(lldb) p $ecx (unsigned int) $8 = 42
より、レジスタ ecx
には、ポインタ p
が指しているアドレスの変数 i
の値 42
が入っていることが分かる。
(lldb) p *(int*)($rbp - 20) (int) $9 = 42
より、レジスタ rbp
の手前 20 の位置に変数 i
の値 42
が書き込まれていることが分かる。
後は繰り返しなので割愛するとして、レジスタ rbp
が指し示すアドレス周辺のメモリの内容を見たい。
(lldb) $rbp - 20 (unsigned long) $10 = 0x00007fffffffe0bc
なので
(lldb) x/5 0x00007fffffffe0bc 0x7fffffffe0bc: 0x0000002a 0xffffe0cc 0x00007fff 0x00000000 0x7fffffffe0cc: 0x0000002a
くらいで幾らか書き出すと、$rbp - 20
の位置に 4 バイト分のデータ 42
(変数 i
由来の値)があり、$rbp - 16
の位置に 8 バイト分のデータとして 変数 i
のアドレスが見えていることが分かる。「(int) $2 = 42
」で見たように $rbp - 4
の位置に 4 バイト分のデータ 42
があることも見て取れる。
lldb
のコマンドについては lldb で使えるコマンド一覧 を参考にした。