ASM内联调用C外部函数

问题描述:

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

的代码是:

__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' 

我们需要做什么?

+1

为什么你将'my_function'标记为直接参数?那么'may_function'将如何返回,因为你只能分支?下面这行永远不会被执行。 – Olaf

+0

创建一个调用'my_function'的非常短的C程序。然后用'-S'选项编译并查看编译器生成的程序集。 – user3386109

+0

此代码适用于Cortex-M4指令集,但不适用于Cortex-M0。 Olaf –

你想要BL指令。这是“分支和链接”。它跳转并将返回地址存储在r14中。

但是,你仍然有一个问题...当你做BL,它破坏需要r14。你还有更多的工作要做,即使在以下情况下:

stmfd sp!,{v1-v6,lr}    // preserve caller registers 
bl  %[my_function]    // call function 
ldmfd sp!,{v1-v6,pc} @std   // restore caller registers and return 

你需要更多的调查。您可能想要反汇编已编译的函数,并查看哪些包装器会绕过内联asm并进行相应调整。它可能为你做stmfd/ldmfd。尝试将r14标记为clobber。

你可能会更好,只有BL。没有恢复的BX可能会产生无限循环或不可预知的结果。我会离开它

+0

应该没有需要一个_call site_来保存被保存的寄存器,即使在完全未定义的疯狂的情况下,比如将内控转移到内联asm之外([asm goto](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm) .html#GotoLabels)例外)。在这种情况下,所有被调用者不需要保存的所有寄存器都需要保存,但是因为无论如何你都必须在clobber列表中声明所有这些寄存器,因此它有最低的工作机会,这是有效的已经照顾好了。 – Notlikethat

+0

@Notlikethat我回答了有关BL的主要内容。我从独立的代码中提取代码。我告诉OP反汇编和检查包装,它可能会为他做保存/恢复。最后,我说bl本身可能就足够了。矫枉过正,也许,OP会弄清楚。但是,对于任何更复杂的情况:(例如)包含func需要3个参数:调用fA(a1),fB(a2),fC(a3)。您需要先在v1-v3中保存a1-a3,然后在呼叫前执行a1 = v2,a1 = v3。那么stmfd/ldmfd就是v1-v3。也许比试图用限制来做到这一点更容易[如果可能] –

+0

这甚至没有解决实际问题;你刚才在被问到的仍然是破折号的代码行上添加了一个不恰当的样板函数序言和尾声,并建议OP应该在他们的实现基本破碎的自毁代码时使用稍微不同的指令来产生它们的语法错误理念。此外,即使是“有用的建议”也是非常有害的,因为不能保证r14在那个时候包含返回地址,即使这样做,将它弹回到PC中,在内联asm_中也是完全错误的。 – Notlikethat

在撰写以下内容后,我想起了ethernut tutorial。他有几乎相同的答案,

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

该OP将很好地阅读本教程;即使它是用于传统的ARM而不是'm0'。


您可以使用'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; 

也将作为参数“创建my_function”到GCC扩展汇编。

Another method只是使用'C'宏字符串连接。下面是一个起始样品,

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

对于大多数代码尺寸(跳跃距离),分公司将能够解决(4MB?)。如果你使用函数指针方法,那么没有问题。

+0

**注意!! **您不能将此机制用于复杂功能(使用堆栈框架)。 f()函数非常简单。如果你想返回到'f()',那么你需要使用'bl'并且应该在'clobber'列表中添加'lr'。然而,'b'可能对高级程序员有用;带'longjmp()'的条件'TAIL_CALL'可以实现一些原始的异常处理。 –