2

Assembly で次の C コードを記述します。

int main(void)
{
    int x,y;
    scanf("%d%d",&x,&y);
    printf("%d%d",x,y);
    return 0;
}

まず、スキャン/印刷する整数を 1 つだけ使用してみました。

.section    .rodata #read only data section
fmt:    .string "%d%d"
    .text   
.globl  main    
    .type   main, @function
main:   
pushl   %ebp    #save the old frame pointer
movl    %esp,   %ebp    #create the new frame pointer

pushl %esp #location of x
pushl $fmt
call scanf

    #stack should now have exactly the scanned number x and then the format, as needed for printf.
call printf

movl    $0, %eax
movl    %ebp,   %esp    #restore the old stack pointer - release all used memory.
popl    %ebp    #restore old frame pointer (the caller function frame)
ret 

しかし、うまくいきませんでした。何らかの理由で、次のトリックが機能しました(printfの前に追加されました):

addl $4,%esp #pop format
pushl 4(%esp)
pushl $fmt

pushl 4(%esp) が機能する理由がわからないので、最初の質問でその点について説明を求めています。次に、2 つの変数を使用して同じことを試みました。

fmt:    .string "%d%d"
[...]
    pushl %esp #location of x
    pushl %esp #location of y
    pushl $fmt
    call scanf

しかし、それはセグメンテーションエラーを引き起こしました。次のようなことを試みたであろうprintf部分にも到達しませんでした:

addl $4,%esp #pop format    
pushl 8(%esp)
pushl 8(%esp)
pushl $fmt

call printf

(以前の pushl 4(%esp) と同じロジックに従います。私の 2 番目の質問は、2 つの変数で機能させるにはどうすればよいかということです。ありがとう!

編集: 2 つの変数をスキャンするために次のコードが機能しないのはなぜですか?

subl $8,%esp #leave place for two vars
pushl -4(%ebp) #location of x
pushl -8(%ebp) #location of y
pushl $fmt
call scanf
4

2 に答える 2

1

「%esp を 4 減算してから、esp が指している場所に %esp を保存する必要があります」

これは 8086 CPU で発生していました。最初に変数を割り当て(1回のプッシュまたは1回のesp-4)、次にこの変数のアドレスを保存する(2回目のプッシュ)必要があります。最初の質問では、3 回プッシュするか、サブ 1 回と 2 回プッシュする必要があります。あなたの場合、 esp は古い EBP が保存されたスタックの場所を指しています。使用できます

push eax
push esp
push fmt

これも同様に機能します。

また、2番目の問題については、関係のない行を参照しましたが、

そうそう、間違ったコード行をコピーしました。申し訳ありません。私はこの作品を参照していました:

pushl %esp #location of x
pushl %esp #location of y
pushl $fmt
call scanf

あなたのコードが間違っている理由を指摘しました。変数の 2 つのアドレスをプッシュする必要があります。代わりに、古い EBP のアドレスをプッシュしてから、prev パラメータ (古い EBP を指す) を使用してスタック内のセルのアドレスをプッシュします。その結果、読み取り時に 1 つのパラメーターが台無しになり、scanf で入力した値を受け取ります。セルのアドレスの代わりに別の値を書きたい場合は、前の int があります。

最後に、提案されたコードについて説明していただけますか? edx と eax を esp に移動する理由

申し訳ありませんが、これはインテルの構文です。したがって、mov eax, esp は「esp を eax に書き込む」ことを意味します。確かに、これはあまり良いコードではありません。ほんの一例です。

スタックに 1 つの変数を割り当て、eax でそのアドレスを取得します。次に、別の var を割り当て、そのアドレスを edx に保存します。次に、両方のアドレスをプッシュしてから、fmt のオフセットをプッシュします。

最初にスペースを割り当てる必要があります。また、EBP 相対アドレスを使用してローカル変数をアドレス指定する場合を除き、フレームは必要ありません。ebp - 4 などをプッシュできます。コードをコンパイルして、任意のデバッガーでどのように動作するかを確認してください (コードをチェックするために ollydbg を使用しました)。最後に、C コンパイラに asm リストを生成するように依頼し、コンパイラがこれを行う方法を確認できます。

于 2012-11-30T18:59:56.637 に答える
0

これとともに:

pushl %esp #location of x
pushl $fmt
call scanf

EBP を上書きします。

まず、CPU はレジスタの値 (古い esp 値) を記憶し、次に 4 を引き、古い ESP 値を保存します。この場合、これは古い EBP です。最初に 4 を減算すると、スタックに変数が割り当てられます (PUSH EAX の方が良い - 短く、1 バイトのみ)。

2番目のケースと同様の問題:

addl $4,%esp #pop format    
pushl 8(%esp)
pushl 8(%esp)
pushl $fmt

ここで、最初のパラメーターは X ではなく、2 番目のパラメーターを指しています。2 番目は EBP を指します。最初にスタックに変数を割り当てる必要があります。

push ebp
mov ebp, esp
push eax
mov edx, esp
push eax
mov eax, esp
push eax
push edx
push offset fmt
call xyz

さらに、ローカル変数を使用していない場合は、ebp をプッシュする必要はなく、フレーム ポインターを作成する必要もありません。または、スタックに変数を割り当てた後、これを使用できます。

LEA eax, [EBP - 4]
LEA edx, [EBP - 8]
于 2012-11-30T14:37:24.213 に答える