C++ 调用C++;使用参数和返回值来自程序集的方法

C++ 调用C++;使用参数和返回值来自程序集的方法,c++,assembly,parameters,x86,return-value,C++,Assembly,Parameters,X86,Return Value,所以我以前问过这个问题,但细节要少得多。问题标题准确地描述了问题:我有一个C++方法,我试图从汇编(x86)调用,它既有参数又有返回值。我对汇编和对C++的理解有一个粗略的理解(否则我就不会承担这个问题)。以下是我所掌握的代码: // methodAddr is a pointer to the method address void* methodAddr = method->Address; // buffer is an int array of parameter values.

所以我以前问过这个问题,但细节要少得多。问题标题准确地描述了问题:我有一个C++方法,我试图从汇编(x86)调用,它既有参数又有返回值。我对汇编和对C++的理解有一个粗略的理解(否则我就不会承担这个问题)。以下是我所掌握的代码:

// methodAddr is a pointer to the method address
void* methodAddr = method->Address;

// buffer is an int array of parameter values. The parameters can be anything (of any type)
// but are copied into an int array so they can be pushed onto the stack in reverse order
// 4 bytes at a time (as in push (int)). I know there's an issue here that is irrelevent to my baseline testing, in that if any parameter is over 4 bytes it will be broken and
// reversed (which is not good) but for basic testing this isn't an issue, so overlook this.
for (int index = bufferElementCount - 1; index >= 0; index--)
{
    int val = buffer[index];

    __asm
    {
        push val
    }
}

int returnValueCount = 0;

// if there is a return value, allocate some space for it and push that onto the stack after
// the parameters have been pushed on
if (method->HasReturnValue)
{
    *returnSize = method->ReturnValueSize;
    outVal = new char[*returnSize];
    returnValueCount = (*returnSize / 4) + (*returnSize % 4 != 0 ? 1 : 0);
    memset(outVal, 0, *returnSize);

    for (int index = returnValueCount - 1; index >= 0; index--)
    {
        char* addr = ((char*)outVal) + (index * 4);

        __asm
        {
            push addr
        }
    }
}

// calculate the stack pointer offset so after the call we can pop the parameters and return value
int espOffset = (bufferElementCount + returnValueCount) * 4;

// call the method
__asm
{
    call methodAddr;
    add esp, espOffset
};
对于我的基本测试,我使用具有以下特征的方法:

Person MyMethod3( int, char, int );
问题是:当从方法签名中忽略返回值时,所有参数值都被正确传递。但当我保持方法不变时,传递的参数数据不正确,但返回的值是正确的。很明显,我的问题是,到底出了什么问题?我尝试在参数之前将返回值空间推到堆栈上。人员结构如下:

class Person
{
public:
    Text Name;
    int Age;
    float Cash;
    ICollection<Person*>* Friends;
};
下面是对其的分解:

00CB0A20  mov         dword ptr [one],36Ch  
    char two = 'X';
00CB0A27  mov         byte ptr [two],58h  
    int three = 9738;
00CB0A2B  mov         dword ptr [three],260Ah  
    Person p = MyMethod3(one, two, three);
00CB0A32  push        10h  
00CB0A34  lea         ecx,[p]  
00CB0A37  call        Person::__autoclassinit2 (0C6AA2Ch)  
00CB0A3C  mov         eax,dword ptr [three]  
00CB0A3F  push        eax  
00CB0A40  movzx       ecx,byte ptr [two]  
00CB0A44  push        ecx  
00CB0A45  mov         edx,dword ptr [one]  
00CB0A48  push        edx  
00CB0A49  lea         eax,[p]  
00CB0A4C  push        eax  
00CB0A4D  call        MyMethod3 (0C6B783h)  
00CB0A52  add         esp,10h  
00CB0A55  mov         dword ptr [ebp-4],0
我对此的解释如下: 执行对局部变量的赋值。然后创建输出寄存器。然后将参数放入一个特定的寄存器(这里的顺序恰好是
eax
ecx
edx
,这是有意义的(
eax
ebx
是一个寄存器,
ecx
是两个寄存器,
edx
和其他一些寄存器是最后一个参数?)。然后调用
LEA
(加载有效地址),我不理解,但已理解为
MOV
。然后它调用以地址作为参数的方法?然后移动堆栈指针以弹出参数和返回值


任何进一步的解释都是值得赞赏的,因为我确信我在这里的理解有点缺陷。

出于某种原因,调用约定似乎在方法的两个版本之间发生了变化。你试过强迫它使用一个特定的平台吗?哪个平台?哪种ABI?哪个电话会议?您是否记得传递
this
指针?指定方法的调用约定很可能是
stdcall
thiscall
。我有办法确定两者之间的差异,但我只是测试传统的
stdcall
。问题是被调用的方法可以是任何类型:它可以有任意数量的任何类型的参数,并且可能有返回值,也可能没有返回值。您已经有了一个引用实现:编译器。在C++中编写调用,编译它,然后查看编译器做了什么。你会发现它比你拥有的更复杂。事实上,您想要的是不可能的,因为编译器可以使用类似于自定义调用约定的优化。我尝试过查看反汇编,但它做了一些非常复杂的事情,涉及LEA和其他我不确定的术语。我会把DA寄出去。
00CB0A20  mov         dword ptr [one],36Ch  
    char two = 'X';
00CB0A27  mov         byte ptr [two],58h  
    int three = 9738;
00CB0A2B  mov         dword ptr [three],260Ah  
    Person p = MyMethod3(one, two, three);
00CB0A32  push        10h  
00CB0A34  lea         ecx,[p]  
00CB0A37  call        Person::__autoclassinit2 (0C6AA2Ch)  
00CB0A3C  mov         eax,dword ptr [three]  
00CB0A3F  push        eax  
00CB0A40  movzx       ecx,byte ptr [two]  
00CB0A44  push        ecx  
00CB0A45  mov         edx,dword ptr [one]  
00CB0A48  push        edx  
00CB0A49  lea         eax,[p]  
00CB0A4C  push        eax  
00CB0A4D  call        MyMethod3 (0C6B783h)  
00CB0A52  add         esp,10h  
00CB0A55  mov         dword ptr [ebp-4],0