2

だから私はシステムの中期を勉強しています、そして同様のコースの古い中期はこの問題を抱えていました、Cとアセンブリコードは次のとおりです:

int gcd(int a, int b)
{
    if(!b)
    {
        return a;
    }
    return gcd(b, a%b);
}

0x08048394 <+0>: push %ebp
0x08048395 <+1>: mov %esp,%ebp
0x08048397 <+3>: sub $0x10,%esp
0x0804839a <+6>: mov 0x8(%ebp),%eax
0x0804839d <+9>: mov 0xc(%ebp),%ecx
0x080483a0 <+12>: test %ecx,%ecx
0x080483a2 <+14>: je 0x80483b7 <gcd+35>
0x080483a4 <+16>: mov %eax,%edx
0x080483a6 <+18>: sar $0x1f,%edx
0x080483a9 <+21>: idiv %ecx
0x080483ab <+23>: mov %edx,0x4(%esp)
0x080483af <+27>: mov %ecx,(%esp)
0x080483b2 <+30>: call 0x8048394 <gcd>
0x080483b7 <+35>: leave
0x080483b8 <+36>: ret

%espの開始値は0xffff1000であり、gcd(213、18)の結果はgcd(213、18)、gcd(18、15)、gcd(15、3)、およびgcdになることがわかります。 (3、0)。次に、gcd(15、3)の戻り命令を実行する前に、%espの値を尋ねます。

解決策はそれが0xffff0fccであることを示しています。理由がよくわかりません。これが私の推論です:

0x10を3回減算し、gcd(18、15)とgcd(15、3)を呼び出しました。これらを組み合わせて、スタックから0x30と0x8を減算する必要があります。だから私たちは0xffff0fc8にいるべきではありませんか?次に、戻った後、$ espが0xffff0fccになるように、もう一度0x4を追加しますが、前ではありませんか?

4

1 に答える 1

1

espこの行の直前に = 0xffff1000を意味する場合:

0x08048394 <+0>: push %ebp

次に、から減算しespます。

4: 0x08048394 <+0>: push %ebp
16: 0x08048397 <+3>: sub $0x10,%esp
4: 0x080483b2 <+30>: call 0x8048394 <gcd>; gcd(18, 15)

その後:

4: 0x08048394 <+0>: push %ebp
16: 0x08048397 <+3>: sub $0x10,%esp
4: 0x080483b2 <+30>: call 0x8048394 <gcd>; gcd(15, 3)

その後:

0: because "leave" undoes "sub $0x10,%esp" and "push %ebp"

これにより、0xffff1000 - 2 * (4 + 16 + 4) = 0xffff0fd0 が得られます。

espメインプログラムの前にOTOH = 0xffff1000の場合、call 0x8048394 <gcd>; gcd(213, 18)上記の値は戻りアドレスのサイズ4だけ減少し、0xffff0fccが得られます。

問題のステートメント (意味を変更していないと仮定して) は少しあいまいです。gcd(213, 18)私も、メインプログラムでの呼び出しであっても、パラメーターのサイズを数えます。しかし、それから別の質問があります。以前にメイン プログラムによって実行されたスタック アライメントはありますgcd(213, 18)か? それもカウントする必要がありますか? gcd()関数がパラメーターを再帰的に渡すために 8 バイトのみを使用するが、16 を割り当てるため、内部にスタック アラインメントがあることに注意してください。

この問題を正しく解決する (= 教師が期待する数を取得する) には、コードのどの時点でesp= 0xffff1000 かを明確に示し、そのコードが含まれていることを確認する必要があります。

于 2012-10-18T09:09:31.760 に答える