如何通过作为参数传递的函数指针调用函数?
How to call a function through a function pointer passed as an argument?
如何在汇编中的 f1 函数的第三个参数中调用传递的函数 (*f2)?
声明看起来像这样:
extern float f1(int v1, float v2, float (*f2)(int v3, float v4));
我想将 v1 传递给 v3,将 v2 传递给 v4,调用函数 f2,并且 return 值
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
我用什么代替问号?
汇编代码因使用的微控制器而异。
不完全是您要查找的内容,但遵循 Windows 具有英特尔核心 I7 的平台生成的汇编代码:-
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 , %esp
movl LC0, %eax
movl %eax, -12(%ebp)
movl , -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
希望这会有所帮助。
没有"Abi64"这样的东西。由于您标记了 MASM 问题,我们可以猜测您正在使用 Windows 平台,并且显然“64”意味着这是 64 位代码,因此确实极大地缩小了可能性。但是,Windows 上仍然有两种常见的 64 位代码调用约定。其中之一是 __vectorcall
,另一个是 Microsoft x64 调用约定(最初发明该约定是为了让所有其他调用约定都过时,但……没有)。
由于 Microsoft x64 调用约定是最常见的,在这种特殊情况下,使用 __vectorcall
不会改变任何东西,我假设您正在使用它。然后所需的代码变得非常简单。您需要做的就是从 f1
跳转到 f2
,因为堆栈的设置是相同的。 f1
的前两个参数是应该传给f2
的两个参数,f2
的return值是[=的return值16=]。因此:
f1:
rex_jmp r8 ; third parameter (pointer to f2) is passed in r8
这不仅写起来很简单,而且是大小和速度方面的最佳实现。
如果需要,您甚至可以预先修改 v1
或 v2
参数,例如:
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
请注意,您不需要问题中显示的 prologue/epilogue 代码,但如果您选择包含它也不会造成任何伤害。
但是,您在问题中显示的示例代码中进行的参数改组是错误的!在 Microsoft x64 调用约定中,在 RCX、RDX、R8 和 R9 中,前四个整数参数从左到右传递到寄存器中。所有其他整数参数都在堆栈上传递。在 XMM0、XMM1、XMM2 和 XMM3 中,前最多四个浮点值也被传递到寄存器中,从左到右。其余的在堆栈上传递,连同对于寄存器来说太大的结构。
不过,奇怪的是插槽是 "fixed",所以总共只能使用 4 个寄存器参数,即使您混合使用整数和 fp 参数。因此:
╔═══════════╦══════════════════════════╗
║ ║ TYPE ║
║ PARAMETER ╠═════════╦════════════════╣
║ ║ Integer ║ Floating-Point ║
╠═══════════╬═════════╬════════════════╣
║ First ║ RCX ║ XMM0 ║
╠═══════════╬═════════╬════════════════╣
║ Second ║ RDX ║ XMM1 ║
╠═══════════╬═════════╬════════════════╣
║ Third ║ R8 ║ XMM2 ║
╠═══════════╬═════════╬════════════════╣
║ Fourth ║ R9 ║ XMM3 ║
╠═══════════╬═════════╩════════════════╣
║ (rest) ║ on stack ║
╚═══════════╩══════════════════════════╝
第二个参数是第一个传递的浮点值并不重要。它不进入 XMM0,因为它是第一个浮点值,它进入 XMM1,因为它是第二个参数,因此在第二个 "slot" 中。 (这不同于 the x86-64 System V ABI,其中前 6 个整数参数进入寄存器,无论是否有 FP 参数)。
关于 Windows 参数传递的更多详细文档可用 here,包括示例。
如何在汇编中的 f1 函数的第三个参数中调用传递的函数 (*f2)? 声明看起来像这样:
extern float f1(int v1, float v2, float (*f2)(int v3, float v4));
我想将 v1 传递给 v3,将 v2 传递给 v4,调用函数 f2,并且 return 值
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
我用什么代替问号?
汇编代码因使用的微控制器而异。
不完全是您要查找的内容,但遵循 Windows 具有英特尔核心 I7 的平台生成的汇编代码:-
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 , %esp
movl LC0, %eax
movl %eax, -12(%ebp)
movl , -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
希望这会有所帮助。
没有"Abi64"这样的东西。由于您标记了 MASM 问题,我们可以猜测您正在使用 Windows 平台,并且显然“64”意味着这是 64 位代码,因此确实极大地缩小了可能性。但是,Windows 上仍然有两种常见的 64 位代码调用约定。其中之一是 __vectorcall
,另一个是 Microsoft x64 调用约定(最初发明该约定是为了让所有其他调用约定都过时,但……没有)。
由于 Microsoft x64 调用约定是最常见的,在这种特殊情况下,使用 __vectorcall
不会改变任何东西,我假设您正在使用它。然后所需的代码变得非常简单。您需要做的就是从 f1
跳转到 f2
,因为堆栈的设置是相同的。 f1
的前两个参数是应该传给f2
的两个参数,f2
的return值是[=的return值16=]。因此:
f1:
rex_jmp r8 ; third parameter (pointer to f2) is passed in r8
这不仅写起来很简单,而且是大小和速度方面的最佳实现。
如果需要,您甚至可以预先修改 v1
或 v2
参数,例如:
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
请注意,您不需要问题中显示的 prologue/epilogue 代码,但如果您选择包含它也不会造成任何伤害。
但是,您在问题中显示的示例代码中进行的参数改组是错误的!在 Microsoft x64 调用约定中,在 RCX、RDX、R8 和 R9 中,前四个整数参数从左到右传递到寄存器中。所有其他整数参数都在堆栈上传递。在 XMM0、XMM1、XMM2 和 XMM3 中,前最多四个浮点值也被传递到寄存器中,从左到右。其余的在堆栈上传递,连同对于寄存器来说太大的结构。
不过,奇怪的是插槽是 "fixed",所以总共只能使用 4 个寄存器参数,即使您混合使用整数和 fp 参数。因此:
╔═══════════╦══════════════════════════╗
║ ║ TYPE ║
║ PARAMETER ╠═════════╦════════════════╣
║ ║ Integer ║ Floating-Point ║
╠═══════════╬═════════╬════════════════╣
║ First ║ RCX ║ XMM0 ║
╠═══════════╬═════════╬════════════════╣
║ Second ║ RDX ║ XMM1 ║
╠═══════════╬═════════╬════════════════╣
║ Third ║ R8 ║ XMM2 ║
╠═══════════╬═════════╬════════════════╣
║ Fourth ║ R9 ║ XMM3 ║
╠═══════════╬═════════╩════════════════╣
║ (rest) ║ on stack ║
╚═══════════╩══════════════════════════╝
第二个参数是第一个传递的浮点值并不重要。它不进入 XMM0,因为它是第一个浮点值,它进入 XMM1,因为它是第二个参数,因此在第二个 "slot" 中。 (这不同于 the x86-64 System V ABI,其中前 6 个整数参数进入寄存器,无论是否有 FP 参数)。
关于 Windows 参数传递的更多详细文档可用 here,包括示例。