Msvc 内联程序集内部函数调用

Msvc Inline assembly inner function calls

首先我想说我研究了这件事,但找不到任何相关的东西。

我正在 msvc 2013 上以 32 位发布模式编写一个 C++ 控制台程序。 我在其中一个文件中使用内联汇编,它工作得很好,除非我的内联汇编 function_1 调用我的 function_2,当发生这种情况时,会有指令被添加到 function_2,因此堆栈损坏,程序崩溃。 如果我停止使用调用,而只是 "lea ebx,[eip+5]",则 push ebx, jmp xxxxxx 可以正常工作。

所以在我的例子中,为了更具体一点,函数 2 是这样定义的:

    void test()
{

_asm{
f_01758630:  // ; <= Procedure Start

    PUSH EBP
            MOV EBP, ESP
            PUSH ESI
            PUSH EDI
            mov edi, [ebp + 0x0C]
            XOR ESI, ESI
            SHR EDI, 0x2
            TEST EDI, EDI
            JLE f_0175866B
            PUSH EBX
            mov ebx, [ebp + 0x08]

    f_01758645:

        MOV EDX, DWORD PTR DS : [EBX + ESI * 0x4]
            ROL EDX, 0x10
            MOV ECX, EDX
            MOV EAX, EDX
            SHR ECX, 0x8
            SHL EAX, 0x8
            XOR ECX, EAX
            SHL EDX, 0x8
            AND ECX, 0xFF00FF
            XOR ECX, EDX
            MOV DWORD PTR DS : [EBX + ESI * 0x4], ECX
            INC ESI
            CMP ESI, EDI
            JL TERA_01758645
            POP EBX

        f_0175866B :

        POP EDI
            POP ESI
            POP EBP
            RETN//; <= Procedure End
}
}

然而,当我调试 运行 程序时,我可以看到函数是这样实现的:

push ebx push esi push edi push ebp mov ebp,esp push esi push edi

即 msvc 实现了 3 次推送,这可能与函数内的 _asm{} 相关吗?我可以如何解决此问题?

第一个

push    ebp
mov     ebp, esp
push    ebx
push    esi
push    edi

是函数自动生成的序言。函数的最后是结语:

pop     ebx
pop     edi
pop     esi
pop     ebx
pop     ebp
ret

您的 _asm 块有自己的序言和结语,因此代码完成了两次。更糟糕的是,_asm 块中的 ret 获得了错误的 return 地址,程序将崩溃。您可以通过将函数声明为 naked:

来避免函数 prologue/epilogue
__declspec (naked) void test()
{
    _asm
    {
        own prolog
        ...
        own epilog
        ret
    }
}

这很危险,因为您可能会忘记保留要 return 不变的寄存器,"callee saved registers":EBX、EBP、EDI、ESI。在MSVC内联汇编中,很容易使用函数参数和局部变量,所以没有必要控制epilog和prolog。

看看这个例子(尽可能接近你的代码):

#include <stdio.h>

void function_2(unsigned* reg_ebx, unsigned reg_edi)
{
    _asm
    {
            mov edi, reg_edi      // take the second argument
            xor esi, esi
            shr edi, 2
            test edi, edi
            jle f_0175866b
            mov ebx, reg_ebx     // take the first argument

        f_01758645:

            mov edx, dword ptr ds : [ebx + esi * 0x4]
            rol edx, 16
            mov ecx, edx
            mov eax, edx
            shr ecx, 8
            shl eax, 8
            xor ecx, eax
            shl edx, 8
            and ecx, 0xff00ff
            xor ecx, edx
            mov dword ptr ds : [ebx + esi * 0x4], ecx
            inc esi
            cmp esi, edi
            jl f_01758645

        f_0175866b :
    }
}

void function_1 ()
{
    unsigned arr[8] = {1000,2000,3000,4000,5000,6000,7000,8000};
    int i;
    for (i=0; i < sizeof(arr)/sizeof(arr[0]); ++i) printf ("%08X ",arr[i]); puts ("");

    _asm
    {
        push LENGTH arr
        lea eax, arr
        push eax
        call function_2
    }

    for (i=0; i < sizeof(arr)/sizeof(arr[0]); ++i) printf ("%08X ",arr[i]); puts ("");
}

int main ( void )
{
    function_1();
    return 0;
}