亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

使用內聯匯編用 C 調用 golang 函數時,“mov”的內存引用過多

使用內聯匯編用 C 調用 golang 函數時,“mov”的內存引用過多

Go
互換的青春 2023-05-08 16:02:20
我正在嘗試從我的 C 代碼中調用 golang 函數。Golang 不使用標準的 x86_64 調用約定,所以我不得不求助于自己實現轉換。由于 gcc 不想將 cdecl 與 x86_64 約定混合使用,我嘗試使用內聯匯編調用該函數:void go_func(struct go_String filename, void* key, int error){    void* f_address = (void*)SAVEECDSA;    asm volatile("  sub     rsp, 0xe0;           \t\n\                    mov     [rsp+0xe0], rbp;   \t\n\                    mov     [rsp], %0;            \t\n\                    mov     [rsp+0x8], %1;       \t\n\                    mov    [rsp+0x18], %2;       \t\n\                    call    %3;                     \t\n\                    mov     rbp, [rsp+0xe0];   \t\n\                    add     rsp, 0xe0;"                              :                    : "g"(filename.str), "g"(filename.len), "g"(key), "g"(f_address)                    : );    return;}可悲的是,編譯器總是向我拋出一個我不明白的錯誤:./code.c:241: Error: too many memory references for `mov'這對應于這一行:mov     [rsp+0x18], %2;       \t\n\如果我刪除它,編譯工作。我不明白我的錯誤是什么......我正在使用 -masm=intel 標志進行編譯,因此我使用了 Intel 語法。有人可以幫幫我嗎?
查看完整描述

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已經包含足夠的空間來跳過紅色區域,如果它不是被調用者參數的一部分的話。


查看完整回答
反對 回復 2023-05-08
?
精慕HU

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;

}


查看完整回答
反對 回復 2023-05-08
  • 2 回答
  • 0 關注
  • 286 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號