c - 在 gcc 中编译时,如何在 c++ 的汇编代码中使用 C 变量?

标签 c gcc assembly

我使用 Visual Studio 编译了此代码,

double callVariadicDoubleFunc(double * doubles, unsigned int numDoubles,double(*TestFunc)(double,...))

{

    // sizeof(double) must be 8!
    if (numDoubles == 0)
        return 0;
    double * lastDouble = doubles + (numDoubles - 1);
    double res;
    int temp;

    __asm mov eax, numDoubles
    __asm mov edx, lastDouble
    __asm mov temp,esp

    __asm label_loop:
    __asm sub esp, 8
    __asm fld qword ptr [edx]
    __asm fstp qword ptr [esp]
    __asm sub eax, 1
    __asm sub edx, 8
    __asm test eax, eax
    __asm jnz label_loop

    __asm call TestFunc
    __asm fstp        qword ptr res;
    __asm mov esp, temp

    return res;
}

但现在我尝试使用 gcc 编译它,但有一些错误我无法解决!为了消除所有编译时错误,我将该代码稍微更改为以下形状:

double evaluationHelper(double* arguments, unsigned numDoubles, double(*mFunction)(...)) 
{
    int temp;
    double res;
    arguments += numDoubles;
    asm("mov eax, numDoubles"  "\n"
        "mov ecx, arguments"   "\n"
        "mov temp,esp"         "\n"

        "label_loop:"          "\n"
        "sub esp, 8"           "\n"
        "fld qword ptr [ecx]"  "\n"
        "fstp qword ptr [esp]" "\n"
        "sub eax, 1"           "\n"
        "sub ecx, 8"           "\n"
        "test eax, eax"        "\n"
        "jnz label_loop"       "\n"

        "call mFunction"       "\n"
        "fstp qword ptr res"   "\n"
        "mov esp, temp"        );
    return res;
}

但现在我遇到链接错误:

undefined reference to `numDoubles'
undefined reference to `arguments'
undefined reference to `temp'

知道如何解决这些问题吗?

旁注:我正在使用以下选项编译代码:“-g -masm=intel -O0 -Wall”

最佳答案

事情有点复杂。您应该使用带约束的扩展 GCC 程序集。另外,使用 temp 来保存堆栈指针是一个坏主意,因为局部变量的地址取决于堆栈指针的值。最好创建一个标准的堆栈框架。

此外,您还忘记将指针减 1。额外的好处是,在 GCC 中,数组中的最后一个指针不需要临时变量。

大致如下:

double evaluationHelper(double* arguments, unsigned numDoubles, double(*mFunction)()) 
{
    double res;
    asm(
        /* set up the frame pointer */
        "push ebp"             "\n"
        "mov ebp, esp"         "\n"

        "label_loop:"          "\n"
        "sub esp, 8"           "\n"
        "fld qword ptr [%2]"   "\n"
        "fstp qword ptr [esp]" "\n"
        "sub %1, 1"            "\n"
        "sub %2, 8"            "\n"
        "test %1, %1"          "\n"
        "jnz label_loop"       "\n"

        "call %3"              "\n"
        "fstp qword ptr [%0]"  "\n"
        "mov esp, ebp"         "\n"
        "pop ebp"              "\n"
        : /* no output */
        :"b"(&res), "a"(numDoubles), "c"(arguments + (numDoubles - 1)), "d"(mFunction) /* input */
        :"cc" /* clobber */);
    return res;
}

res 变量不能用作输出,因为 fstp 指令需要一个指针,而指针是输入。如果您没有对堆栈做有趣的事情,您可以使用 m 约束(请参阅下面我的更正)。

其他更正: 如果您不必在破坏列表中列出 EAX、ECX 和 EDX,则可以使用 r 约束,因为它们不会通过函数调用保留(并且您正在调用函数)。但是您无法列出用作输入/输出的被破坏的寄存器。

请注意,&res 使用通过函数调用保留的约束“b”,因此 fstp 将按预期工作。

最后,只有“cc”列在破坏列表中,因为您正在更改标志寄存器(使用 test)和函数调用。

运行gcc -masm=intel -save-temps我们可以检查生成的程序集:

; Before the asm
mov     eax, DWORD PTR [ebp+12] ; numDoubles
sub     eax, 1 
sal     eax, 3 
mov     ecx, eax           
add     ecx, DWORD PTR [ebp+8]  ; arguments + 8*(numDoubles - 1)
lea     ebx, [ebp-16]           ; res
mov     eax, DWORD PTR [ebp+12] ; numDoubles
mov     edx, DWORD PTR [ebp+16] ; mFunction

; The asm
push ebp
mov ebp, esp
label_loop: 
sub esp, 8  
fld qword ptr [ecx]
fstp qword ptr [esp]
sub eax, 1
sub ecx, 8
test eax, eax
jnz label_loop
call edx         ; clobbers eax, ecx, edx and flags
fstp qword ptr [ebx]
mov esp, ebp
pop ebp

; After the asm
fld QWORD PTR [ebp-16]  ; return res

这对我来说似乎基本正确。

更正:不完全是。函数指针和 res 变量必须保存在寄存器中,因为您正在创建编译器一无所知的堆栈帧,因此它无法计算这些局部变量的地址。所以我最后的一些更正很好但没用。

此外,指向 res 变量的指针必须位于 ECX 中。

已恢复。

关于c - 在 gcc 中编译时,如何在 c++ 的汇编代码中使用 C 变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8650729/

相关文章:

c - 有什么方法可以将内存位置更改为 C 中的共享内存?

c++ - 为什么 gdb 不能显示调试信息?

performance - 对x86上的L1缓存行的独占访问?

assembly - x86_64 程序集 - 尝试在 x64 程序集中编辑数组内的字节时出现段错误

assembly - 如何在 x86 上的寄存器中存储地址

c - 倒回 VT100 终端命令中的一段文本

c - 如何模拟头文件中的函数?

c - strcat() 实现有效,但最后导致核心转储

c++ - 如何可靠地让析构函数覆盖缓冲区而不在 C++ 中对其进行优化

c - 确定体系结构是 32 位还是 64 位的优雅而安全的方法