1

私はこの関数を持っています。これは主にインライン asm で構成されています。

long *toarrayl(int members, ...){
    __asm{
        push esp

        mov eax, members
        imul eax, 4
        push eax
        call malloc
        mov edx, eax
        mov edi, eax

        xor ecx, ecx
        xor esi, esi
loopx:
        cmp ecx, members
        je done
        mov esi, 4

        imul esi, ecx
        add esi, ebp
        mov eax, [esi+0xC]
        mov [edi], eax
        inc ecx
        add edi, 4
        jmp loopx
done:
        mov eax, edx
        pop esp
        ret
    }
}

実行すると、リターン命令でアクセス違反が発生します。

私は VC++ 6 を使用していますが、上記の行を指すことを意味する場合があるため、'pop esp' で可能です。あなたが私を助けることができれば、それは素晴らしいことです. ありがとう、アイドモ。

4

3 に答える 3

8

スタック ポインタを正しく管理できていません。特に、 への呼び出しmallocはスタックのバランスを崩しpop esp、間違った値を にポップしてしまいますesp。したがってret、無効なスタックからアクセスしようとすると、アクセス違反が発生します (CPU が戻りアドレスを読み取ることができません)。なぜプッシュしてポップしているのかは不明ですesp。それは何も達成しません。

于 2012-12-31T22:12:46.600 に答える
0

お気づきのように、POP ESP命令は絶対に使用しないでください。コードでそれを見ると、何か非常に悪いことが起こっていることがわかります。もちろん、アセンブラコード内でmallocを呼び出すことも、かなり悪いことです。たとえば、NULLが返されたかどうかを確認するのを忘れているため、クラッシュする可能性があります。それをアセンブラの外に貼り付けて、NULLをチェックすると、「アセンブラのどこかで、ファイルmycode.cの54行目にメモリを割り当てることができませんでした」をデバッグする方がはるかに簡単です。

ループを少しスピードアップする改善のためのいくつかの提案があります:

long *toarrayl(int members, ...){
    __asm{
        mov eax, members
        imul eax, 4
        push eax
        call malloc
        add esp, 4
        mov edx, eax
        mov edi, eax

        mov ecx, members
        lea esi, [ebp+0xc]
loopx:
        mov eax, [esi]
        mov [edi], eax
        add edi, 4
        add esi, 4
        dec ecx
        jnz loopx
        mov lret, eax
        ret
    }
}

改善点:すべてのループで乗算を4で削除します。esi代わりにインクリメントしてください。increamentの代わりにecxでデクリメントを使用し、ループの前にメンバーをロードします。これにより、ループ内で2つではなく1つのジャンプを使用できます。edxからeaxへの冗長な移動を削除します。eaxを直接使用してください。

于 2012-12-31T22:28:52.027 に答える
0

私は自分で答えを見つけました。

これと同じ、または同様の問題を抱えている人のために:

実際の例外は、関数が呼び出される前の状態に vc++ が自動的にレジスタをポップ/復元するときに、ユーザー コードの後に​​発生していました。mallocの呼び出し時にスタックポインタをミスアラインしたため、スタックからのポップ時にアクセス違反が発生しました。これは私のコードではないため、エディターで見ることができませんでした。そのため、関数内の最後のコードとして表示されました。

これを修正するには、呼び出しの後に add esp (前の呼び出しのパラメーターのサイズ) を追加するだけです。

固定コード:

long *toarrayl(int members, ...){
    __asm{
        mov eax, members
        imul eax, 4
        push eax
        call malloc
        add esp, 4
        mov edx, eax
        mov edi, eax

        xor ecx, ecx
        xor esi, esi
loopx:
        cmp ecx, members
        je done
        mov esi, 4

        imul esi, ecx
        add esi, ebp
        mov eax, [esi+0xC]
        mov [edi], eax
        inc ecx
        add edi, 4
        jmp loopx
done:
        mov eax, edx
        ret
    }
    //return (long*)0;
}

最適化されたコード:

long *toarrayl(int members, ...){
    __asm{
        mov eax, members
        shl eax, 2
        push eax
        call malloc
        add esp, 4
        ;cmp eax, 0
        ;je _error
        mov edi, eax
        mov ecx, members
        lea esi, [ebp+0xC]
loopx:
        mov edx, [esi]
        mov [edi], edx
        add edi, 4
        add esi, 4
        dec ecx
        jnz loopx
    }
}
于 2012-12-31T22:12:54.523 に答える