簡體   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