我已经有一段时间没有编写ARM汇编了,对细节有点生疏。如果我从ARM调用C函数,我只需要担心保存R0-R3和LR寄存器,是吗?
如果C函数使用其他寄存器,那么是否需要负责将这些寄存器保存到堆栈并在必要时恢复它们?换句话说,编译器会为C函数生成代码来完成这些操作。
例如,如果我在汇编函数中使用R10寄存器,我不必将其值推送到堆栈或内存中,并在C调用后弹出/恢复它,是吗?
这是针对arm-eabi-gcc 4.3.0的。
我已经有一段时间没有编写ARM汇编了,对细节有点生疏。如果我从ARM调用C函数,我只需要担心保存R0-R3和LR寄存器,是吗?
如果C函数使用其他寄存器,那么是否需要负责将这些寄存器保存到堆栈并在必要时恢复它们?换句话说,编译器会为C函数生成代码来完成这些操作。
例如,如果我在汇编函数中使用R10寄存器,我不必将其值推送到堆栈或内存中,并在C调用后弹出/恢复它,是吗?
这是针对arm-eabi-gcc 4.3.0的。
这取决于您正在编译的平台的ABI。在Linux上,有两种ARM ABI;旧的和新的。据我所知,新的(EABI)实际上是ARM的AAPCS。完整的EABI定义目前在ARM的infocenter上。
来自AAPCS,§5.1.1:
被调用者保存的寄存器必须由被调用者保存(与调用者保存寄存器相反);因此,如果这是您正在使用的ABI,则在调用另一个函数之前无需保存r10(另一个函数负责保存它)。
编辑:你使用的编译器并不重要;特别是gcc可以配置为几种不同的ABI,并且它甚至可以在命令行上更改。查看生成的序言/尾声代码并没有太大用处,因为它适用于每个函数和编译器可以使用其他保存寄存器的方式(例如,在函数中间保存寄存器)。
r4-r11
寄存器,就需要保存和恢复它们,这就是它们被称为被调用者保存的原因。 - user339222来自AAPCS,§5.1.2.1 VFP寄存器使用约定:
原始帖子:
arm-to-c-calling-convention-neon-registers-to-save
SIMD和浮点寄存器指定了Neon和浮点寄存器。
对于64位ARM,A64(来自ARM 64位架构的过程调用标准),该指令集中有31个64位通用(整数)寄存器,分别标记为r0-r30。在64位上下文中,这些寄存器通常使用名称x0-x30引用;在32位上下文中,这些寄存器使用w0-w30指定。此外,堆栈指针寄存器SP可以与受限数量的指令一起使用。
前八个寄存器r0-r7 用于将参数值传递到子程序中,并从函数中返回结果值。它们也可以用于在例程内保存中间值(但一般仅在子程序调用之间使用)。
寄存器r16(IP0)和r17(IP1)可以由链接器用作例程和任何被调用的子例程之间的临时寄存器。它们还可以在例程内用于保存子例程调用之间的中间值。
寄存器r18的作用与平台相关。如果平台ABI需要一个专门用于承载程序间状态(例如线程上下文)的通用寄存器,则应将此寄存器用于该目的。如果平台ABI没有这样的要求,则应将r18用作额外的临时寄存器。平台ABI规范必须记录此寄存器的用法。
SIMD
ARM 64位架构还有另外32个寄存器,v0-v31,可用于SIMD和浮点运算。精确的寄存器名称会根据访问大小而改变。
注意:与AArch32不同,在AArch64中,SIMD和浮点寄存器的128位和64位视图不会重叠多个较窄视图的寄存器,因此q1、d1和s1都指向寄存器库中的同一项。
前八个寄存器v0-v7用于向子例程传递参数值并从函数返回结果值。它们也可以用于在例程内保存中间值(但一般仅在子例程调用之间使用)。
寄存器v8-v15必须由被调用者在子例程调用之间保留;剩余的寄存器(v0-v7,v16-v31)不需要被保留(或应由调用者保留)。此外,只需保留在v8-v15中存储的每个值的底部64位;保留更大值的责任由调用方承担。
void foo() {
asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}
使用命令行 arm-eabi-gcc-4.7 -O2 -S -o - foo.c
并为您的平台添加开关(例如,-mcpu=arm7tdmi
)。
该命令将在标准输出上打印生成的汇编代码。它可能看起来像这样:
foo:
stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
nop
ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
bx lr
-mcpu=arm7tdmi -mno-thumb-interwork
或使用-mcpu=cortex-m4 -mthumb
,我们可以获得略有不同的汇编代码,看起来像这样:foo:
stmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
nop
ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}
lr
作为 pc
弹出以更快地返回。你 r9
问题的答案在 APCS 中。在这个文档中,它被称为 静态基址,而 可重入代码与不可重入代码 部分是相关的。APCS 支持多种配置,但 gcc
通常是没有 堆栈限制 的 可重入。特别地,在某些 APCS 变体中,sb/r9
和 sl/r10
有专门的角色。在其他变体中,它们可能被用作被调用者保存的寄存器。 - artless noiselr
的值很重要,因为汇编包装器必须将lr
的值恢复到先前的值。因此,了解lr
是否被调用者保存并不是那么假设性的问题。 - Sven在Cortex M3体系结构中,函数调用和中断至少有一个区别。
如果发生中断,它将自动将R0-R3、R12、LR、PC推入堆栈,并在从IRQ返回时自动进行POP。如果您在IRQ例程中使用其他寄存器,则必须手动将它们推入/弹出堆栈。
我认为这种自动PUSH和POP不是为了函数调用(跳转指令)而设计的。如果约定规定R0-R3只能用作参数、结果或暂存寄存器,则在函数调用之前无需存储它们,因为在函数返回后不应再使用任何值。但是与中断相同,如果您在函数中使用其他CPU寄存器,则必须存储它们。
register
使用的不同名称。 - artless noise