私はいくつかのアセンブリを理解しようとしています。
アセンブリは次のとおりです。testl
行に興味があります。
000319df 8b4508 movl 0x08(%ebp), %eax
000319e2 8b4004 movl 0x04(%eax), %eax
000319e5 85c0 testl %eax, %eax
000319e7 7407 je 0x000319f0
%eax
と%eax
の間のtestl
のポイントを理解しようとしていますか?このコードが重要ではないことの詳細は、自分自身でテストを理解しようとしているだけだと思います-値は常に真実ではないでしょうか?
eax
が0以上、または以下かどうかをテストします。この場合、eax
が0の場合にジャンプします。
test
の意味は、引数をANDで結合し、ゼロの結果をチェックすることです。したがって、このコードはEAXがゼロかどうかをテストします。 je
はゼロの場合ジャンプします。
ところで、これはcmp eax, 0
よりも小さな命令を生成します。これがコンパイラーが通常この方法で行う理由です。
テスト命令は、オペランド間で論理AND演算を実行しますが、結果をレジスタに書き戻しません。フラグのみが更新されます。
テストeaxの例では、eaxは、eaxがゼロの場合はゼロフラグを設定し、最上位ビットが設定されている場合は符号フラグを設定し、その他のフラグも設定します。
ゼロフラグが設定されている場合、Jump if Equal(je)命令はジャンプします。
コードを次のような読みやすいコードに変換できます。
cmp eax, 0
je somewhere
これには同じ機能がありますが、コードバイトがさらに数バイト必要です。これが、コンパイラが比較の代わりにテストを発行した理由です。
test
は and
と似ていますが、FLAGSのみを書き込み、両方の入力を変更しないままにします。 2つのdifferent入力では、一部のビットがすべてゼロかどうか、または少なくとも1つのビットが設定されているかどうかをテストするのに役立ちます。 (たとえば、test al, 3
_は、EAXが4の倍数である(したがって、下位2ビットの両方がゼロになっている)場合にZFを設定します。
_test eax,eax
_ は、 _cmp eax, 0
_ とまったく同じ方法ですべてのフラグを設定します:
a = a&a = a-0
_)。廃止されたAF(ASCII/BCD命令で使用される補助キャリーフラグ)を除きます。 TESTは未定義 のままにしますが、 CMPは「結果に応じて」 に設定します。ゼロを減算しても、4番目から5番目のビットの桁上げは発生しないため、CMPは常にAFをクリアする必要があります。
TESTは小さく(即時ではありません)、時には高速です(CMPよりも多くの場合、より多くのCPUでマクロヒューズを比較分岐uopにできます)。 これは、test
を、レジスタをゼロと比較するための好ましいイディオムにする 。これは、セマンティックな意味に関係なく使用できる_cmp reg,0
_のピープホール最適化です。
即時0でCMPを使用する唯一の一般的な理由は、メモリオペランドと比較する場合です。たとえば、cmpb $0, (%esi)
は、暗黙的な長さのCスタイル文字列の末尾にあるゼロの終端をチェックします。
AVX512Fは _kortestw k1, k2
_を追加し、AVX512DQ/BW(Skylake-XがKNLではない)を追加 _ktestb/w/d/q k1, k2
_ 、AVX512マスクレジスタ(k0..k7)で動作しますが、test
のように通常のフラグを設定します。整数OR
またはAND
指示があります。 (SSE4のようなptest
またはSSE ucomiss
:SIMDドメインの入力で、整数FLAGSになります。)
_kortestw k1,k1
_は、AVX512比較結果に基づいて、SSE/AVX2の_(v)pmovmskb/ps/pd
_ + test
またはcmp
を置換する慣用的な方法です。
jz
とje
の使用は混乱を招く可能性があります。
jz
とje
は、文字通り同じ命令 、つまりマシンコードの同じオペコードです。これらは同じことをしますが、人間に対して異なる意味を持ちます。逆アセンブラー(および通常、コンパイラーからのasm出力)は1つだけを使用するため、セマンティックの区別は失われます。
cmp
とsub
は、2つの入力が等しい(つまり、減算結果が0)場合にZFを設定します。 je
(等しい場合はジャンプ)は、意味的に関連する同義語です。
_test %eax,%eax
_/_and %eax,%eax
_は、結果がゼロのときに再びZFを設定しますが、「等価」テストはありません。テスト後のZFでは、2つのオペランドが等しいかどうかはわかりません。したがって、jz
(ゼロの場合はジャンプ)は意味的に関連する同義語です。
このコードスニペットは、何か、おそらくいくつかの構造体またはオブジェクトへのポインターが与えられたサブルーチンからのものです。 2行目はそのポインターを逆参照し、そのことから値を取得します-おそらくそれ自体がポインターであるか、2番目のメンバー(オフセット+4)として格納されているintだけです。 3行目と4行目では、この値をゼロ(ポインターの場合はNULL)でテストし、ゼロの場合は次のいくつかの操作(図示せず)をスキップします。
ゼロのテストは、即時リテラルのゼロ値との比較としてコーディングされる場合がありますが、これを書いたコンパイラー(または人間?)は、パイプラインやレジスターなどの最新のCPUをすべて考慮して、testl opの実行が高速になると考えたかもしれません名前を変更します。明らかですが、より遅いMOV EAXではなく、XOR EAX、EAX(コロラドの誰かのナンバープレートで見た!)でレジスタをクリアするという考えを保持しているのと同じトリックの袋からです。 、#0(古い表記を使用)。
Asmでは、Perlと同様に、TMTOWTDI。
Eaxがゼロの場合は条件付きジャンプを実行し、そうでない場合は319e9で実行を継続します
一部のプログラムでは、バッファオーバーフローをチェックするために使用できます。割り当てられたスペースの最上部に0が配置されます。スタックにデータを入力した後、割り当てられたスペースの先頭で0を探し、割り当てられたスペースがオーバーフローしないようにします。
これは、エクスプロイトエクササイズのstack0エクササイズで使用され、オーバーフローしたかどうかをチェックし、ゼロがなかった場合は「Try again」と表示されます
0x080483f4 <main+0>: Push ebp
0x080483f5 <main+1>: mov ebp,esp
0x080483f7 <main+3>: and esp,0xfffffff0
0x080483fa <main+6>: sub esp,0x60
0x080483fd <main+9>: mov DWORD PTR [esp+0x5c],0x0 ;puts a zero on stack
0x08048405 <main+17>: lea eax,[esp+0x1c]
0x08048409 <main+21>: mov DWORD PTR [esp],eax
0x0804840c <main+24>: call 0x804830c <gets@plt>
0x08048411 <main+29>: mov eax,DWORD PTR [esp+0x5c]
0x08048415 <main+33>: test eax,eax ; checks if its zero
0x08048417 <main+35>: je 0x8048427 <main+51>
0x08048419 <main+37>: mov DWORD PTR [esp],0x8048500
0x08048420 <main+44>: call 0x804832c <puts@plt>
0x08048425 <main+49>: jmp 0x8048433 <main+63>
0x08048427 <main+51>: mov DWORD PTR [esp],0x8048529
0x0804842e <main+58>: call 0x804832c <puts@plt>
0x08048433 <main+63>: leave
0x08048434 <main+64>: ret