我正试图从我的C代码调用一个golang函数。Golang不使用标准的x86_64调用约定,因此我必须自己来实现转换。由于GCC不想将CDECL与x86_约定混合,
我尝试使用内联程序集调用函数:

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语法。有人能帮我吗?

最佳答案:

"g"mov mem,memmov"ri"[rsp+16][rsp-4]pushmov
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
    );

请注意,输出不是早期的clobber,因此编译器可以(根据自己的选择)使用这些寄存器作为输入,但我们不会强制它,因此编译器仍然可以自由使用其他寄存器或立即寄存器。
经过进一步讨论,go函数不会阻塞rbp,因此没有理由手动保存/恢复它。您可能想这样做的唯一原因是本地人可能会使用RBP相对寻址模式,而旧的gcc使在不使用
-fomit-frame-pointer
编译时在RBP上声明一个clobber成为一个错误。(我想。或者我在考虑32位图像代码的ebx。)
另外,如果您使用的是x86-64 SystemVABI,请注意内联ASM不能破坏红色区域。编译器假定不会发生这种情况,并且无法在红色区域上声明一个clobber,甚至无法在每个函数的基础上设置
-mno-redzone
。所以您可能需要
sub rsp, 128 +  0xe0
。或者,
0xe0
已经包含足够的空间来跳过红色区域,如果红色区域不是被调用方参数的一部分。