使用GCC的内联汇编直接调用

如果要从内联汇编调用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)); 

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

错误:后缀或操作数对`call’无效

这是因为GCC会发出代码

 call $callee 

代替

 call callee 

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

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

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

现在我只需要找出%P0实际意味着什么,因为它似乎是一个无证件的特征……

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

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

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

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

技巧是字符串文字连接。 在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 ++,因为它们被破坏了。

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

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