2

IA-32 アセンブリ プログラミングを学習しています。そこで、アセンブリで関数を書き、C++ から呼び出したいと思います。

私がフォローしているチュートリアルは、実際には x64 アセンブリ用です。しかし、私はIA-32に取り組んでいます。x64 では、関数の引数は RCX、RDX、R8、R9 などのレジスタに格納されます。

しかし、少し調べてみると、IA-32 では引数がレジスタではなくスタックに格納されていることがわかりました。

以下は私のC++コードです:

#include <iostream>
#include <conio.h>

using namespace std;
extern "C" int PassParam(int a,int b);

int main()
{
    cout << "z is " << PassParam(15,13) << endl;
    _getch();
    return 0;
}

以下は PassParam() 関数のアセンブリ コードです (2 つの引数を追加するだけです。これは学習目的のみです)。

アセンブリの PassParam() :

.model C,flat
.code
PassParam proc
    mov eax,[ebp-212]
    add eax,[ebp-216]
    ret
PassParam endp
end

私のアセンブリ コードでは、最初の引数を [ebp-212] から eax に移動したことがわかります。その値は次のように取得されます。

PassParam() 関数を C++ 自体で記述し、逆アセンブルしました。次に、ebp がどこにあり、2 番目の引数がどこに格納されているかを確認します (引数は右から左に格納されます)。212 の差があることがわかったので、その値を取得しました。その後、通常どおり、最初の引数は 4 バイト後に格納されます。そして、それはうまくいきます。

質問 :

これは、アセンブリから引数にアクセスする正しい方法ですか? つまり、引数が保存されている場所は常に [ebp-212] ですか?

そうでない場合、C++ から assembly に引数を渡す正しい方法を誰かが説明できますか?

ノート :

Windows 7 マシンで Visual C++ 2010 を使用しています。

4

1 に答える 1

8

32 ビット アーキテクチャでは、呼び出し規約に依存します。たとえば、Windows には、レジスタとスタック引数を使用する__fastcallとの両方があり、スタック引数を使用しますが、クリーンアップを行う人が異なります。MSDN には、こちら(または、よりアセンブリ指向のバージョン) の優れたリストがあります。FPU/SSE 操作にも独自の規則があることに注意してください。__thiscall__cdecl__stdcall

簡単かつシンプルにする__stdcallために、すべてに を使用してみてください。これにより、スタック フレームを使用して 経由で引数にアクセスできます。MOV r32,[EBP+4+(arg_index * 4)]スタック フレームを使用していない場合は、 を使用できますMOV r32,[ESP+local_stack_offset+(arg_index * 4)]ここにある注釈付きの C++ -> x86 アセンブリの例が役に立ちます。


簡単な例として、MulAddC++ のプロトタイプを使用してアセンブリに関数がint __stdcall MulAdd(int base, int mul, int add)あるとします。次のようになります。

MOV EAX,[ESP+4] //get the first arg('base') off the stack
MOV ECX,[ESP+8] //get the second arg('mul') off the stack
IMUL EAX,ECX //base * mul
MOV ECX,[ESP+12] //get arg 3 off the stack
ADD EAX,ECX
RETN 12 //cleanup the 3 args and return

または、スタック フレームを使用する場合:

PUSH EBP
MOV EBP,ESP //save the stack
MOV EAX,[EBP+8] //get the first arg('base') off the stack
MOV ECX,[EBP+12] //get the second arg('mul') off the stack
IMUL EAX,ECX //base * mul
MOV ECX,[EBP+16] //get arg 3 off the stack
ADD EAX,ECX
MOV ESP,EBP //restore the stack
POP EBP
RETN //return to caller

スタック フレームを使用PUSHすると、引数の 'ing、スピル、レジスタ、またはローカル変数に対して行われたスタック割り当てによってスタックに加えられた変更を調整する必要がなくなります。その欠点は、操作する必要があるレジスタの数が減ることです。

于 2013-01-07T12:13:54.273 に答える