まずひとつ目は「espをpushするときにespはpushする前に値が変更されるのか、それともpushした後に変更されるのか」という疑問です。
答えを言ってしまうとespがpushされた後に値は変更されます。当然ですけど。
つまり、espの値を表示する関数などを作りたいときにはそのままpushしてから表示すればいいことになります。かんたんですね。
テストコードとしては下のようなものが挙げられます。
printというのはprintfの自作バージョンです。
あと、cdeclの呼び出し規約に基づいてprint関数呼び出し直後に「addl $8, %esp」でespを調節しています。(引数は2個なので4 * 2 = 8)
stack_test1ではただ単に「pushl %esp」をしてその時のespの値を表示し、stack_test2でははじめに「movl %esp, %eax」としてeaxにespの値を保存してから「pushl %eax」としてespの値をpushしています。
なので、espの値が変更される前にespがpushされるのであれば同じ値が表示されるはずです。
.code32 .globl stack_test .extern print .text stack_test: pushl %ebp movl %esp, %ebp stack_test1: pushl %esp pushl $debug call print addl $8, %esp stack_test2: movl %esp, %eax pushl %eax pushl $debug call print addl $8, %esp stack_test_end: leave ret .data debug: .string "esp = %x\n"
僕の環境では実行結果は次のようになりました。やったね。
esp = FFE49228 esp = FFE49228
そして2つ目は「push命令では値をpushするのが先か、espを減算するのが先か」という疑問です。(pop命令についてもその逆)
これがわからないと、例えば「pushl %eax」したあとにこの値を参照しようとしたときに「(%esp)」で参照したらいいのか「0x4(%esp)」で参照したらいいのかわからなくなってしまいます。
これも結果を先にいってしまうと、「pushはespを減算した後に値を格納」「popは値を受け取った後にespを加算」となります。
実験のコードを載せておきます。
pushしたあとに(%esp)をしらべて、予想した値になっているかを調べます。
もしpushが値を格納した後にespを減算するのであれば、これは予想と異なる値(0x12345678とは異なる値)になるはずです。
popについても同じような感じで、0x12345678以外の値が表示されれば良いことになります。
.code32 .globl stack_test .extern print .text stack_test: pushl %ebp movl %esp, %ebp stack_test_start: pushl $0x12345678 movl (%esp), %eax pushl %eax pushl $debug2 call print addl $0x8, %esp popl %eax movl (%esp), %eax pushl %eax pushl $debug2 call print addl $0x8, %esp stack_test_end: leave ret .data debug: .string "esp = %x\n" debug2: .string "(%%esp) = %x\n"
このプログラムの実行結果は次のようになります。
(%esp) = 12345678 (%esp) = FFD9D4D8
結論を言ってしまうと「espをpushする際は、pushという動作でespが変更される前のespの値がpushされる」「pushでは減算した後に値をスタックに押し込み、popでは値を受け取った後に加算をする」ということがわかります。