Msvc内联汇编内部函数调用

时间:2015-09-30 15:53:28

标签: assembly call inline-assembly

首先,我想说我研究了这个问题但是找不到与之相关的任何相关内容。

我正在msvc 2013上以32位发布模式编写c ++控制台程序。 我在其中一个文件中使用内联,它很有效,除非我的内联汇编function_1调用我的function_2,当发生这种情况时,会有一些内容被添加到function_2,因此堆栈会被损坏并且程序崩溃。 如果我停止使用电话但仅仅是#e; lea ebx,[eip + 5]",请推送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 {}相关,这是在函数内部如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

第一个

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

是自动生成的函数序言。功能结束时是结语:

pop     ebx
pop     edi
pop     esi
pop     ebx
pop     ebp
ret

你的_asm块有自己的序言和尾声,所以代码完成了两次。更糟糕的是,ret块中的_asm获取了错误的返回地址,程序将崩溃。您可以通过将函数声明为naked来避免函数序言/结尾:

__declspec (naked) void test()
{
    _asm
    {
        own prolog
        ...
        own epilog
        ret
    }
}

这很危险,因为您可以忘记保留要更改的寄存器,“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;
}