スタック操作でスタックポインタの位置を見る
関数を呼び出すときや呼び出し元に戻るときなどにスタックポインタというものを利用しているらしいのですが、どうもスタックポインタが具体的にメモリ上にどこら辺にあるのか出力させてみなければはっきりとわかりません。そういうことでプログラムを書いて実際にスタックポインタの値を出してみましょう。
- stack.asm
;; スタックのpush/popのesp(スタックポインタ)の変化の観察 ;; 2005/May/13 ;; $ nasmw -f win32 stack.asm [extern _addr1] [extern _addr2] [extern _addr3] [extern _addr4] [extern _addr5] [global _search_address] [BITS 32] [section .text] _search_address: mov [_addr1], esp push ebx mov [_addr2], esp push ebx mov [_addr3], esp pop ebx mov [_addr4], esp pop ebx mov [_addr5], esp ret
- stack.c
/* スタックのpush/popのesp(スタックポインタ)の変化の観察 2005/May/13 $ gcc stack.obj stack.c */ #include <stdio.h> #include <stdlib.h> extern int search_address(); int addr1 = 0; int addr2 = 0; int addr3 = 0; int addr4 = 0; int addr5 = 0; int main(void) { search_address(); puts("search_address() called"); printf("addr1 = %p\n", addr1); puts("pushed"); printf("addr2 = %p\n", addr2); puts("pushed"); printf("addr3 = %p\n", addr3); puts("poped"); printf("addr4 = %p\n", addr4); puts("poped"); printf("addr5 = %p\n", addr5); return EXIT_SUCCESS; }
そしてコンパイルしてみます。
$ nasm -f win32 stack.asm $ gcc stack.obj stack.c
できた実行ファイルを動かしてみます。
$ ./a search_address() called addr1 = 0073FDCC pushed addr2 = 0073FDC8 pushed addr3 = 0073FDC4 poped addr4 = 0073FDC8 poped addr5 = 0073FDCC
見事にスタックポインタのメモリ上の位置がとれてます。まず、何も呼び出さないときは0073FDCCという場所にあり、pushを一回実行したら位置が-4になります。またpushしたらまた-4になります。popは逆に+4になることもわかります。
ffffffff : 0073FDCC ↓push 呼び出されたときのespの値 0073FDC8 0073FDC4 ↑pop : 00000000
見事に実験成功です。スタックをpush/popによって操作するたびにスタックポインタの値が変化する様子を手に取るようにわかります。何も操作しないときには、引数の関係でC言語の標準の呼び出し規約は関数の第一引数は呼び出される側は[esp+4]に代入されますが、スタックを操作するとこのespが変化するためにesp+4+4*(pushした回数)としなければならないことがわかります。