2012-01-25 25 views
10

私はDumpBinを使用して(最も可能性が高いのVisual C++コンパイラを使用して生成された)オブジェクトファイルを逆アセンブルし、以下のコードを見て:EAXレジスタは、これらの4渡って保存および復元されている理由コンパイラがこのコードを生成するのはなぜですか?

...   ... 
mov   dword ptr [ebp-4],eax  // Why save EAX? 
push  dword ptr [ebp+14h] 
push  dword ptr [ebp+10h] 
push  dword ptr [ebp+0Ch] 
push  dword ptr [ebp+8] 
mov   eax,dword ptr [ebp-4]  // Why restore EAX? Did it change at all? 
call  <function> 
...   ... 

誰かが説明してもらえますpush命令?

+2

コンパイラがこれよりも愚かなことをしたのを見たことがあります... – Mysticial

+0

@Mysticial:Oh lol ...私はこれに気づいたのは初めてです。 :)よく知っています。 – Mehrdad

+7

おそらく、最初のプッシュへの分岐があります。 –

答えて

9

また、リリースモードでコンパイルされている可能性がありますが、その変数はvolatileとマークされています。これは、この変数がわからなくても変更される可能性があることをコンパイラに通知するためです。

+0

これはかなり可能です! +1ありがとう。 – Mehrdad

+0

私は、「揮発性」がここで違いを生むかどうかはわかりません。 「volatile」はメモリ位置に関係するが、EAXはレジスタである。レジスタを揮発性としてマークすることはできません。したがって、「揮発性」は、すべての操作の直前に* eaxにリロードされると説明しますが、eaxは保存されません。 – Crashworks

+0

特定の算術演算では、コンパイラは、揮発性とマークされたメモリ位置をレジスタにロードする必要があります。これは、リード・モディファイ・ライト命令として実行できないためです。このコードセグメントの後にストアが続く可能性があり、その前にロードが先行する可能性があるため、手元の情報からは絶対に可能です。しかし、うまくいけば、VCによる最適化が欠けているだけです。 – hirschhornsalz

6

これはデバッグモードでビルドされましたか?その場合、コンパイラはすべてのローカル変数をスタックに格納して、デバッガが一貫した方法でそれらを見つけることができるようにします。

このような不要なストアとリロードの除去は、「リリース」モードを構成する最適化の1つです。

+0

私はリリースモードのコードであると信じられています*(私がどこでも見ることのできるものの「デバッグ」バージョンはありませんが)わかりません...私はソースコードを持っていませんどちらか。しかし+1は合理的な推測です、ありがとうございます。 – Mehrdad

2

volatileかそのfunctionがWindows CS_SYSCALL呼び出し規約を使用して、すなわち、__syscall宣言されている場合、EAXWindows上での関数呼び出しを行う前に直接を初期化しなければならないであろう理由だけ技術理由でした。概念的には、これは%al%xmmレジスタで渡された浮動小数点型引数の数が含まれているUN * X x86_64の規則に少し似ています。

Windows上のsyscall呼び出し規約は、__cdeclと同じです。つまり、スタック上の関数argsは逆順になりますが、ALには引数の数が含まれています。これは、通常、最後の最後にあるカーネルコードが、ユーザースタックからカーネルスタックに読み込んでargsを取得するためのデータの量を知るように行われます。

EAXは、32ビットWindows上のすべての呼び出し規則のスクラッチレジスタであり、その値は関数呼び出しに対して保持されず、呼び出しを行う前に直接初期化することは冗長です。たとえ変数が保持されていても、単純な再読み込みはメモリバリアではなく、前のストアを「コミット」しないため、volatileです。さらに、場所[EBP - 4]スタックの範囲内にあるため、変数はローカルです(また、volatile修飾子はほとんど意味がありません)。それは逃した最適化ではない場合

は、それは

__syscall printf_syscall_conv(char *fmt, ...); 

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3) 
{ 
    if (*strchr('%', fmt) == '\0') // if no "%" in fmt, pass no args 
     printf_syscall_conv(fmt); 
    else 
     printf_syscall_conv(fmt, val1, val2, val3); 
} 

これはおそらく、あなたのようなアセンブリ出力を作成することができ、仮に引数などの異なる数、と__syscall function(...)の呼び出しである可能性があります。