繁体   English   中英

x64 asm如何设置指向_cdecl C函数的函数指针并调用它?

[英]x64 asm how to set a function pointer to a _cdecl C function and call it?

我正在尝试在x64 asm中做一些非常基本的事情:

  1. 有一个使用函数指针并将其设置在变量中的asm函数。 此函数从C代码中调用。

  2. 还有另一个asm函数,如果不为null,它将调用函数指针,该函数指针也是C函数(由1中的函数设置)。

到目前为止,这是我对C方面的了解:

extern "C" void _asm_set_func_ptr(void* ptr);

void _cdecl c_call_back()
{

}

void init()
{
    _asm_set_func_ptr(c_call_back);
}

和asm方面:

.DATA

g_pFuncPtr QWORD 0

.CODE             ;Indicates the start of a code segment.

_asm_set_func_ptr PROC fPtr:QWORD
    mov     [rsp+qword ptr 8], rcx
    mov     rax, [rsp+qword ptr 8]
    mov     g_pFuncPtr, rax
    ret
_asm_set_func_ptr ENDP 

_asm_func PROC

push RBX
push RBP
push RDI
push RSI
push RSP
push R12
push R13
push R14
push R15

CMP g_pFuncPtr, 0
JE SkipCall
    MOV RAX, [ g_pFuncPtr ];
    CALL RAX;
SkipCall:

pop RBX
pop RBP
pop RDI
pop RSI
pop RSP
pop R12
pop R13
pop R14
pop R15
ret

_asm_func ENDP 

但是似乎我在调用_asm_set_func_ptr()之后损坏了堆栈,也不确定在_asm_func中如何调用g_pFuncPtr是否正确? 我的代码有什么问题? 我正在使用VS2013 MASM64构建它。

首先,通常需要按相反的顺序弹出寄存器,即:
push RBXpush RBP ... push R15 > pop R15 ... pop RSIpop RBXret 这肯定会破坏_asm_func的调用者。


接下来,您应该查看Windows x64调用约定 ,进行正确的函数调用需要做些什么。 正确满足所有需求是非常重要的,否则在某些其他代码中事情可能会中断甚至到很晚,这并不是调试的最大事情。

例如,您不需要保存所有寄存器。 如果回调函数销毁了它们,它将保存并恢复它们本身。 因此,在那里没有必要进行推送和弹出操作,无论如何, RAX都可以无效,也无需传递任何参数。

但是请注意这一部分:

在Microsoft x64调用约定中,调用者有责任在调用函数之前(无论使用的实际参数数如何)在堆栈上分配32个字节的“影子空间”,并在调用后弹出堆栈。

因此,您应该在代码之前执行SUB ESP, 32 ,然后在RET之前执行ADD ESP, 32

还要求“ 堆栈对齐16个字节 ”,但是您当前不需要解决这个问题,因为“ 8个返回地址+ 32个字节的影子空间+ 8个下一个返回地址”是按16个对齐的字节。

此外,Windows x64 ABI对异常处理和正确展开也有严格的要求。 正如Raymond在评论中指出的那样,由于您的函数不是叶子函数(调用其他函数),因此您需要提供适当的序言和结尾-参见此处


不需要在_asm_set_func_ptr的开头临时保存RCX

否则,我在那里看不到任何问题。


最后是分号; 汇编文件中的行尾不需要。

在检查g_pFuncPtr之前,您要推入很多寄存器,但是,如果未设置它们,则不会将它们弹出堆栈。 如果您将某些东西推入堆栈,然后不拨打电话也不将其弹出,则您的堆栈将很快填满。

您必须以与推送相反的顺序弹出寄存器,否则您将取回错误的寄存器。

最后,除非您与它们有任何关系,否则不要浪费时间和CPU周期来推动它们:

    CMP g_pFuncPtr, 0
    JE SkipCall

    PUSH RBX
    PUSH RBP
    PUSH RDI
    PUSH RSI
    PUSH RSP
    PUSH R12
    PUSH R13
    PUSH R14
    PUSH R15
    MOV RAX, [ g_pFuncPtr ];
    CALL RAX;
    POP R15
    POP R14
    POP R13
    POP R12
    POP RSP
    POP RSI
    POP RDI
    POP RBP
    POP RBX
SkipCall:
    ret

...而且请-请...请仔细阅读如何设置堆栈框架以及如何在调用中管理堆栈框架。 C调用和ASM调用对堆栈帧的处理彼此非常不同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM