c++ - 特徴 - スタックフレームとは




メソッド/関数を呼び出すと、アセンブリ言語ではどうなりますか? (8)

アセンブリではどうなりますか?

簡単な説明:現在のスタック状態が保存され、新しいスタックが作成され、実行される関数のコードがロードされて実行されます。 これは、あなたのマイクロプロセッサのいくつかのレジスタに迷惑をかけることと、狂ったやり方でメモリに読み書きすることと、いったん終了すると、呼び出し関数のスタック状態が復元されます。

私がC ++ / Cで(言語はそれほど重要ではなく、単に概念を説明するために)必要なプログラムを持っていれば:

#include <iostream>    

void foo() {
    printf("in foo");
}

int main() {
    foo();
    return 0;
}

アセンブリではどうなりますか? 私は実際にアセンブリコードを探しているわけではありません。なぜなら、まだそれほど遠くにあるわけではありませんが、基本原則は何ですか?


1-呼び出しコンテキストがスタック上に確立される

2つのパラメータがスタックにプッシュされます

3-「コール」がメソッドに対して実行される


あなたはそれをあなた自身のために見ることができます:

Linuxでは、以下を使ってプログラムをコンパイルします。

gcc -S myprogram.c

そして、アセンブラでプログラムのリストを取得します(myprogram.s)。

もちろん、アセンブラについて理解するのは少し分かっているはずです(ただし、コンピュータの仕組みを理解するのに役立ちますので、学習する価値があります)。 関数を呼び出す(x86アーキテクチャでは)基本的には:

  • スタックに変数aを入れる
  • 変数bをスタックに入れる
  • 変数nをスタックに入れる
  • 関数のアドレスにジャンプする
  • スタックから変数をロードする
  • 機能を果たす
  • クリーンスタック
  • メインに戻る

一般的な考え方は、呼び出し元のメソッドで使用されるレジスタがスタックにプッシュされる(スタックポインタがESPレジスタにある)ことです。このプロセスは「レジスタをプッシュする」と呼ばれます。 時にはそれらもゼロになっていますが、それは依存しています。 アセンブリプログラマは、関数内でより多くの可能性を持たせるために、より多くのレジスタを解放し、共通の4( EAXEBXECX 、およびEDX on x86)を解放する傾向があります。

関数が終了すると、逆の場合も同じことが起こります。スタックは呼び出し前の状態に復元されます。 これは「レジスタをポップする」と呼ばれます。

更新:このプロセスは必ずしも実行する必要はありません。 コンパイラは、それを最適化して関数をインライン化することができます。

更新:通常、関数のパラメータはスタックの逆順にプッシュされ、スタックから取得されると通常の順序で表示されます。 この注文はCによって保証されていません(ref: Inner Loops by Rick Booth)


何が起こるのですか? x86では、main関数の最初の行は次のようになります。

call foo

call命令は、スタック上の戻りアドレスをプッシュし、次にjmpをfooの位置にプッシュします。


何が起こるのですか?

Cはアセンブリで何が起こるかを模倣します...

それはマシンにとても近く、何が起こるかを理解することができます

void foo() {
    printf("in foo");

/*

db mystring 'in foo'
mov eax, dword ptr mystring
mov edx , dword ptr _printf
push eax
call edx
add esp, 8
ret
//thats it
*/

}

int main() {
    foo();
    return 0;
}






function