r0-r3用于参数传递,r0用于返回值。r12用于Intra-procedure-call scratch register。这几个寄存器是caller-saved register。被调用者可以随意修改它们,在返回前不需要恢复其调用前的原值。
r4-r11, r13-r15均为callee-saved register,被调用者在返回前需要恢复它们。
程序包含程序状态(由寄存器表示)和它能访问的内存。
程序的内存通常分为下述5种:
代码
只读静态数据
可写静态数据
堆
栈
可写静态数据又可分为已初始化、初始化为0和未初始化数据。除了栈,其它数据不要求连续。一个程序至少需要有代码和栈,可以不需要其它内存。
栈是连续的内存区用于存储局部变量、传递参数到子程序(寄存器不足够存储所有的参数时)。AACPS要求栈实现为full-descending。栈顶位置存储在寄存器SP (r13).
维护栈的规则分为两部分:所有情况下都要遵守的约束;公共接口需要遵守的约束;
Universal stack constraints
必须小于等于stack-base,大于stack-limit。
栈必须对齐到4字节。
进程只能访问当前的栈,范围:[SP, stack-base-1]。如stack-base为4096,则从4095往下。SP以下的内存,栈还未生长到,不能访问。
stack constraints at a public interface
栈必须对齐到8
ARM和Thumb指令集均包含一个原始的子程序调用指令:BL。它将PC的下一个值保存到LR,将目标地址拷贝到PC。如果在Thumb状态执行BL,则LR的第0位置为1,在ARM状态则置为0。
链接器使用IP (r12)
BL不能address全部32位地址。链接器可以在calling routine和called subroutine之间插入一个veneer。ARM-Thumb inter-working和动态链接也需要veneer。任何veneer必须保存除IP (r12)之后的所有寄存器和状态标志位。
查看AAELF获取ARM的重定向信息。
不大于4字节的均存到r0。
A Composite Type larger than 4 bytes或者大小不能决定的参数。存到内存中,其地址作为一个额外的参数传递(r0)。函数调用期间,该内存可以修改。
参数传递通过r0-r3和栈,寄存器不够用时,才使用栈。
参数传递定义为一个2层概念模型
映射源语言参数到机器类型
编排机器类型产生参数列表
参数可能放入协处理器寄存器,这类参数叫做:Co-processor Register Candidate (CPRC)
被调用者可以修改从调用者接收参数的栈空间。
A.1 The Next Core Register Number (NCRN) is set to r0
NCRN指示下一个存储参数的寄存器。首先从r0开始。
A.2 Co-processor argument register initialization is performed.
A.3 The next stacked argument address (NSAA) is set to the current stack-pointer value (SP).
NSAA指示下一个放入栈的参数的地址。首先从SP开始。
A.4 If the subroutine is a function that returns a result in memory, then the address for the result is placed in r0, and the NCRN is set to r1.
如果返回值需要放入内存,则r0存储返回值的地址,NCRN设置为r1。
这里只列一些汇编代码代码中常用的情况:
- 如果参数是一个大小不能静态决定的复合类型,则参数被拷贝到内存,参数被替换为一个指向内存的指针。
- 如果参数是小于4字节的整数基本类型,其被零扩展或符号扩展位4字节。
- 如果参数是复合类型,其size round到最近的4的倍数。
- 如果参数是一个CPRC,且有足够的协处理器寄存器,则参数分配到协处理器。
- 如果参数是一个CPRC,且没有可用的协处理器参数,则NSAA向上增长到满足参数的对齐要求,将参数放入内存,NSAA在增大参数的大小。
- 如果参数要求双字(8字节)对齐,则NCRN round为下一个偶数寄存器号。
- 如果参数可以放入寄存器(r0-r3),则放入寄存器。NCRN往后移。只允许一个复合参数一部分放到寄存器,一部分放到栈。
- 寄存器不够放了,放入NSAA的位置,放入后NSAA增加参数的大小。
ARM的fp (frame pointer) 是可选的,每个函数在返回时,自己负责把栈指针恢复到父函数的栈顶。没有fp使得栈回溯变的异常复杂。使用GCC的-fomit-frame-pointer选项设置为不使用fp。
arm使用r11作为fp。
【1】Procedure Call Standard for the ARM ® Architecture. ARM IHI 0042D, current through ABI release 2.08. 2009