[英]How to declare assembly function with dynamic arguments in C++ like in C
我有一些 C 代碼,我想將它移植到 c++,問題是在 C++ 中我不能使用程序集 function,因為它是動態使用
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
程序集 function 在單獨的 asm 文件中聲明,它使用來自 ntdll.dll 函數的直接系統調用,這就是為什么它需要動態 arguments
如何讓它發揮作用?
使用...
在參數列表中指定 function 是一個 可變參數 function采用未指定的 arguments,例如:
extern "C" VOID asmFunc(...); // C++ function prototype version
//actual use example
asmFUNC(var1,ptr2,HANDLE);
asmFUNC(ptr4,var2,NULL,eg);
同一個地址可以有多個標簽。 您可以擁有多個名稱不同的原型,而這些原型恰好都具有相同的地址(因此由相同的機器代碼實現)。 在您的.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
。
您可以將其視為同一個 function 的多個名稱,或者將其實現落入實際 function 的其他函數,作為優化的尾部調用,您甚至通過使它們在 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");
使用 clang 和 GCC 查看它在Godbolt上的運行情況,注意return foo_float('a', 1);
在 C++ 源代碼中編譯為編譯器生成的 asm 中的call foo
。 (Godbolt 正常編譯為 Linux,但我使用-mabi=ms
獲取 GCC 以使用 Windows x64 調用約定。)
如果這是在一個單獨的 DLL 中,每個名稱將被單獨解析,為多個 DLL 導入條目浪費一點空間(無論 Windows 調用等同於 Linux 動態鏈接中的 GOT 的函數指針。)GNU C asm("name")
方式沒有這個缺點,因為只有一個 asm 符號名稱。
但是,如果您只是將此.asm
文件鏈接到與 C++ 調用方相同的可執行文件或庫中,則單獨的符號表條目將在構建時解析,而 go 將在構建時消失,而不是每次運行時。
對於每個不同的 function 簽名,您都可以擁有完整的原型(和名稱)。
或者你可以為每個不同的返回類型起一個名字,
使用extern "C" int foo_int(...);
正如雷米的回答所示。 請注意(...)
將觸發默認參數提升,例如float
將提升為double
,因此不可能傳遞實際的float
。 (這就是為什么printf
的“%f”轉換需要加倍。)
不過,對於 integer,促銷是無害的並且非常便宜,只是對int
進行符號擴展或零擴展。 主流 x86 和 x86-64 調用約定的設計使得 args 被傳遞給可變參數函數,就像它們傳遞給具有相同 arg 類型的原型 function 一樣(在將 float 提升為 double 之后)。 x86-64 System V 要求可變參數函數的調用者設置 AL = # of XMM args,一個從 0 到 8 的數字; 沒有任何 FP 或矢量參數,這意味着每個調用站點將獲得一個額外的xor eax,eax
。 這與 2 字節 NOP 一樣便宜,尤其是在 Intel 上。
假設返回類型對於任何給定的調用站點都是靜態已知的,這應該可以解決您的整個問題。 不然就更麻煩了!
返回union
並在運行時決定使用哪個返回值不一定有效。 一些調用約定會在不同的寄存器(例如 xmm0)中返回一個純float
,而不是他們為具有float
成員的聯合選擇的。 即使對於純 integer, int
和更大結構的聯合也會在 memory 中返回,這與普通int
不同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.