如果要从内联汇编调用C / C ++函数,可以执行以下操作:
void callee() {}
void caller()
{
asm("call *%0" : : "r"(callee));
}
然后GCC会发出如下代码:
movl $callee, %eax
call *%eax
这可能会有问题,因为间接调用会破坏旧CPU上的管道。
由于callee
的地址最终是常量,因此可以想象可以使用i
约束。从GCC在线引用docs:
`I'
允许使用立即整数操作数(具有常量值的操作数)。这个 包括符号常量 值只有在汇编时才会知道 时间或以后。
如果我尝试这样使用它:
asm("call %0" : : "i"(callee));
我从汇编程序中收到以下错误:
错误:对于`call'
,后缀或操作数无效
这是因为GCC发出代码
call $callee
而不是
call callee
所以我的问题是,是否可以使GCC输出正确call
。
答案 0 :(得分:10)
我从GCC的邮件列表中获得了answer:
asm("call %P0" : : "i"(callee));
现在我只需要找出%P0
实际意味着什么,因为它似乎是一个没有记录的功能......
编辑:在查看GCC源代码之后,不清楚约束前面的代码P
是什么意思。但是,除其他外,它阻止GCC在常量值前加$
。这正是我在这种情况下所需要的。
答案 1 :(得分:1)
也许我在这里遗漏了一些东西,但是
extern "C" void callee(void)
{
}
void caller(void)
{
asm("call callee\n");
}
应该可以正常工作。您需要extern“C”,以便不会根据C ++命名修改规则对名称进行修饰。
答案 2 :(得分:0)
技巧是字符串文字连接。在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;
}
如果您还不知道,您应该知道从内联汇编调用内容非常棘手。当编译器生成自己对其他函数的调用时,它包含用于在调用之前和之后设置和恢复事物的代码。但是,它并不知道应该为你的通话做任何这样的事情。您必须自己包含它(非常棘手,可能会因编译器升级或编译标志而中断)或确保您的函数编写方式使其看起来没有更改任何寄存器或条件堆栈(或其上的变量)。
答案 3 :(得分:0)
如果您正在生成32位代码(例如-m32 gcc选项),则以下asm inline会发出直接调用:
asm ("call %0" :: "m" (callee));