代码之家  ›  专栏  ›  技术社区  ›  mtvec

使用GCC的内联程序集直接调用

  •  9
  • mtvec  · 技术社区  · 15 年前

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

    GCC将发出如下代码:

    movl $callee, %eax
    call *%eax
    

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

    自从 callee 终究是一个常数,可以想象,用 i 约束。引用GCC在线 docs :

    `我很抱歉

    允许立即整数操作数(具有常量值的操作数)。这个 包括符号常量 只有在装配时才知道值 时间或更晚。

    如果我试着这样使用它:

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

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

    这是因为GCC发出代码

    call $callee
    

    而不是

    call callee
    

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

    4 回复  |  直到 7 年前
        1
  •  11
  •   mtvec    15 年前

    我拿到钱了 answer 从GCC的邮件列表:

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

    现在我只想知道 %P0 实际上意思是因为它似乎是一个未记录的功能。。。

    编辑 :在查看了GCC源代码之后,还不清楚代码是什么 P 在一个约束的前面意味着。但是,除其他外,它阻止了海湾合作委员会 $ 在常量前面。这正是我在这个案子中所需要的。

        2
  •  2
  •   markpapadakis    10 年前

    也许我错过了什么,但是

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

    应该很管用。你需要外部的“C”,所以这个名称不会基于C++命名规则来修饰。

        3
  •  0
  •   Community Mohan Dere    6 年前

    诀窍是字符串-文字串联。在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++,因为它们被修改了。

        4
  •  0
  •   user5000736 user5000736    11 年前

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

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