ポインタ変数がメモリー内部でどのように配置されているか見てみる 続き

coLinux上でプログラムを実行してみる。

# ./pointer
      8059:     binding file ./pointer to /lib/tls/i686/cmov/libc.so.6: normal symbol `printf' [GLIBC_2.0]
5

なぜかローダーのメッセージが出ているけど気にしない事にする。それよりも、coredumpせずに実行できてしまった事が驚き。仕方ないので宣言部分を適当にいじる。

  int *n = (int *)0xAAAAAAAA;
# ulimit -c unlimited
# ./pointer
Segmentation fault (core dumped)

coredumpしたので中身をみていく。

# gdb pointer core
GNU gdb 6.4-debian
Copyright 2005 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

Failed to read a valid object file image from memory.
Core was generated by `./pointer'.
Program terminated with signal 11, Segmentation fault.

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0  0x08048386 in main ()

スタックトレースはbt。

(gdb) bt
#0  0x08048386 in main ()

main関数で落ちてるからな・・・。
レジスターはinfo registersで見るらしい。

(gdb) info registers
eax            0xaaaaaaaa       -1431655766
ecx            0xbfa177cc       -1079937076
edx            0x1      1
ebx            0xb7f04adc       -1208988964
esp            0xbfa17710       0xbfa17710
ebp            0xbfa17738       0xbfa17738
esi            0xbfa177c4       -1079937084
edi            0xbfa17750       -1079937200
eip            0x8048386        0x8048386 <main+38>
eflags         0x10282  66178
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

なるほど。eaxというレジスターにsegmentation faultの原因になったアドレスが入っている。

(gdb) x/i $eip
0x8048386 <main+38>:    movl   $0x5,(%eax)

%eaxのアドレスへ0x5をコピー(move long-word??)してるところで落ちてる。

(gdb) x/20i main
0x8048360 <main>:       push   %ebp
0x8048361 <main+1>:     mov    %esp,%ebp
0x8048363 <main+3>:     sub    $0x18,%esp
0x8048366 <main+6>:     and    $0xfffffff0,%esp
0x8048369 <main+9>:     mov    $0x0,%eax
0x804836e <main+14>:    add    $0xf,%eax
0x8048371 <main+17>:    add    $0xf,%eax
0x8048374 <main+20>:    shr    $0x4,%eax
0x8048377 <main+23>:    shl    $0x4,%eax
0x804837a <main+26>:    sub    %eax,%esp
0x804837c <main+28>:    movl   $0xaaaaaaaa,0xfffffffc(%ebp)  <-- %ebp - 4(最初のローカル変数) に 0xaaaaaaaa を入れる
0x8048383 <main+35>:    mov    0xfffffffc(%ebp),%eax         <-- %eax = 0xaaaaaaaa
0x8048386 <main+38>:    movl   $0x5,(%eax)
0x804838c <main+44>:    mov    0xfffffffc(%ebp),%eax         <-- 再び %ebp - 4 を %eaxに入れて
0x804838f <main+47>:    mov    (%eax),%eax                   <-- dereference?
0x8048391 <main+49>:    mov    %eax,0x4(%esp) 
0x8048395 <main+53>:    movl   $0x8048498,(%esp)
0x804839c <main+60>:    call   0x80482b0 <printf@plt>
0x80483a1 <main+65>:    mov    $0x0,%eax
0x80483a6 <main+70>:    leave

命令長がバラバラで気持ち悪い・・・(←RISC系の人)。命令もよく分からないし・・・何なんだ。
で、スタックはどうやってみるのかな。

http://www.unixwiz.net/techtips/win32-callconv-asm.html によると

%ESPはStack Pointer。
%EBPはBase Pointer(Frame Pointer)らしい。関数の引数とローカル変数を参照するときに使うらしい。へえ。
最初のローカル変数は、%EBP - 4にあるらしい。

(gdb) x/32x 0xbfa17710
0xbfa17710:     0xb7f04adc      0xbfa177cc      0xbfa17738      0x080483c3
                ^^^^^^^^^^
                esp

0xbfa17720:     0x00000001      0xb7f04adc      0x080494a0      0xb7f04adc
0xbfa17730:     0x00000000      0xaaaaaaaa      0xbfa17798      0xb7deeea2
                                ^^^^^^^^^^      ^^^^^^^^^^      ^^^^^^^^^-
                                local var1      ebp             old EIP

0xbfa17740:     0x00000001      0xbfa177c4      0xbfa177cc      0xb7f1bbf6
                ^^^^^^^^^^      ^^^^^^^^^^
                param #1        param #2

0xbfa17750:     0xb7f04adc      0x00000000      0xbfa17750      0xbfa17798
0xbfa17760:     0xbfa17740      0xb7deee64      0x00000000      0x00000000
0xbfa17770:     0x00000000      0xb7f26778      0x00000001      0x080482c0
0xbfa17780:     0x00000000      0xb7f1bb30      0xb7f1c691      0xb7f26778
(gdb)

確かにそんな感じだ。ついでにパラメーターも見てみる。第一引数はargcだから1*1、第二引数は**argvだから、最初の要素は実行ファイル名になってるはず。

(gdb) x/x 0xbfa177c4
0xbfa177c4:     0xbfa17e98
(gdb) x/s 0xbfa17e98
0xbfa17e98:      "./pointer"

なるほど。
ただ、上のURLでは__cdecl conventionsのときのスタックって言ってるから、conventionによってフォーマットが違うのかも。
あと、%ESPは0xbfa17710なんだけど、ここにはレジスターの値が保存されてるらしい。この例だとebx。じゃあスタックをたどるのはどうやってやるんだろう。この辺も調べねば。

あー

わすれてた。nを未初期化に戻して、何が入ってるか確認してみる。

# gdb ./pointer
[GNU gdb 6.4-debian
Copyright 2005 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb) x/20i main
0x8048360 <main>:       push   %ebp
0x8048361 <main+1>:     mov    %esp,%ebp
0x8048363 <main+3>:     sub    $0x18,%esp
0x8048366 <main+6>:     and    $0xfffffff0,%esp
0x8048369 <main+9>:     mov    $0x0,%eax
0x804836e <main+14>:    add    $0xf,%eax
0x8048371 <main+17>:    add    $0xf,%eax
0x8048374 <main+20>:    shr    $0x4,%eax
0x8048377 <main+23>:    shl    $0x4,%eax
0x804837a <main+26>:    sub    %eax,%esp
0x804837c <main+28>:    mov    0xfffffffc(%ebp),%eax
0x804837f <main+31>:    movl   $0x5,(%eax)   <--- ココで止める

0x8048385 <main+37>:    mov    0xfffffffc(%ebp),%eax
0x8048388 <main+40>:    mov    (%eax),%eax
0x804838a <main+42>:    mov    %eax,0x4(%esp)
0x804838e <main+46>:    movl   $0x8048494,(%esp)
0x8048395 <main+53>:    call   0x80482b0 <printf@plt>
0x804839a <main+58>:    mov    $0x0,%eax
0x804839f <main+63>:    leave
0x80483a0 <main+64>:    ret

前回Segmentation Faultが起こったmovl $0x5,(%eax)のとこで止めたいので、今回のケースでは0x804837f ブレークポイントをセットします。

(gdb) break *0x804837f
Breakpoint 1 at 0x804837f
(gdb) run
Starting program: /root/work/pointer
Failed to read a valid object file image from memory.

Breakpoint 1, 0x0804837f in main ()

止まりました

(gdb) info register
eax            0xb7f44be0       -1208726560
ecx            0xbf8e167c       -1081207172
edx            0x1      1
ebx            0xb7f22adc       -1208866084
esp            0xbf8e15c0       0xbf8e15c0
ebp            0xbf8e15e8       0xbf8e15e8
esi            0xbf8e1674       -1081207180
edi            0xbf8e1600       -1081207296
eip            0x804837f        0x804837f <main+31>
eflags         0x286    646
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
(gdb) x/32x 0xbf8e15c0
0xbf8e15c0:     0xb7f22adc      0xbf8e167c      0xbf8e15e8      0x080483bf
0xbf8e15d0:     0x00000001      0xb7f22adc      0x0804949c      0xb7f22adc
0xbf8e15e0:     0x00000000      0xb7f44be0      0xbf8e1648      0xb7e0cea2
                                ^^^^^^^^^^

0xbf8e15f0:     0x00000001      0xbf8e1674      0xbf8e167c      0xb7f39bf6
0xbf8e1600:     0xb7f22adc      0x00000000      0xbf8e1600      0xbf8e1648
0xbf8e1610:     0xbf8e15f0      0xb7e0ce64      0x00000000      0x00000000
0xbf8e1620:     0x00000000      0xb7f44778      0x00000001      0x080482c0
0xbf8e1630:     0x00000000      0xb7f39b30      0xb7f3a691      0xb7f44778
(gdb) x/x 0xb7f44be0
0xb7f44be0 <_rtld_global_ro>:   0x00000000

_rtld_global_roってのはぐぐってもよく分からなかった。

*1:voidなので