2 回答

TA貢獻1942條經驗 獲得超3個贊
約束允許編譯器選擇內存或寄存器,所以很明顯,如果發生這種情況, "g"你最終會得到。最多可以有 1 個內存操作數。(與所有 x86 指令一樣,最多可以有一個顯式內存操作數。)mov mem,memmov
"ri"對將被移動到內存目標的輸入使用約束,以允許寄存器或立即但不是內存。
此外,您正在修改 RSP,因此您無法安全地使用內存源操作數。編譯器將假設它可以使用[rsp+16]or之類的尋址模式[rsp-4]。所以你不能使用pushinstead of mov。
您還需要在所有被調用破壞的寄存器上聲明破壞者,因為函數調用會這樣做。(或者更好的是,也許在那些被調用破壞的寄存器中請求輸入,這樣編譯器就不必通過像 RBX 這樣的調用保留寄存器來反彈它們。但是你需要讓這些操作數讀/寫或聲明單獨的輸出操作數相同的寄存器讓編譯器知道它們將被修改。)
因此,提高效率的最佳選擇可能是
int ecx, edx, edi, esi; // dummy outputs as clobbers
register int r8 asm("r8d"); // for all the call-clobbered regs in the calling convention
register int r9 asm("r9d");
register int r10 asm("r10d");
register int r11 asm("r11d");
// These are the regs for x86-64 System V.
// **I don't know what Go actually clobbers.**
asm("sub rsp, 0xe0\n\t" // adjust as necessary to align the stack before a call
// "push args in reverse order"
"push %[fn_len] \n\t"
"push %[fn_str] \n\t"
"call \n\t"
"add rsp, 0xe0 + 3*8 \n\t" // pop red-zone skip space + pushed args
// real output in RAX, and dummy outputs in call-clobbered regs
: "=a"(retval), "=c"(ecx), "=d"(edx), "=D"(edi), "=S"(esi), "=r"(r8), "=r"(r9), "=r"(r10), "=r"(r11)
: [fn_str] "ri" (filename.str), [fn_len] "ri" (filename.len), etc. // inputs can use the same regs as dummy outputs
: "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", // All vector regs are call-clobbered
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
"memory" // if you're passing any pointers (even read-only), or the function accesses any globals,
// best to make this a compiler memory barrier
);
請注意,輸出不是early -clobber,因此編譯器可以(根據其選擇)將這些寄存器用于輸入,但我們不強制這樣做,因此編譯器仍然可以自由使用其他寄存器或立即數。
經過進一步討論,Go 函數不會破壞 RBP,因此沒有理由手動保存/恢復它。您可能想要這樣做的唯一原因是本地人可能使用 RBP 相對尋址模式,而較舊的 GCC 在不使用-fomit-frame-pointer. (我想?;蛘咭苍S我正在考慮 32 位 PIC 代碼中的 EBX。)
此外,如果您使用的是 x86-64 System V ABI,請注意內聯 asm 不得破壞紅色區域。編譯器假設這不會發生,并且沒有辦法在紅色區域聲明一個 clobber,甚至無法-mno-redzone在每個函數的基礎上設置。所以你可能需要sub rsp, 128 + 0xe0?;蛘?xe0已經包含足夠的空間來跳過紅色區域,如果它不是被調用者參數的一部分的話。

TA貢獻1845條經驗 獲得超8個贊
原始發布者將此解決方案添加為對他們問題的編輯:
如果有人發現這個,當您嘗試使用內聯 asm 調用 golang 代碼時,接受的答案對您沒有幫助!接受的答案僅有助于解決我最初的問題,這幫助我修復了 golangcall。使用這樣的東西:**
void* __cdecl go_call(void* func, __int64 p1, __int64 p2, __int64 p3, __int64 p4){
void* ret;
asm volatile(" sub rsp, 0x28; \t\n\
mov [rsp], %[p1]; \t\n\
mov [rsp+0x8], %[p2]; \t\n\
mov [rsp+0x10], %[p3]; \t\n\
mov [rsp+0x18], %[p4]; \t\n\
call %[func_addr]; \t\n\
add rsp, 0x28; "
:
: [p1] "ri"(p1), [p2] "ri"(p2),
[p3] "ri"(p3), [p4] "ri"(p4), [func_addr] "ri"(func)
: );
return ret;
}
- 2 回答
- 0 關注
- 286 瀏覽
添加回答
舉報