その時々

その時々で違うんです。特に決まっていないんです。

main関数の引数や変数等のレジスタ周り

インラインアセンブラのローカル変数の部分を勉強していたら、こちらが気になったので脱線中です。
main関数で変数を宣言したときのEBPからのオフセットと呼びだした関数の中で変数を宣言したときのEBPからのオフセットが変わるのはなぜだろうと疑問が出てきました。
そこで、EBPとESP、いわゆるスタック関連について調査です。

まずは簡単なCのソースを書いてみます。

int main(int argc, char *argv[])
{
  return 0;
}

これをgccコンパイルアセンブラコードを出力してみます。

$ gcc -S hoge.c

main関数はこんな感じになります。

main:
	pushl	%ebp
	movl	%esp, %ebp
	movl	$0, %eax
	popl	%ebp
	ret

最初にEBPレジスタをスタックに積んでいますが、ここは呼び出し元のアドレスを退避するためです。
終了するときにはpopl %ebpEBPレジスタから読み出して終了することからも読み出し元へEBPレジスタの位置を教えていることが分かります。
次に、ESPレジスタEBPレジスタにセットしています。
この時点でmain関数で使用するスタックベースがEBPレジスタにセットされます。
最終的には戻り値はEAXレジスタにセットして終了します。

スタックフレーム*1

未確認 呼び出し元のアドレス

実際に引数を渡してデバッグしてみます。

$ gcc -g -o hoge hoge.c
$ gdb hoge

1 2 3と引数を渡してみます。

(gdb) break main
(gdb) run 1 2 3

レジスタの状態です。

(gdb) i r
eax            0xbffff974	-1073743500
ecx            0x5dea12cf	1575621327
edx            0x4	4
ebx            0x283ff4	2637812
esp            0xbffff8c8	0xbffff8c8
ebp            0xbffff8c8	0xbffff8c8
esi            0x0	0
edi            0x0	0
eip            0x80483b7	0x80483b7 <main+3>
eflags         0x200246	[ PF ZF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

これを見るかぎり呼び出し元は0xbffff8c8に格納されていることがわかります。

(gdb) x $ebp+4
0xbffff8cc:	0x00144bd6

スタックフレーム

0xbffff8c8 呼び出し元のアドレス:0x00144bd6

0x00144bd6から呼ばれていますね。
次に引数はどうなっているのかを見てみましょう。

(gdb) x $ebp+8
0xbffff8d0:	0x00000004

まずは引数の数が入っています。
ここでは4つです。
1 2 3と引数を入れたのですが、なぜ4つかといいますと、最初の引数には実行ファイルのパスが入っているからです。

スタックフレーム

0xbffff8c8 呼び出し元のアドレス:0x00144bd6
0xbffff8d0 引数の数:4

一番目の引数はどうなっているのでしょうか。

(gdb) x/xw $ebp+12
0xbffff8d4:	0xbffff974
(gdb) x 0xbffff974
0xbffff974:	0xbffffae7
(gdb) x/s 0xbffffae7
0xbffffae7:	 "/home/hogehoge/hoge"

まずベースポインタから+12オフセットしたアドレスを参照します。
ここでは0xbffff974に一番目の引数のポインタが格納されていることがわかります。
次にアドレス0xbffff974に格納されているポインタの値を参照します。
0xbffffae7に実際の文字列が格納されていることがわかります。
そこで0xbffffae7のアドレスを参照してみると、/home/hogehoge/hogeという実行ファイルのパスが表示されました。

スタックフレーム

0xbffff8c8 呼び出し元のアドレス:0x00144bd6
0xbffff8d0 引数の数:4
0xbffff974 一番目の引数のポインタ:0xbffffae7

2〜4番目の引数も同じように確認してみます。

(gdb) x/xw $ebp+16
0xbffff8d8:	0xbffff988
(gdb) x/xw 0xbffff988
0xbffff988:	0xbffffb0e
(gdb) x/s 0xbffffb0e
0xbffffb0e:	 "ORBIT_SOCKETDIR=/tmp/orbit-hoge"

ところが、EBP+16には引数のポインタは格納されていません。
環境変数が入っていました。

スタックフレーム

0xbffff8c8 呼び出し元のアドレス:0x00144bd6
0xbffff8d0 引数の数:4
0xbffff974 一番目の引数のポインタ:0xbffffae7
0xbffff988 環境変数のポインタ:0xbffff988

引数の2〜4番目は引数の数を参考に一番目の引数のポインタから引数分POPしてやると取り出せるようです。

(gdb)x/xw $ebp+12 x/xw $ebp+12
0xbffff8d4:	0xbffff974
(gdb) x/x 0xbffff974
0xbffff974:	0xbffffae7
(gdb) x/s 0xbffffae7
0xbffffae7:	 "/home/hogehoge/hoge"
(gdb) 
0xbffffb08:	 "1"
(gdb) 
0xbffffb0a:	 "2"
(gdb) 
0xbffffb0c:	 "3"

次回は、変数宣言を調査したいと思います。

参考URL
http://questionbox.jp.msn.com/qa5902352.html
http://scientia.jpn.org/index.php?%A5%B3%A5%F3%A5%D4%A5%E5%A1%BC%A5%BF%B9%D6%BA%C2%2F%A5%B9%A5%BF%A5%C3%A5%AF
http://www.c-tipsref.com/words/stackframe.html
http://hp.vector.co.jp/authors/VA014520/asmhsp/chap4.html
http://d.hatena.ne.jp/eagletmt/20080712/1215841076
http://hack.ninja-web.net/academy003-060.htm
http://hack.ninja-web.net/academy002-051.htm
http://hack.ninja-web.net/academy002-052.htm
http://hack.ninja-web.net/academy002-053.htm

*1:関数呼び出し時に呼び出し元のアドレスや引数等をまとめたもの