読者です 読者をやめる 読者になる 読者になる

gdbでスタックフレームを見てみる

最近Linuxプログラミングインターフェースの輪読をおこなっているので、その復習のため、ちょっと見てみました。

こんなソースを実行。 allocaはおまけです。

     1   #include <stdio.h>
     2  #include <alloca.h>
     3
     4  void test1(int *);
     5  void test2(int *);
     6  void test3(int *);
     7
     8  int main(int argc, char *argv) {
     9    int a = 111;
    10    test1(&a);
    11    return 0;
    12  }
    13
    14  void test1(int *a){
    15    printf("test1 : %d\n",*a);
    16    printf("test1 address : %p\n",test1);
    17    test2(a);
    18  }
    19
    20  void test2(int *a){
    21    *a = 222;
    22    void *x;
    23    printf("test2 : %d\n",*a);
    24    printf("test2 address : %p\n",test2);
    25    x = alloca(4);
    26    printf("alloca : %p\n",x);
    27    test3(a);
    28  }
    29
    30  void test3(int *a){
    31    *a = 333;
    32    printf("test3 : %d\n",*a);
    33    printf("test3 address : %p\n",test3);
    34  }

gdbでdebugしてみる。 break pointの設定 && 実行

(gdb) break 34
Breakpoint 1 at 0x400678: file ./stack_test.c, line 34.
(gdb) run
Starting program: /home/foxtrot/stack_test/./a.out
test1 : 111
test1 address : 0x400559
test2 : 222
test2 address : 0x40059e
alloca : 0x7fffffffe510
test3 : 333
test3 address : 0x400637

Breakpoint 1, test3 (a=0x7fffffffe59c) at ./stack_test.c:34
34  }
(gdb)

backtrace! 先頭からプログラムカウンタの値、関数名、引数、コードの実行箇所みたいです。

(gdb) backtrace
#0  test3 (a=0x7fffffffe59c) at ./stack_test.c:34
#1  0x0000000000400635 in test2 (a=0x7fffffffe59c) at ./stack_test.c:27
#2  0x000000000040059c in test1 (a=0x7fffffffe59c) at ./stack_test.c:17
#3  0x0000000000400552 in main (argc=1, argv=0x7fffffffe688 "\253\350\377\377\377\177") at ./stack_test.c:10

frame 数字 を指定することで、実行しているソースコードの場所も見れます。 このときlistを実行することで、さらにその周りのソースコードも見れるとか。

(gdb) frame 1
#1  0x0000000000400635 in test2 (a=0x7fffffffe59c) at ./stack_test.c:27
27    test3(a);
(gdb) list
22    void *x;
23    printf("test2 : %d\n",*a);
24    printf("test2 address : %p\n",test2);
25    x = alloca(4);
26    printf("alloca : %p\n",x);
27    test3(a);
28  }
29
30  void test3(int *a){
31    *a = 333;

info localsでローカル変数。info argsで引数

(gdb) info args
a = 0x7fffffffe59c
(gdb) info locals
x = 0x7fffffffe510

info frame 数字 でより詳しい情報がみれます。 呼び出し元とか呼び出し先とかプログラムカウンタの値とか。

(gdb) info frame 0
Stack frame at 0x7fffffffe510:
 rip = 0x400678 in test3 (./stack_test.c:34); saved rip 0x400635
 called by frame at 0x7fffffffe560
 source language c.
 Arglist at 0x7fffffffe500, args: a=0x7fffffffe59c
 Locals at 0x7fffffffe500, Previous frame's sp is 0x7fffffffe510
 Saved registers:
  rbp at 0x7fffffffe500, rip at 0x7fffffffe508
(gdb) info frame 1
Stack frame at 0x7fffffffe560:
 rip = 0x400635 in test2 (./stack_test.c:27); saved rip 0x40059c
 called by frame at 0x7fffffffe580, caller of frame at 0x7fffffffe510
 source language c.
 Arglist at 0x7fffffffe550, args: a=0x7fffffffe59c
 Locals at 0x7fffffffe550, Previous frame's sp is 0x7fffffffe560
 Saved registers:
  rbp at 0x7fffffffe550, rip at 0x7fffffffe558
(gdb) info frame 2
Stack frame at 0x7fffffffe580:
 rip = 0x40059c in test1 (./stack_test.c:17); saved rip 0x400552
 called by frame at 0x7fffffffe5b0, caller of frame at 0x7fffffffe560
 source language c.
 Arglist at 0x7fffffffe570, args: a=0x7fffffffe59c
 Locals at 0x7fffffffe570, Previous frame's sp is 0x7fffffffe580
 Saved registers:
  rbp at 0x7fffffffe570, rip at 0x7fffffffe578
(gdb) info frame 3
Stack frame at 0x7fffffffe5b0:
 rip = 0x400552 in main (./stack_test.c:10); saved rip 0x7ffff7a3bb15
 caller of frame at 0x7fffffffe580
 source language c.
 Arglist at 0x7fffffffe5a0, args: argc=1, argv=0x7fffffffe688 "\253\350\377\377\377\177"
 Locals at 0x7fffffffe5a0, Previous frame's sp is 0x7fffffffe5b0
 Saved registers:
  rbp at 0x7fffffffe5a0, rip at 0x7fffffffe5a8

Stack frame at xxxxの箇所が、Stackのメモリアドレスなのかな。 試しにallocaに大きな値を入れてみた。

25    x = alloca(64);

これでgdbで再度見てみると確かに値が変わっている! 前回はframe 0の値が「Stack frame at 0x7fffffffe510:」だったけれども、今回は「Stack frame at 0x7fffffffe4e0:」になっている。

(gdb) info frame 0
Stack frame at 0x7fffffffe4e0:
 rip = 0x4006c2 in test3 (./stack_test.c:37); saved rip 0x400635
 called by frame at 0x7fffffffe560
 source language c.
 Arglist at 0x7fffffffe4d0, args: a=0x7fffffffe59c
 Locals at 0x7fffffffe4d0, Previous frame's sp is 0x7fffffffe4e0
 Saved registers:
  rbp at 0x7fffffffe4d0, rip at 0x7fffffffe4d8
(gdb) info frame 1
Stack frame at 0x7fffffffe560:
 rip = 0x400635 in test2 (./stack_test.c:27); saved rip 0x40059c
 called by frame at 0x7fffffffe580, caller of frame at 0x7fffffffe4e0
 source language c.
 Arglist at 0x7fffffffe550, args: a=0x7fffffffe59c
 Locals at 0x7fffffffe550, Previous frame's sp is 0x7fffffffe560
 Saved registers:
  rbp at 0x7fffffffe550, rip at 0x7fffffffe558

ripとかは退避したプログラムカウンタの値?ripはeipの64bit版なんですかね。。 なんか調べると奥が深そうだから、また今度!