9

これは、ステートメントの逆アセンブルによって私が見るものですfunction(1,2,3);:

movl   $0x3,0x8(%esp)
movl   $0x2,0x4(%esp)
movl   $0x1,(%esp)
call   0x4012d0 <_Z8functioniii>

ret アドレスがまったくスタックにプッシュされていないようですが、どのように機能しますretか?

4

4 に答える 4

10

x86 プロセッサでは (アセンブリ言語の例のように)、call命令はリターン アドレスをスタックにプッシュし、制御を関数に移します。

そのため、関数へのエントリでは、スタック ポインターは戻りアドレスを指してretおり、プログラム カウンター (EIP / RIP) にポップする準備ができています。


すべてのプロセッサ アーキテクチャがリターン アドレスをスタックに置くわけではありません。多くの場合、リターン アドレスを保持するように設計された 1 つまたは複数のレジスタのセットがあります。ARM プロセッサでは、BL命令は戻りアドレスを特定のレジスタ (LRまたは「リンク レジスタ」) に配置し、制御を関数に渡します。

ia64 プロセッサも同様のことを行いますが、戻りアドレスを受け取ることができるレジスタ ( b0- ) がいくつかあり、そのうちの 1 つが命令で指定されます (これがデフォルトです)。b7b0

于 2010-03-30T05:35:43.310 に答える
6

理想的には、callステートメントはそれを処理する必要があります。プログラム カウンターの次の位置がスタックにプッシュされます。呼び出された関数 (サブルーチン) が動作を完了し、return ステートメントに遭遇すると、コントロールはスタックにプッシュされたアドレスに移動し、ポップされます。

于 2010-03-30T05:02:34.507 に答える
3

callこれは ABI とアーキテクチャに依存しますが、リターン アドレスがスタックに置かれる場合は、そこに置く命令の副作用です。

于 2010-03-30T05:01:59.457 に答える
1

callは RIP レジスタの現在の値 (戻りアドレス) をスタックにプッシュします + 呼び出し
retは戻りアドレス (プッシュされた呼び出し) をスタックの一番上からポップし (RSP レジスタはそこを指します)、RIP レジスタに書き込みます。

GNU/Linux ボックスでの例: 関数 f は関数 g を呼び出し、g のフレームを見てみましょう。

ローアドレス

... <- RSP (スタック ポインタはスタックのトップを示します) レジスタはこのアドレスを指します
g のローカル変数
f のベース ポインタ (古い RBP 値) <- RBP (ベース ポインタ) レジスタはこのアドレスを指します
f の ret アドレス (古い RIP 値) (これは(fからの)呼び出しがプッシュしたものであり、(gからの) retがポップするものです)
fがgを呼び出してレジスタに収まらなかった引数(Windowsではこれは異なると思います)
...

ハイアドレス

g はローカル変数を解放します (movq %rsp, %rbp)
g は「古い RBP」をポップし、RBP レジスターに格納します (pop %rbp)
g retは、RSP が指す場所に格納されている値で RIP を変更しますで

それが役に立てば幸い

于 2014-06-05T12:45:56.270 に答える