assembly 如何在C++中像在C中那样声明带有动态参数的汇编函数

z2acfund  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(169)

我有一些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,这就是它需要动态参数的原因
如何让它发挥作用?

vu8f3i0k

vu8f3i0k1#

在参数列表中使用...指定函数是采用未指定参数的variadic function,例如:

extern "C" VOID asmFunc(...); // C++ function prototype version

//actual use example
asmFUNC(var1,ptr2,HANDLE);
asmFUNC(ptr4,var2,NULL,eg);
cqoc49vn

cqoc49vn2#

**您可以在同一个位址中有多个标签。**您可以有多个原型,它们的名称不同,但碰巧都有相同的位址(因此是由相同的机器码实作)。在您的.asm档案中,您可以放入:

global foo_int, foo_long, foo_float   ; NASM syntax for putting these in the symbol table
foo_int:
foo_long:
foo_float:
     your code goes here
     ret

您也可以使用汇编指令为符号创建别名,如GAS .set foo_long, foo
您可以将其视为同一个函数的多个名称,或者其他函数的实现将落入真实的函数,作为优化的tailcall,您甚至通过使它们在asm中连续来优化掉jmp
或者使用GNU扩展,声明多个C或C++名称,它们都使用相同的asm符号名。编译器生成的.obj将使每个调用点引用相同的符号名。(您完全手动设置,因此即使没有extern "C",也不会发生名称损坏和前导下划线,除非您选择了一个。)

// GNU C or C++;  compatible with the mainstream compilers other than MSVC
int foo_int(...) asm ("foo");
long foo_long(...) asm ("foo");
float foo_float(...) asm ("foo");

在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不同。

相关问题