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

时间:2016-12-06 11:07:49

标签: assembly function-pointers x86-64 masm

如何在汇编中调用f1函数的第三个参数中的传递函数(* f2)? 声明如下:

scenario02 ramp to 70 over y seconds

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

extern float f1(int v1, float v2, float (*f2)(int v3, float v4));

我应该用什么来代替问号?

2 个答案:

答案 0 :(得分:5)

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

由于Microsoft x64调用约定是最常见的,并且在这种特殊情况下,使用__vectorcall不会改变任何内容,我将假设它是您的#&# 39;重新使用。然后所需的代码变得非常微不足道。您需要做的就是从f1跳转到f2,因为堆栈的设置方式相同。 f1的前两个参数是应传递给f2的两个参数,f2的返回值是f1的返回值。因此:

f1:
    rex_jmp  r8    ; third parameter (pointer to f2) is passed in r8

这不仅可以轻松编写,而且是大小和速度的最佳实现 如果需要,您甚至可以事先修改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

如果您想要做一些更复杂的事情,请按照以下方式进行操作:

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

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

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

但是,奇怪的是,插槽是固定的#34;因此,即使你有一个整数和fp args混合,也只能使用4个寄存器参数。因此:

╔═══════════╦══════════════════════════╗
║           ║           TYPE           ║
║ PARAMETER ╠═════════╦════════════════╣
║           ║ Integer ║ Floating-Point ║
╠═══════════╬═════════╬════════════════╣
║ First     ║   RCX   ║      XMM0      ║
╠═══════════╬═════════╬════════════════╣
║ Second    ║   RDX   ║      XMM1      ║
╠═══════════╬═════════╬════════════════╣
║ Third     ║   R8    ║      XMM2      ║
╠═══════════╬═════════╬════════════════╣
║ Fourth    ║   R9    ║      XMM3      ║
╠═══════════╬═════════╩════════════════╣
║ (rest)    ║         on stack         ║
╚═══════════╩══════════════════════════╝

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

有关Windows参数传递的更详细文档可用here,包括示例。

答案 1 :(得分:-2)

装配代码因使用的微控制器而异。

不完全是您正在寻找的,但是遵循在具有intel核心I7的Windows平台上生成的汇编代码: -

C代码: -

extern float f1(int v1, float v2, float (*f2)(int v3, float v4))
{
    float a = 10.0;
    int b = 12;

    f2(b, a);
    return a+ 12.5;
}

汇编代码: -

_f1:
 pushl  %ebp
 movl   %esp, %ebp
 subl   $40, %esp
 movl   LC0, %eax

 movl   %eax, -12(%ebp)
 movl   $12, -16(%ebp)
 movl   -12(%ebp), %eax
 movl   %eax, 4(%esp)
 movl   -16(%ebp), %eax
 movl   %eax, (%esp)
 movl   16(%ebp), %eax
 call   *%eax
 fstp   %st(0)
 flds   -12(%ebp)
 flds   LC1
 faddp  %st, %st(1)
 leave
ret

希望这会有所帮助。