2017-11-13 54 views
1
int x = 5; 

void foo() 
{ 
    long unsigned r[8]; 
    memset(&r, 0, sizeof(long unsigned) * 8); 

    __asm { 

    pushad; 

    pop r[7]; 
    pop r[6]; 
    pop r[5]; 
    pop r[4]; 
    pop r[3]; 
    pop r[2]; 
    pop r[1]; 
    pop r[0]; 
    } 

    printf("Register values: \n"); 

    printf("eax: %lu\n", r[0]); 
    printf("ecx: %lu\n", r[1]); 
    printf("edx: %lu\n", r[2]); 
    printf("ebx: %lu\n", r[3]); 
    printf("esp: %lu\n", r[4]); 
    printf("ebp: %lu\n", r[5]); 
    printf("esi: %lu\n", r[6]); 
    printf("edi: %lu\n", r[7]); 

    if (--x) { foo(); } 
} 

int main() 
{ 
    foo(); 
    return 0; 
} 

再帰関数でpushadを使用して、どのようにのGPRS変更を確認しようとしていたときに増加していないようです。しかし、私はr(0)を除いて、これらのすべてのために0のプリントアウトを得ているようですが、foo()コールごとに44ずつ降順に数字が出力されます。あれは正しいですか?スタックポインタは、私はちょうどpushad/PUSHA操作について学んだ

新しい機能を呼び出すたびにespレジスターが動くべきではないと思いますが、少しわかりますか?

+1

通常、 'pushad' /' popad'は使用しないでください。遅いですし、すべてのレジスタを保存/復元する必要はほとんどありません。 (通常、関数はeax/ecx/edxを壊す可能性があります)。また、MSVCスタイルのインラインasmでは、レジスタを自分で保存/復元する必要はありません。コンパイラは保存する必要がある場合に使用するレジスタを保存します。 (あなたのコードがすでに 'ebx'、' esi'などを保存しているもっと大きな関数にインライン展開されているとします) –

+0

@PeterCordesああ、私は注意します!私たちが与えたユーザレベルのスレッド割り当ては、pushadとpopad(esp、ebp、eip以外のレジスタはほとんどわかりません)だけをカバーしていました。 – ozma

答えて

2

これは非常に何をしたいあなたを行いません。

pop r[7]; 
pop r[6]; 
pop r[5]; 
pop r[4]; 
pop r[3]; 
pop r[2]; 
pop r[1]; 
pop r[0]; 

は、これは実際には配列rに各LONGのアドレスを計算し、その場所に値をポップしません。それは書き込みのようにされているでしょう:

pop [r+7]; 
pop [r+6]; 
pop [r+5]; 
pop [r+4]; 
pop [r+3]; 
pop [r+2]; 
pop [r+1]; 
pop [r+0]; 

インラインアセンブリは、アカウントに配列データ要素の大きさを取ることはありません。配列の基数に値を追加すると、インデックスがバイトであるかのように増分されます。

何が起こったのかがはっきりしているかもしれません。 rが配列のベースアドレスになります(スタック上にあるため、ebpに相対します)。 r+0は、最初のバイトの位置です。r+1は、2番目のバイトなどの位置です。計算を行い、各ロングのインデックスを計算する必要があります。だから、次のように見えるはずです:

pop r[28]; 
pop r[24]; 
pop r[20]; 
pop r[16]; 
pop r[12]; 
pop r[8]; 
pop r[4]; 
pop r[0]; 
+0

素晴らしいです、ありがとう!それがまさに問題でした。 – ozma

関連する問題