虽然在内联汇编中使用适当的参数调用函数指针应该不是什么大问题,但我认为在 x64 中天真地重新编码不会对您有帮助,因为要使用的调用约定很可能不同(默认值对于 32 位和 64 位 linux 来说肯定是不同的)。看一看here http://www.agner.org/optimize/calling_conventions.pdf了解详情。所以我想,如果在这种情况下您可以摆脱内联汇编(请参阅其他答案),那么移植会更容易。
Edit:好的,我知道你可能需要使用汇编。这里有一些提示。
根据Agner Fog的文档,linux x64使用RDI、RSI、RDX、RCX、R8、R9和XMM0-XMM7进行参数传输。这意味着为了实现您想要的(不考虑浮点使用)您的函数必须:
(1)保存所有需要保存的寄存器(RBX、RBP、R12-R15):在堆栈上留出空间,将这些寄存器移到那里。这将类似于(英特尔语法):
sub rsp, 0xSomeNumber1
mov [rsp+i*8], r# ; insert appropriate i for each register r# to be moved
(2) 评估必须通过堆栈传递给目标函数的参数数量。使用它在堆栈上留出所需的空间(sub rsp, 0xSomeNumber2
), 考虑在内0xSomeNumber1
这样堆栈的末尾将是 16 字节对齐的,即rsp
必须是16的倍数,请勿修改rsp
之后直到被调用的函数返回。
(3) 将函数参数加载到堆栈(如果需要)和用于参数传输的寄存器中。在我看来,如果从堆栈参数开始并最后加载寄存器参数是最简单的。
;loop over stack parameters - something like this
mov rax, qword ptr [AddrOfFirstStackParam + 8*NumberOfStackParam]
mov [rsp + OffsetToFirstStackParam + 8*NumberOfStackParam], rax
根据您设置例程的方式,第一个堆栈参数等的偏移量可能是不必要的。然后设置寄存器传递的参数数量(跳过不需要的参数):
mov r9, Param6
mov r8, Param5
mov rcx, Param4
mov rdx, Param3
mov rsi, Param2
mov rdi, Param1
(4) 使用与上面不同的寄存器调用目标函数:
call qword ptr [r#] ; assuming register r# contains the address of the target function
(5)恢复保存的寄存器并恢复rsp
到它在进入你的函数时所具有的值。如有必要,请将被调用函数的返回值复制到您想要的任何位置。就这样。
Note:上面的草图没有考虑在 XMM 寄存器中传递的浮点值,但适用相同的原则。免责声明:我在 Win64 上做过类似的事情,但从未在 Linux 上做过,所以可能有一些我忽略的细节。好好阅读,仔细编写代码并好好测试。