如何通过作为参数传递的函数指针调用函数?

hel*_*ide 4 assembly function-pointers x86-64 masm

如何在汇编中调用f1函数的第三个参数中的传递函数(*f2)?宣言看起来像这样:

extern float f1(int v1, float v2, float (*f2)(int v3, float v4));
Run Code Online (Sandbox Code Playgroud)

我想将v1传递给v3,v2传递给v4,调用函数f2,并返回值

f1:
    push rbp           
    mov rbp, rsp

    mov rdx, rdi ; v1 to v3
    mov xmm1, xmm0 ; v2 to v4
    call ??? 
    mov xmm0, xmm1

    mov rsp, rbp       
    pop rbp    
ret
Run Code Online (Sandbox Code Playgroud)

我该用什么来代替问号?

Cod*_*ray 5

没有"Abi64"这样的东西.由于您标记了MASM问题,我们可以猜测您正在使用Windows平台,显然"64"意味着这是64位代码,因此这极大地缩小了可能性.但是,Windows上仍有两种常见的64位代码调用约定.其中一个是__vectorcall,另一个是Microsoft x64调用约定(最初发明的那个使所有其他调用约定过时但是......没有).

由于Microsoft x64调用约定是最常见的,并且在这种特殊情况下,使用__vectorcall不会改变任何东西,我将假设它是您正在使用的那个.然后所需的代码变得非常微不足道.您需要做的就是跳转f1f2,因为堆栈将设置相同.f1前两个参数是应该传递给的两个参数,f2返回值f2是返回值f1.因此:

f1:
    rex_jmp  r8    ; third parameter (pointer to f2) is passed in r8
Run Code Online (Sandbox Code Playgroud)

这不仅可以轻松编写,而且它是大小和速度的最佳实现.如果需要,
您甚至可以事先修改v1v2参数,例如:

f1:
    inc      ecx        ; increment v1 (passed in ecx)

    ; multiply v2 (xmm1) by v1 (ecx)
    movd     xmm0, ecx
    cvtdq2ps xmm0, xmm0
    mulss    xmm1, xmm0

    rex_jmp  r8    ; third parameter (pointer to f2) is passed in r8
Run Code Online (Sandbox Code Playgroud)

如果你想做一些更复杂的事情,以下是它的工作方式:

f1:
    sub   rsp, 40     ; allocate the required space on the stack
    call  r8          ; call f2 through the pointer, passed in r8
    add   rsp, 40     ; clean up the stack
    ret
Run Code Online (Sandbox Code Playgroud)

请注意,您不需要在问题中显示的序言/结尾代码,但如果您选择包含它,则不会有任何损害.

但是,您在问题中显示的示例代码中执行的参数的混乱是错误的!在Microsoft x64调用约定中,第一个最多四个整数参数在RCX,RDX,R8和R9中从左到右传递到寄存器中.所有其他整数参数都在堆栈上传递.第一个最多四个浮点值也在寄存器中传递,从左到右,在XMM0,XMM1,XMM2和XMM3中.其余的都是在堆栈上传递的,结构对于寄存器来说太大了.

但奇怪的是,插槽是"固定的",因此即使你有一个整数和fp args混合,也只能使用4个寄存器args.从而:

????????????????????????????????????????
?           ?           TYPE           ?
? PARAMETER ????????????????????????????
?           ? Integer ? Floating-Point ?
????????????????????????????????????????
? First     ?   RCX   ?      XMM0      ?
????????????????????????????????????????
? Second    ?   RDX   ?      XMM1      ?
????????????????????????????????????????
? Third     ?   R8    ?      XMM2      ?
????????????????????????????????????????
? Fourth    ?   R9    ?      XMM3      ?
????????????????????????????????????????
? (rest)    ?         on stack         ?
????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

第二个参数是传递的第一个浮点值并不重要.它不在XMM0中,因为它是第一个浮点值,它在XMM1中,因为它是第二个参数,因此在第二个"槽"中.(这与x86-64 System V ABI不同,其中前6个整数args进入寄存器,无论是否存在FP args).

在Windows参数传递更详细的信息,请点击这里,包括示例.