使用参数和返回值从Assembly调用C ++方法

时间:2013-12-20 14:08:53

标签: 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. 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;
};

非常感谢任何帮助。谢谢!

我正在使用Visual Studio 2013和2013年11月的C ++ CTP编译器,目标是x86。

因为它与反汇编有关,这是直接的方法调用:

int one = 876;
char two = 'X';
int three = 9738;

Person p = MyMethod3(one, two, three);

以下是反汇编:

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

我对此的解释如下: 执行局部变量的赋值。然后创建输出寄存器。然后将参数放在特定的寄存器中(此处的顺序恰好是eaxecxedx,这是有意义的(eaxebx是例如,ecx用于两个,edx和其他一些寄存器用于最后一个参数?))。然后调用LEA(加载有效地址),我不明白但已理解为MOV。然后它以地址作为参数调用方法?然后移动堆栈指针以弹出参数并返回值。

任何进一步的解释都值得赞赏,因为我确信我的理解存在一些缺陷。

0 个答案:

没有答案