在arm gcc中控制函数的prelog和epilog

时间:2016-06-01 08:07:50

标签: c gcc assembly arm

我注意到有时当我编译C代码时,汇编代码有时会在方法1中生成:

STR R11, [SP, #-4]!
ADD R11, SP, #0
SUB SP, SP, #4

有时在方法2中:

STMFD SP!, {R11, LR}
ADD R11, SP, #4
SUB SP, SP, #4

第一种方法和第二种方法的区别在于第二种方法将LR保存到堆栈中。

现在我遇到一个问题,我的函数,就像第一个方法一样,使用链接寄存器(BL)调用另一个函数,并且因为我的函数首先没有保存LR,所以它会导致一个严重的问题。 如果我可以告诉编译器使用第二种方法它可以解决我的问题。

这可能与函数使用内联汇编调用内部函数这一事实有关,因此"不能识别"有一个调用另一个函数,并没有看到保存LR没有意义。调用内联汇编是有义务的,因为被调用的函数将SP的值作为参数。

这是一个非常有吸引力的情况,希望有人可以帮我解决这个问题。 谢谢!

3 个答案:

答案 0 :(得分:1)

lr标记为已被破坏以使编译器保存它:

extern void foo(void);
extern void bar(void)
{
    asm ( "bl foo" : : : "lr" );
}

生成以下代码:

bar:
    str lr, [sp, #-4]!
    bl foo
    ldr lr, [sp], #4
    bx  lr

有关详细信息,请参阅the documentation。请记住将您使用的所有其他寄存器标记为已破坏。否则编译器可能会在其中一个中放入一个变量。您可能还需要将所有调用者保存的寄存器标记为已破坏。

答案 1 :(得分:1)

最简单/直接的答案是将lr添加到clobber中,但您可能需要添加更多内容!

<强> WHY ??

编译器根据函数的类型生成不同的序言/结尾。它们可以是通用函数,叶函数(不调用其他函数)或尾调用。您没有显示任何代码,因此很难了解您的具体情况。但是,如果你没有记录你的内联汇编程序正在调用另一个函数,那么gcc认为它不是 leaf 函数。

来自gcc assembler documentation

  

在不使用输入/输出操作数的情况下从C程序访问数据(例如直接使用汇编程序模板中的全局符号)可能无法按预期工作。类似地,直接从汇编程序模板调用函数需要详细了解目标汇编程序和ABI。

出于这个原因,我认为最安全的做法是将r0 - r3lr添加到clobbers中。也可能需要氖/浮点寄存器。应该让编译器意识到这些可以改变'内联asm'语句。

如果汇编语句后面没有代码,并且假设没有使用“帧指针”或其他寄存器,则可以使用尾调用。即,仅使用bl inline_asm_target替换b inline_asm_target,其中inline_asm_target函数仍然执行bx lr(或者适用于正在使用的编译器选项/ ABI的任何内容)。

相关:
  - ARM link register and frame pointer
  - ARM to C calling convention registers to save

答案 2 :(得分:0)

有几种解决方案可以保存LR。

1)您可以手动保存,因为您编写内联汇编,您可以在调用函数之前保存LR并在调用后恢复它。

2)您可以使用以下C代码声明一个将放入LR的虚拟变量:

register int foo asm ("lr");

3)使用clobbers in your inline assembly

请注意,最佳解决方案可能是第三种。