我有一些C代码,我想把它移植到C++,问题是在C++中,我不能使用汇编函数,因为它是动态使用的
C版本
extern asmFunc(); // C function prototype version
//actual use example
asmFunc(var1,ptr2,HANDLE);
asmFunc(ptr4,var2,NULL,eg ...); //everything works
C++版本
extern "C" VOID asmFunc(); // C++ function prototype version
//actual use example
asmFunc(var1,ptr2,HANDLE); // E0140 too many arguments in function call
asmFunc(ptr4,var2,NULL,eg ...); // E0140 too many arguments in function call
Assembly函数在单独的asm文件中声明,它使用ntdll.dll函数中的直接syscalls,这就是它需要动态参数的原因
如何让它发挥作用?
2条答案
按热度按时间vu8f3i0k1#
在参数列表中使用
...
指定函数是采用未指定参数的variadic function,例如:cqoc49vn2#
**您可以在同一个位址中有多个标签。**您可以有多个原型,它们的名称不同,但碰巧都有相同的位址(因此是由相同的机器码实作)。在您的
.asm
档案中,您可以放入:您也可以使用汇编指令为符号创建别名,如GAS
.set foo_long, foo
。您可以将其视为同一个函数的多个名称,或者其他函数的实现将落入真实的函数,作为优化的tailcall,您甚至通过使它们在asm中连续来优化掉
jmp
。或者使用GNU扩展,声明多个C或C++名称,它们都使用相同的asm符号名。编译器生成的
.obj
将使每个调用点引用相同的符号名。(您完全手动设置,因此即使没有extern "C"
,也不会发生名称损坏和前导下划线,除非您选择了一个。)在Godbolt上使用clang和GCC来查看它的运行情况,注意C源代码中的
return foo_float('a', 1);
在编译器生成的asm中编译为call foo
。(Godbolt通常在Linux上编译,但我使用-mabi=ms
来让GCC使用Windows x64调用约定。)如果这是在一个单独的DLL中,每个名称将被单独解析,为多个DLL导入条目浪费了一点空间(无论Windows如何调用函数指针,它们都等效于Linux动态链接中的GOT)。GNU C
asm("name")
方式没有这个缺点,因为只有一个asm符号名称。但是,如果您只是将这个
.asm
文件链接到C调用方所在的同一个可执行文件或库中,则会在构建时(而不是每次运行时)解析并删除单独的符号表条目。对于每个不同的函数签名,您都可以有一个完整的原型(和名称)。
**或者您可以为每个不同的返回类型指定一个名称,
使用
extern "C" int foo_int(...);
**,如雷米的答案所示。注意,(...)
将触发默认参数提升,例如float
将提升为double
,因此不可能传递实际的float
。(这就是为什么printf
的“%f”转换需要一个双精度浮点数。)不过,这些促销活动对整数来说是无害的,而且非常便宜,主流的x86和x86-64调用约定被设计为,args被传递给变量函数的方式与它们传递给具有相同args类型的原型函数的方式相同x86-64 System V要求变量函数的调用者设置AL = # of XMM args,一个从0到8的数字;没有任何FP或向量参数,这意味着每个调用站点将获得一个额外的
xor eax,eax
。这与一个2字节的NOP一样便宜,尤其是在Intel上。假设返回类型对于任何给定的调用点都是静态已知的,这应该可以解决整个问题。
**返回
union
**并在运行时决定使用哪个返回值并不一定有效。某些调用约定将在不同的寄存器中返回纯float
(例如xmm 0)。即使是纯整数,int
和更大结构体的并集也会返回到内存中,与普通int
不同。