0

課題として、アセンブリ コードを読み、その高度な C プログラムを作成する必要があります。この場合の構造はswitchステートメントであるため、それぞれのケースについて、アセンブリ コードは C コードのケースに変換されます。以下はその一例にすぎません。これを解釈するのを手伝っていただければ、残りのケースをよりよく理解できるはずです.

p1 is in %ebp+8
p2 is in %ebp+12
action is in %ebp+16
result is in %edx

...
.L13:
    movl 8(%ebp), %eax    # get p1
    movl (%eax), %edx     # result = *p1?
    movl 12(%ebp), %ecx   # get p2
    movl (%ecx), %eax     # p1 = *p2
    movl 8(%ebp), %ecx    # p2 = p1
    movl %eax, (%ecx)     # *p1 = *p2?
    jmp .L19              #jump to default
...
.L19
    movl %edx, %eax       # set return value

もちろん、コメントは私がそれを理解するために追加したものですが、それが私をより混乱させることを除いて. これは交換という意味ですか?おそらくそうではありません。フォーマットは異なります。2 行目と 6 行目では実際に何が起こっているのでしょうか。%edx が戻り値である場合、なぜこんなに早く一度だけ変更されるのですか? このコードを解釈するためのガイドラインを添えて回答してください。

4

1 に答える 1

4

上記のスニペットは、(IMO が壊れている) AT&T 構文の x86_32 アセンブリです。

AT&T 構文では、すべてのオペランドにサイズ サフィックスが付加されます。

movl32 ビットオペランドを意味します。(長い場合は l)
movwは 16 ビット オペランドを意味します (ワードは w)
movbは 8 ビット オペランドを意味します (バイトは b)

オペランドの順序が逆になっているため、宛先が右側にあり、ソースが左側にあります。
これは、他のほぼすべてのプログラミング言語とは対照的です。

%レジスタ名には、変数名と区別するために接頭辞 a が付きます。レジスタが括弧で囲まれている場合は()、レジスタ自体の値ではなく、レジスタが指すメモリ アドレスが使用されることを意味します。
EBP はスタックフレームへのポインターとして使用されるため、これは理にかなっています。
スタックフレームは、関数内のパラメーターとローカル変数にアクセスするために使用されます。

書く代わりに: mov eax, dword ptr [ebp+8] (Intel 構文)
AT&T 構文では次のようにリストされます: movl 8(%ebp), %eax (gas 構文)

つまり、(ebp + 8) が指すメモリアドレスの内容を eax に入れます。

翻訳は次のとおりです。

.L13:   <<-- label used as a jump target. 
    movl 8(%ebp), %eax    <<--  p1, stored at ebp+8 goes into EAX
    movl (%eax), %edx     <<-- p1 is a pointer, EDX = p1->next
    movl 12(%ebp), %ecx   <<-- p2, stored at ebp+12 goes in ECX
    movl (%ecx), %eax     <<-- p2 is (again) a pointer, EAX = p2->next
    movl 8(%ebp), %ecx    <<-- ECX = p1
    movl %eax, (%ecx)     <<-- p2->next = p1->next 
    jmp .L19              <<-- jump to exit 
...
.L19
    movl %edx, %eax       <<-- EAX is always the return value
                          <<-- return p1->data.

x86 の多くの呼び出し規則のすべてで、関数の戻り値は EAX レジスタに入れられます。(または INT64 の場合は EAX:EDX)

散文: p1 と p2 はデータへのポインターであり、このデータではポインターへのポインターです。
このコードは、連結リストを操作しているように見えます。
p2->nextに設定されていp1->nextます。
それ以外は、スニペットは不完全に見えます。これは、最初にあったものはすべて処理されp2->nextていないため、表示されていないコードがさらにある可能性があります。

紛らわしい AT&T 構文を除けば、これは非常に単純なコードです。

C では、コードは次のようになります。

(void *)p2->next = (void *)p1->next;

コードは非常に非効率的であり、まともなコンパイラ (または人間) がこのコードを生成しないことに注意してください。

次の等価物は、より理にかなっています。

mov eax,[ebp+8]
mov ecx,[ebp+12]
mov eax,[eax]
mov [ecx],eax
jmp done

AT&T と Intel の構文の違いについて詳しくは、http ://www.ibm.com/developerworks/linux/library/l-gas-nasm/index.html を参照してください。

于 2013-10-13T17:12:05.630 に答える