スタック Edit

目次 Edit

スタックとは Edit

LIFO(Last In First Out)形式でデータを出し入れするデータ領域。
push命令やpop命令でデータの出し入れをし、ESP?レジスタでメモリのアドレスを指定する。

スタックの動作(スタックポインタや一回の出し入れで処理するバイト数など)はセグメントの属性によって変わり、

  • スタックポインタ(sp/esp)は、SS?レジスタが指すセグメントのD/Bフラグによる。
  • 処理するバイト数(2/4Byte)は、CS?レジスタが指すセグメントのD/Bフラグによる。

32ビット幅のスタックに16ビットの値をpushすると、アライメントが合わなくなり、著しく処理能力が落ちるだけでなく、場合によっては障害になることもある。
例外として、セグメントレジスタをpushした場合は、プロセッサが自動でアライメントを合わせる。

スタックの動き Edit

以下は、32bitモード時の一般的なスタックの動き。
popしても基本的にはデータが消えることはなく、次にpushするときに上書きする。

push 0x89abcdef Edit

定数"0x89abcdef"をpushする。

0xfd
push前esp->0xfc
0xfb0x89abcdef
0xfa
0xf9
push後esp->0xf8
0xf7
0xf6

call _Func Edit

サブルーチン"_Func"をcallする。

0xfd
0xfc
0xfb0x89abcdef
0xfa
0xf9
call前esp->0xf8
0xf7callした時のEIP?
0xf6
0xf5
call後esp->0xf4
0xf3
0xf2

ret Edit

サブルーチン"_Func"からretする。

0xfd
0xfc
0xfb0x89abcdef
0xfa
0xf9
ret後esp->0xf8
0xf7callした時のEIP?
0xf6
0xf5
ret前esp->0xf4
0xf3
0xf2

pop x Edit

pushした定数"0x89abcdef"をレジスタ"x"にpopする。

0xfd
pop後esp->0xfc
0xfb0x89abcdef
0xfa
0xf9
pop前esp->0xf8
0xf7callした時のEIP?
0xf6
0xf5
0xf4
0xf3
0xf2

c/c++言語におけるスタックの動き Edit

c言語やc++言語では、引数やローカル変数があり、それらの変数はスタックを利用しています。

int		main( int argc, char **argv )
{
	int		i_a = 1;
	int		i_b = 2;
	i_a = Func( i_b );		//i_aに入れる意味は特にない。
}

int		Func( int i_value )
{
	int		i = 2;
	char	ch = 5;
	short	sh = 3;
	return i_value + i + ch + sh;		//全部足して返すだけ。
}

このcソースをアセンブラに直すと、次のような感じになります。多分。
gccは少し勝手が違いました。

_main:
	pushl	%ebp				#ebpをスタックに格納
	movl	%esp,		%ebp		#espの値をebpにコピー。

	subl	$8,		%esp		#ローカル変数のバイト数分espの値を引く
	movl	$1,		-4(%ebp)	#ローカル変数:i_aを1で初期化
	movl	$2,		-8(%ebp)	#ローカル変数:i_bを2で初期化

	pushl	-8(%ebp)			#ローカル変数:i_bを引数としてスタックに格納
	call	_Func				#関数:Func()を呼び出す。
	movl	%eax,		-4(%ebp)	#関数;Func()の戻り値を引数:i_aに格納

	movl	%ebp,		%esp		#espを復元
	popl	%ebp				#スタックからebpを復元
	ret

_Func:
	pushl	%ebp				#ebpをスタックに格納
	movl	%esp,		%ebp		#espの値をebpにコピー。

	subl	$12,		%esp		#ローカル変数のバイト数分espの値を引く
	movl	$2,		-4(%ebp)	#ローカル変数:iを2で初期化
	movl	$5,		-8(%ebp)	#ローカル変数:chを5で初期化
	movl	$3,		-12(%ebp)	#ローカル変数:shを3で初期化

	movl	8(%ebp),	%eax		#引数:i_valueをeaxに格納
	addl	-4(%ebp),	%eax		#ローカル変数:iをeaxに加算
	addl	-8(%ebp),	%eax		#ローカル変数:chをeaxに加算
	addl	-12(%ebp),	%eax		#ローカル変数:shをeaxに加算

	movl	%ebp,		%esp		#espを復元
	popl	%ebp				#スタックからebpを復元
	ret					#戻り値はeaxで返す。

main関数実行~Func関数の途中まで Edit

  1. pushl %ebp
  2. movl %esp, %ebp
  3. subl $8, %esp
  4. pushl -8(%ebp)
  5. call _Func
  6. pushl %ebp
  7. movl %esp, %ebp
  8. subl $12, %esp
espebpAddrMemory
0xfc
0xfbebp:?
0xfa
0xf9
1.pushl %ebp2.movl %esp, %ebp0xf8
0xf7ローカル変数:i_a = 1
0xf6
0xf5
0xf4
0xf3ローカル変数:i_b = 2
0xf2
0xf1
3.subl $8, %esp0xf0
0xef引数:i_value (i_b)
0xee
0xed
4.pushl -8(%ebp)0xec
0xebcall時のEIP
0xea
0xe9
5.call _Func0xe8
0xe7ebp:0xf8
0xe6
0xe5
6.pushl %ebp7.movl %esp, %ebp0xe4
0xe3ローカル変数:i = 2
0xe2
0xe1
0xe0
0xdfローカル変数:ch = 5
0xde
0xdd
0xdc
0xdbローカル変数:sh = 3
0xda
0xd9
8.subl $12, %esp0xd8
0xd7

Func関数の途中からFunc関数のreturnまで Edit

  1. movl %ebp, %esp
  2. popl %ebp
  3. ret
espebpAddrMemory
0xfc
0xfbebp:?
0xfa
0xf9
2.popl %ebp0xf8
0xf7ローカル変数:i_a = 1
0xf6
0xf5
0xf4
0xf3ローカル変数:i_b = 2
0xf2
0xf1
0xf0
0xefゴミ。引数:i_value (i_b)
0xee
0xed
0xec
0xebcall時のEIP
0xea
0xe9
3.ret0xe8
0xe7ebp:0xf8
0xe6
0xe5
1.movl %ebp, %esp0xe4
0xe3ゴミ。ローカル変数:i = 2
0xe2
0xe1
0xe0
0xdfゴミ。ローカル変数:ch = 5
0xde
0xdd
0xdc
0xdbゴミ。ローカル変数:sh = 3
0xda
0xd9
0xd8
0xd7

まとめ Edit

c言語やc++言語では、
espを、callやret、ebpの基準に使い、
ebpを、引数やローカル変数の基準に使います。

ebpより上に引数が、
ebpより下にローカル変数があります。


Since 2008 July. OS Project Wiki
リロード   新規 下位ページ作成 編集 凍結 差分 添付 コピー 名前変更   ホーム 一覧 検索 最終更新 バックアップ リンク元   ヘルプ   最終更新のRSS