ARM学习(17)ARM函数调用规则
笔者来聊聊ARM的函数的调用规则
1、ARM函数调用规则介绍
首先介绍几个术语,
- AAPCS:Procedure Call Standard for the ARM Architecture
- APCS:ARM Procedure Call Standard
- TPCS:Thumb Procedure Call Standard
- ATPCS:ARM-Thumb Procedure Call Standard
- ABI:Application Binary Interface
- EABI:Embedded Application Binary Interface
前三个基本就是ARM 架构的程序调用标准(包括ARM指令以及thumb指令),最后两个是程序二进制接口,描述是汇编下面寄存器名使用、函数调用、压栈、参数传递以及程序返回等一系列接口标准。
比如x86与arm架构的ABI 就不兼容,寄存器都不一样,肯定就是无法互相使用,包括最近比较火热的RISC-V架构。
2、ARM32/64 函数调用规则
ARM架构下面分为32位以及64位的程序,其标准有些不同以及扩展,可分开来讲。
2.1、ARM
- 机器寄存器
对于ARM以及thumb指令集来说,总有16个寄存器使用,不管大小写,在汇编语言里面都是可以识别的,一般来说大写的有特殊用途,详情见下文。
- R0-R3 作为函数参数传递以及返回值使用,也可以在程序中保存立即数。
- R12 (IP)链接器使用的暂存器
- R9 平台寄存器,ARM不推荐使用,避免平台依赖
- R4-R8,R10,R11 暂存器
- R11 作为栈指针,指向程序调用中开辟空间的栈底,可用来栈回溯,具体可以参考ARMARM学习(1) 寄存器的理解 ===》FP、SP、LR寄存器。
- R13 SP 栈指针
- R14 LR 链接寄存器
- R15 PC 程序计数器
- CPSR 状态寄存器 详情介绍见ARM学习(2) 寄存器的理解 ===》通用寄存器及状态寄存器。
- cp15 协处理 监控通用寄存器的使用
- 程序处理、内存以及栈
一个程序的内存通常会划分为几部分:Code、read-only static data、writeable static data、heap and stack,Code段里面会包括read-only static data,DATA段会包括初始化好的全局变量,BSS包括堆栈以及未初始化和初始化为0的全局变量
- 程序处理、内存以及栈
- stack 临时存储变量以及传递额外的参数(程序调用时寄存器数量不足),
- 栈有范围限制,Limit < SP < Base
- 其次必须4 byte对齐,
- 程序必须访问栈的部分范围内区域(SP ,base-1),不能访问之外的区域,
- 子程序调用
- BL指令会跳到目标程序,并且修改LR为当前PC的按顺序下一条指令地址,指的是没跳转之前的PC的下一条。
- 需要注意修改LR最低位表示当前为什么状态,,0:是arm状态,无需修改(因为地址都是word 2Byte对齐的),1:是thumb状态,需要修改,
可以看到R14以及PC的值都是奇数,代表是thumb指令。
R15(PC):0x0801247E 位于 DMA_GetFlagStatus,
R14:0x08014ECB 位于 UART1_SentMsgL,保存了其返回的地址。- 返回值
- R0:返回值是D-Word(32 bit),或者是单精度浮点型float,
- R0-R1:返回值是Q-Word(64 bit),或者双精度浮点型double
-
R0-R3:返回值是128bit的向量
调用GetSysTime之后,r0则获取到值,然后r1计算好之后,则cmp进行比较r0,r1.- 参数传递
- R0-R3 和栈进行传递
- 参数少的函数,只用寄存器就可以了,减少访问内存带来的负载
-
优先寄存器,然后栈,寄存器从小到大,栈地址从小到大。
1、函数声明:void OLED_DrawBMP_test(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[],unsigned char state1,unsigned char state2);
2、共用7个参数:x0,y0,x1,y1,BMP[],state1,state2
3、前四个参数都分别存储在r0-r3,
4、后面三个参数存储在栈中,strd r7,r6,[r13] 以及str r6,[r13,#0x8] ,前面指令存储r7 r6 两个参数,后面地址偏移8之后,又存储一个参数,总计三个参数,存储顺序依次都是参数从左向右去存储。
5、不符合cdecl、stdcall、__fastcall这三种调用标准。
- 内部联合工作(ARM与Thumb指令)
-
子程序调用和程序返回支持两张状态切换。bx或者blx都可以切换状态。
2.2、ARM64
- 机器寄存器
- 通用寄存器
- 31个通用寄存器都适用于A64指令集,记为r0-r31,还有一个栈指针,SP,有限指令集才可以使用
- 64位的上下文场景中:符号记为为X0-X31,32位的上下文场景中记为W0-W31
- 寄存器名字不区分大小写,都可以识别,这个就是二进制接口,可以被汇编器识别
- r0-r7 用来传递参数以返回值,也可以用做函数调用的立即数保存。
- r16-r17 程序内部调用暂存器,与PLTcode相关。
- r19-r28 被调用者需要保存的寄存器
- r29:FP指针,r30:LR。具体可以参考ARMARM学习(1) 寄存器的理解 ===》FP、SP、LR寄存器。
- 浮点寄存器:v0-v31
- v0-v7:与上面一致,r0-r7
- v8-v15:被调用者保存
- v16-v31:调用者保存
- 程序、内存和栈
- 与32位基本一致。
- 其次必须16 byte(quad-word)对齐,
- 子程序调用
- BL以及BLR,BLR:跳转的地址来源于特殊寄存器
- A64分支指令集并不能访问到程序空间的每个地址,因此在程序调用或者被调用的时候,对于链接器来说插入一个虚式,其可能在动态链接的时候需要,IP0或者IP1,可能被一个程序改变,然后该指令会被插入到支持长分支重定位的任意一个指令当中。
- 参数传递
- r0-r7传递参数,更多参数的话会使用栈,
- 可变的参数传递,调用者知道全部的参数列表,但是被调用者仅仅知道最少的参数,要需要知道所有参数个数,必须要通过其他参数值来进行确定。
- 结果返回
- 内部联合工作
- 32位的调用规则和64位的调用规则不能在一个 程序中使用,但是在AARCH64架构下,异常状态(32位和64位)切换操作会发生,
参考文档
[1、ARM Procedure Call Standard]()
[2、Procedure Call Standard for the ARM 64-bit Architecture (AArch64)]()
版权声明:
作者:ZhangYixi
链接:http://zyixi.xyz/arm%e5%ad%a6%e4%b9%a0%ef%bc%8817%ef%bc%89arm%e5%87%bd%e6%95%b0%e8%b0%83%e7%94%a8%e8%a7%84%e5%88%99/
来源:一西站点
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论