ASM内联调用C外部函数

时间:2015-11-04 17:59:25

标签: c gcc arm inline-assembly gcc-extensions

我试图通过asm内联分支来调用c中的外部函数。我正在编译一个arm m0指令集,但它返回错误的表达式。

代码是:

__asm volatile (
                "   cmp     r3,#0                   \n"                     
                "   b %[my_function]                \n" //Call function
                "   bx r14                          \n"
                : // no output
                : [my_function] "i" (my_function) // input
                : "r0" // clobber
            );

回报是:

/tmp/ccICkDIE.s: Assembler messages:
/tmp/ccICkDIE.s:152: Error: bad expression -- `b #my_function'

我们需要做什么?

2 个答案:

答案 0 :(得分:0)

您需要BL指令。这是“分支和链接”。它执行跳转并将返回地址存储在r14

但是,您仍然遇到问题......当您执行BL时,它会销毁您需要的<{1}}。即使在以下情况之后,您还有更多工作要做:

r14

你需要更多的调查。您可能希望反汇编已编译的函数,并查看内联asm周围的包装器并进行相应调整。它可以为你做stmfd sp!,{v1-v6,lr} // preserve caller registers bl %[my_function] // call function ldmfd sp!,{v1-v6,pc} @std // restore caller registers and return 。尝试将stmfd/ldmfd标记为clobber。

仅使用r14可能会更好。没有恢复的BL可能会产生无限循环或不可预测的结果。我会把它关掉

答案 1 :(得分:0)

在撰写下面的内容后,我记得ethernut tutorial。他几乎有相同的答案,

asm volatile(
    "mov lr, %1\n\t"
    "bx %0\n\t"
    : : "r" (main), "r" (JMPADDR));

OP会很好地阅读本教程;即使它是传统的ARM,而不是&#39; m0&#39;

您可以使用'r'约束将地址放在寄存器中并分支到它。

An example可以在在线编译器godbolt上找到。

extern int my_function(void);

void f(void)
{
__asm volatile (
                "   cmp     r3,#0                   \n"                     
                "   b %[my_function]                \n" //Call function
                "   bx r14                          \n"
                : // no output
                : [my_function] "r" (my_function) // input
                : "r0" // clobber
            );

 }

输出

f():
    ldr r3, .L2
       cmp     r3,#0                   
   b r3                
   bx r14                          

    bx  lr
.L2:
    .word   my_function()

我们可以看到输出的几个问题。 r14为lrb r3将直接转移控件并返回f的调用者。似乎完全不需要cmp r3, #0(给出有限的问题背景)。

上面的示例回答了问题,它可以用于尾调用宏或其他用途,但显然需要一些工作。一个函数指针,如

int (*g)(void) = my_function;

也可作为参数&#39; my_function&#39; 加入GCC扩展汇编程序。

Another method只是使用&#39; C&#39;宏字符串连接。这是一个起始样本,

#define xstr(s) str(s)
#define str(s) #s
#define TAIL_CALL(func) __asm volatile(" b  " str(func) "\n")

对于大多数代码大小(跳转距离),分支将能够解析(4MB?)。如果使用函数指针方法,则没有问题。