13

如果要从内联汇编中调用 C/C++ 函数,可以执行以下操作:

void callee() {}
void caller()
{
    asm("call *%0" : : "r"(callee));
}

然后 GCC 将发出如下代码:

movl $callee, %eax
call *%eax

这可能会产生问题,因为间接调用会破坏旧 CPU 上的管道。

由于地址callee最终是一个常数,可以想象使用i约束是可能的。引用 GCC 在线文档

‘我’

允许使用立即整数操作数(具有常数值的操作数)。这包括符号常量,其值仅在汇编时或以后才知道。

如果我尝试像这样使用它:

asm("call %0" : : "i"(callee));

我从汇编程序中收到以下错误:

错误:“调用”的后缀或操作数无效

这是因为 GCC 发出代码

call $callee

代替

call callee

所以我的问题是是否可以让 GCC 输出正确的call.

4

4 回答 4

14

我从 GCC 的邮件列表中得到了答案:

asm("call %P0" : : "i"(callee));  // FIXME: missing clobbers

现在我只需要找出%P0实际含义,因为它似乎是一个未记录的功能......

编辑:查看 GCC 源代码后,并不清楚P约束前面的代码是什么意思。但是,除其他外,它会阻止 GCC 将 a$放在常量值的前面。这正是我在这种情况下所需要的。


为了安全起见,您需要告诉编译器函数调用可能修改的所有寄存器,例如: "eax", "ecx", "edx", "xmm0", "xmm1", ..., "st(0)", "st(1)", ....

请参阅在扩展的内联 ASM 中调用 printf 以获取完整的 x86-64 示例,该示例正确且安全地从内联 asm 进行函数调用。

于 2010-08-13T09:56:12.473 回答
2

也许我在这里遗漏了一些东西,但是

extern "C" void callee(void) 
{

}

void caller(void)
{
  asm("call callee\n");
}

应该可以正常工作。您需要 extern "C" 以便名称不会根据 C++ 命名修改规则进行修饰。

于 2016-01-13T13:13:07.973 回答
0

如果您正在生成 32 位代码(例如 -m32 gcc 选项),则以下 asm 内联会发出直接调用:

asm ("call %0" :: "m" (callee));
于 2015-06-11T20:06:26.893 回答
-1

诀窍是字符串文字连接。在 GCC 开始尝试从您的代码中获取任何真正含义之前,它将连接相邻的字符串文字,因此即使汇编字符串与您在程序中使用的其他字符串不同,如果您这样做,它们也应该被连接:

#define ASM_CALL(X) asm("\t call  " X "\n")


int main(void) {
    ASM_CALL( "my_function" );
    return 0;
}

由于您使用的是 GCC,您也可以这样做

#define ASM_CALL(X) asm("\t call  " #X "\n")

int main(void) {
   ASM_CALL(my_function);
   return 0;
}

如果您还不知道,您应该知道从内联汇编调用事物是非常棘手的。当编译器生成自己对其他函数的调用时,它包括在调用前后设置和恢复事物的代码。但是,它不知道它应该为您的通话做任何这些。您必须自己包含它(要正确处理非常棘手,并且可能会因编译器升级或编译标志而中断),或者确保您的函数的编写方式似乎没有更改任何寄存器或条件堆栈(或上面的变量)。

编辑这仅适用于 C 函数名称 - 不适用于 C++,因为它们已被破坏。

于 2010-08-12T15:39:16.833 回答