コンテンツ
キーレジスタの2つ:ebp、esp。これらの2つのレジスタは、関数スタックフレームを維持するために使用されるアドレスを格納します
まず、各関数呼び出しはスタック領域にスペースを開く必要があることを理解する必要があります
1.変数を作成するときに値を割り当てます。変数が割り当てなしで作成される場合、デフォルトはCCCCCCCCです。
関数内に入力します(最初の数ステップの操作の順序は、前のメイン関数と同じです)
正式なパラメータはAdd関数内では作成されませんが、計算のためにスタック上のスペースデータを見つけるために戻ってきます
仮パラメータは実際のパラメータのゼロ回コピーであるとよく言われますが、この文は完全に正しいです。仮パラメータを変更しても、実際のパラメータには影響しません。
疑問がありますか?return zが戻ったとき、zは破棄されませんか?どうすれば結果を戻すことができますか?
関数に戻ってローカル変数cに入れるときに、最初にeaxレジスタに入れます。
2.ローカル変数の初期化されていない値がランダムな値である理由
3.関数はどのようにパラメーターを渡しますか?パラメータが渡される順序は何ですか?
注:次の環境は、表示用のvs2013に基づいています
関数スタックフレームの作成と破棄
学習の過程で多くの疑問があるはずです、ここにいくつかの例があります:
・ローカル変数はどのように作成されますか?
ローカル変数の初期化されていない値がランダムな値であるのはなぜですか?
関数はどのようにパラメーターを渡しますか(パラメーターを渡す順序は何ですか)?
正式なパラメータと実際のパラメータの関係は何ですか?
・関数呼び出しはどのように行われますか?
・呼び出しの終了後、関数はどのように戻りますか?
次に、問題を解決します
1.登録する
レジスタがCPUに統合されているコンピュータのストレージシステム
以下で遭遇するレジスタは、eax、ebx、ecx、edxです。
キーレジスタの2つ:ebp、esp。これらの2つのレジスタは、関数スタックフレームを維持するために使用されるアドレスを格納します
まず、各関数呼び出しはスタック領域にスペースを開く必要があることを理解する必要があります
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
return 0;
}
メイン関数の呼び出しを開始し、メイン関数にスペースを割り当てます。どの関数が呼び出されるか、ebpとespはその関数の関数スタックフレームを維持します。
Add関数、ebpおよびesp runを呼び出して、Add関数を維持したいのですが、ebpとespの間のスペースは、関数スタックフレームと呼ばれるAdd関数呼び出しのために開かれたスペースです。
スタック領域の使用法は、最初に上位アドレスを使用し、次に下位アドレスを使用することであるため、データを配置するときは、データを上から下に配置します。これにより、espはスタックのトップポインタとして理解できます。 ebpは、スタックの下位ポインタとして理解されます。
メイン関数が呼び出されているのに、誰がメイン関数を呼び出しているのか疑問に思ったことはありませんか?
下に移動してコードを実行すると、__ tmainCRTStartupが表示されます。この関数は内部でメイン関数を呼び出し、メイン関数が他のユーザーからも呼び出されていることを示しています。__tmainCRTStartupはtmainCRTStartupによって呼び出されています。
これらの2つの機能のためのスペースも、メイン機能の前に割り当てる必要があります
アウトラインはおそらくこんな感じで、次の研究の呼び方
分解する
main関数は他のユーザーからも呼び出されており、main関数を呼び出す__tmainCRTStartupはすでにスペースを作成しています。
最初の文を分解します(プッシュ:スタックの一番上に要素を置きます)(ポップ:スタックの一番上から要素を削除します)
ebpは__tmainCRTStartupスタックの下位アドレスを格納します。プッシュはスタックのプッシュと呼ばれ、要素をスタックに配置します
プッシュ後、espのアドレスも変更する必要があります
モニター
espとebpを見てください、これは最初のアドレスです
プッシュを開始すると、アドレスが4減少します
羊
ebpが実際にプッシュによってプッシュされていることがわかります
2番目の文を分解します
mov命令は、次の値を前面に割り当て、espをebpに割り当てることです。
3番目の文を分解します
subは減算であり、espから0E4hを減算し、0E4hを10進数で228に変換します。
モニタリング(特にアドレス変更、メイン機能のための大きなスペースの開放)
4番目、5番目、6番目の文を分解します
7番目、8番目、9番目、および10番目の文を分解します。lea=実効アドレスのロード(実効アドレスのロード)
次の有効なアドレスをediにロードします。この文は簡単にはわかりません。シンボル名を表示すると、ebp-0E4hになります。
movは39hをecxに入れます。0CCCCCCCCchの値をeaxに入れます
Rep stosは、ediの位置から始まり、39時間下がる非常に多くのスペースdwordのデータコンテンツを0CCCCCCCCchに変更する必要があることを意味します。
ワードは2バイトで、dwordは4バイトです
ediからebpstopまで、すべてのスペースの内容がCCCCCCCCに初期化されます
メモリモニタリング
この時点で、メイン関数スタックフレームが開かれ、コードの実行が正式に開始されます。
mov、ebp-8の位置に0Ah(10進数で10)を置くと、CCCCCCCCは4バイトであり、aのために開かれたスペースはebpからebp-8までのスペースであると考えられます。
1.変数を作成するときに値を割り当てます。変数が割り当てなしで作成される場合、デフォルトはCCCCCCCCです。
印刷が高温になる理由は、初期化されていないランダムな値CCCCCCCCであるためです。コンパイラが異なれば、異なる値が入力される可能性があります。
メモリモニタリング
0a 00 00 00は、このコンパイラがリトルエンディアンであるためです
メモリモニタリングb
14hは16進数で、10進数に変換すると20になり、2つの整数位置は空のままになります
メモリモニターc
それは2つの位置の違いです
関数呼び出し
関数呼び出しはパラメーターを渡す必要があります。次の2つのアクションはパラメーターを渡しますか?
mov、ebp-14h(つまり、b)をeaxに入れます。push-> eax、スタックをプッシュ
mov、ebp-8(つまり、a)をecxに入れます。push-> ecx、スタックをプッシュ
答えは確かに合格です
呼び出し命令は関数を呼び出すことです。これで、呼び出し命令のアドレスを覚えています。
呼び出し命令を実行した後、AA8のアドレスは00 c21450です。
呼び出し命令の次のアドレスがスタックにプッシュされますが、なぜこのアドレスを覚えておく必要があるのですか?心配しないでください
Add関数は、呼び出しが呼び出された直後に呼び出されます。Add関数が呼び出された後、戻る必要があります。呼び出し命令の次の命令に戻る必要があります(呼び出し命令が実行されます)。
関数内に入力します(最初の数ステップの操作の順序は、前のメイン関数と同じです)
追加機能用のスタックフレームを準備します
最初にebpをプッシュし(この時点では、ebpはまだメイン関数スタックを低く維持しています)、ebp値を一番上にプッシュします
メモリ監視(ebpは、呼び出し命令の下の次の命令のアドレスでスタックにプッシュされます)
espの値をebpに与える
esp、0CChは、このAddの呼び出しに関数スタックフレームスペースを割り当てています
関数ロードアドレス
leaはアドレス[ebp+FFFFFF34h]をediにロードし、movは33hをecxに入れ、次に0XCCCCCCCChの値をeaxに入れます
rep->ediからebpまでのすべての位置が0XCCCCCCCChに初期化されます
コンピューティングタスクを実行する
ebp-8の位置に0を入れます
加算は実行されますが、x、yはどこにありますか?
ebp + 8の値をeaxに入れると、ebp+8はecxを見つけます
ecxはスタックにプッシュされ、彼をa'と呼ぶことができ、eaxはb'です。
ebp + 8の値をeaxに入れ、次にebp+0chの値をeaxに追加します
追加が完了し、eaxは30になります
正式なパラメータはどのようにして生まれましたか?パラメータを積極的に作成していますか?
いいえ、最初に以下の関数を呼び出したときにパラメータを渡したためです
mov pushmovpush命令を介したパラメーターの受け渡し
プッシュプッシュbを最初に押し、次にaを押します。c = Add(a、b)bが最初に渡され、aが渡され、パラメーターが右から左に渡されます
正式なパラメータはAdd関数内では作成されませんが、計算のためにスタック上のスペースデータを見つけるために戻ってきます
仮パラメータは実際のパラメータのゼロ回コピーであるとよく言われますが、この文は完全に正しいです。仮パラメータを変更しても、実際のパラメータには影響しません。
関数リターン
疑問がありますか?return zが戻ったとき、zは破棄されませんか?どうすれば結果を戻すことができますか?
return zは、ebp-8の値をレジスターであるeaxに入れることを意味します。プログラムの終了時にレジスタは破棄されません
ebp-8の位置はzです
3つの文をポップし、ポップし、スタックの一番上の要素をポップしてediに入れます。espがポップされるたびに、++がダウンします。
movはebpをespに割り当てます。espはスタックの最上位を指しませんが、ebpの場所を指します(追加機能のスタックフレームをリサイクルします)
それをポップすると、ebp-main関数のスタックローアドレスが以前に保存されていました(関数が破棄された後にスタックローポインターが見つからないようにするため)、ebpはメイン関数スタックローアドレスに戻ります
この時点で、popがポップアップした後、ebpはメイン関数スタックに戻ります。
ret命令、espは、呼び出し命令の次の命令のアドレスである00C21450のアドレスに戻り、呼び出し命令の次の命令から実行を継続します(出て行くだけでなく、戻ってくる)。
espに8を追加し、パラメーターa、bをスキップし、パラメーターaおよびbを破棄します。
eaxの値をebp-20hに入れ、ebp-20hの位置はcのスペースであり、eaxの値は合計です:30
戻り値はどのように戻されますか?
関数に戻ってローカル変数cに入れるときに、最初にeaxレジスタに入れます。
Add関数が破棄され、main関数が破棄されます。これは、関数スタックフレームを作成および破棄するプロセスです。
元の質問に戻る
1.ローカル変数はどのように作成されますか?
まず、関数にスタックフレームスペースを割り当てます。スタックフレームスペースがスペースの一部を初期化した後、スタックフレーム内のローカル変数に少しスペースを割り当てます。
2.ローカル変数の初期化されていない値がランダムな値である理由
ランダム値はCCCCCCCCなどのコンパイラによって入力されるため、初期化するとランダム値が上書きされます
3.関数はどのようにパラメーターを渡しますか?パラメータが渡される順序は何ですか?
関数を呼び出していないときは、スタックを右から左にプッシュしています。仮パラメーター関数に入ると、Add関数で、ポインターのオフセットから仮パラメーターを見つけます。
4.正式なパラメータと実際のパラメータの関係は何ですか?
仮パラメーターがスタックにプッシュされたときに作成されるスペースは、実際のパラメーター値と同じであり、スペースは独立しており、仮パラメーターは実際のパラメーターのゼロ回コピーです。この文は完全に正しいです。変更仮パラメータは実際のパラメータには影響しません。
5.関数呼び出しはどのように行われますか?
上記は明確に要約します
6.関数呼び出しの結果はどのように返されますか?
呼び出しの前に、呼び出し命令の次の命令のアドレスを覚えて、ebp呼び出し関数の前の関数のスタックフレームを保存します。関数呼び出しの後に関数が戻ったら、ebpをポップアップして、のebpを見つけます。元の前の関数。ポインタが下がってespアドレスを見つけます
戻り値はレジスタを介して戻されます
関数内で作成された静的変数はグローバルに開かれます。今日は、スタック領域を利用します。