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;
}
首先我想说我研究了这件事,但找不到任何相关的东西。
我正在 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
:
__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;
}