知っておくべきこと:
- EIP は、次に実行する命令を指すレジスタです。
関数を呼び出すと、引数と EIP (呼び出された関数がどこに戻るかを知るため) がスタックに保存されます。
コンパイラは、(明示的または暗黙的に) フレーム ポインターを使用するように指示されると、フレーム ポインターをスタックに (EBP レジスターに) 保存します (これにより、後でフレーム ポインターを呼び出し元の関数の値に戻すことができます)。をクリックし、フレーム ポインターを現在のスタックの一番上を指すように設定します。これにより、既知の参照ポイント (フレーム ポインター) から引数とローカル変数に簡単にアクセスでき、デバッグが大幅に簡素化されます。
- 次に、ローカル変数用にスペースが確保され、関数が実行されます。
- 関数から戻ると、前のフレーム ポインタと命令ポインタが復元されます。
x86 での関数呼び出しは次のようになります。
...
int main() add $-0x8,%esp ; alignment
{ push $0x2 ; arg 2
... push $0x1 ; arg 1
func(1, 2); call func ; function call
... add $0x10,%esp ; pop args from stack
} ...
呼び出された関数は次のようになります。
void func(int arg1, int arg2) push %ebp ;\
{ mov %esp,%ebp ;/ create stack frame
int local1; sub $0x18,%esp ; reserves space
... ...
} mov %ebp,%esp ;\
pop %ebp ;/ destroys frame
ret ; returns
したがって、スタックは次のようになります。
: :
+-----------+
: alignment :
+-----------+
12(%ebp) | arg2 |
+-----------+
8(%ebp) | arg1 |
+-----------+
4(%ebp) | ret | -----> return address
+-----------+
(%ebp) | ebp | -----> previous ebp
+-----------+
-4(%ebp) | local1 | -----> local vars
+-----------+
: alignment :
+-----------+
: :
(アスキーアートでは下位アドレスが下位)