第六章 ATPCS 介绍
为了使单独编译的C语言和汇编程序之间能够相互调用,必须为子程序间的调用规定一定的规则。ATPCS就是ARM和Thumb程序中子程序调用的基本规则
6.1 ATPCS 概述
基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则、参数的传递规则。根据需要分为
支持数据栈限制检查的ATPCS
支持只读段位制无关(ROPI)的 ATPCS
支持可读写段位置无关(RWPI)的 ATPCS
支持ARM程序和Thumb程序混合使用的ATPCS
处理浮点运算的ATPCS
汇编语言子程序必须满足:
在子程序编写时必须遵守相应的ATPCS规则
数据栈的使用要遵守相应的ATPCS规则
在汇编编译器中使用-atpcs选项
6.2 基本ATPCS
规定了在子程序调用时的一些基本规则,包括:
各寄存器的使用规则及其相应的名称
数据栈的使用规则
参数传递的规则
相对于其他ATPCS,满足基本ATPCS的程序的执行速度更快,所占用的内存更少。但是它不能提供以下的支持:
ARM程序和Thumb程序相互调用
数据以及代码的位置无关的支持
子程序的可重入性
数据栈检查支持
6.2.1 寄存器的使用规则
子程序通过寄存器R0~R3来传递参数。
在子程序中,使用寄存器R4~R11来保存局部变量
寄存器R12用作子程序间scratch寄存器,记作ip
寄存器R13用作数据栈指针,记作sp
寄存器R14称为连接寄存器,记作lr
寄存器R15是程序计数器,记作pc
6.2.2 数据栈使用规则
栈指针的位置,指向栈顶位置称为FULL栈;指向与栈顶元素相邻的一个可用数据单元时,称为EMPTY栈
栈的增长方向,当向内存地址减小的方向增长时,称为DESCENDING栈;当向内存地址增加的方向增长时,称为ASCENDING栈。
4种数据栈:
FD 满递减
ED 空递减
FA 满递增
EA 空递增
6.2.3 参数传递规则
-
参数个数固定的子程序
如果系统包含浮点运算的硬件部件,浮点运算将按照下面的规则传递:
各个浮点参数按顺序处理
为每个浮点参数分配FP寄存器
分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器。
第一个整数参数,通过寄存器R0~R3来传递。其他参数通过数据栈传递。
-
参数个数可变的子程序
当不超过4个时,可以使用寄存器R0~R3来传递参数;超过4个时,还可以使用数据栈来传递参数。
-
子程序结果返回规则:
结果为32位整数时,可以通过寄存器R0返回
结果为64位整数时,可以通过寄存器R0和R1返回,一次类推
结果为一个浮点数时,可以通过浮点运算部件的寄存器f0、d0或者s0来返回
结果为复合型的浮点数时,可以通过寄存器f0~fN或者d0~dN来返回
对于位数更多的结果,需要通过内存来传递。
6.3 几种特定的ATPCS
6.3.1 支持数据栈限制检查的ATPCS
-
基本原理
在进行数据栈检查时,使用寄存器R10作为数据栈限制指针,这时寄存器R10又记作sl.用户在程序中不能控制该寄存器。
这种ATPCS要满足以下规则:
- 在已经占用的栈的最低低至和sl之间必须有256字节的空间。
- 用户在程序中不能修改sl的值
- 数据栈栈指针sp的值必须不小于sl的值
支持的相关编译/汇编选项:
- /swst(software stack limit checking)指示编译器生成的代码遵守支持数据栈限制检查的ATPCS
- /noswst 指示编译器生成的代码不支持数据栈限制检查功能
- /swsna(software stack limit checking applicable)如果汇编程序对于是否进行数据栈检查无所谓,而与该汇编程序连接的其他程序指定了选项/swst或选项/noswst,这时该汇编程序使用选项/swstna
-
编写汇编程序
叶子子程序是指不调用别的程序的子程序
编写支持数据栈限制检查的APTCS的汇编程序
数据栈小于256字节的叶子子程序不需要进行数据栈检查
-
数据栈小于256字节的非叶子子程序使用下面代码段
ARM程序
SUB sp, sp, #size ;#size为sp和sl CMP sp, sl BLLO __ARM_stack_overflow
Thumb程序
ADD sp, #-size CMP sp, sl BLO __Thumb_stack_overflow
-
数据栈大于256字节的子程序
为了保证sp的值不小于数据栈可用的内存单元最小的地址值,需引入相应的寄存器
ARM程序
SUB ip, sp, #size CMP ip, sl BLLO __ARM_stack_overflow
Thumb程序
LDR wr, #-size ADD wr, sp CMP wr, sl BLO __Thumb_stack_overflow
6.3.2 支持只读段位置无关(ROPI)的APTCS
-
支持只读段位置无关(ROPI)的ATPCS的应用场合
可以避免必须将程序放在特定的位置
应用场合:
- 程序在运行期间动态加载到内存中
- 程序在不同的场合,与不同的程序组合后加载到内存中
- 在运行期间映射到不同的地址。
-
遵守支持只读段位置无关(ROPI)的ATPCS的程序设计
- 当ROPI段中的代码引用同一个ROPI段中的符号时,必须是基于PC的
- 当ROPI段中的代码引用另一个ROPI段中的符号时,必须是基于PC的,并且两个ROPI段的位置关系必须固定
- 其他被ROPI段中的代码引用的必须是绝对地址,或是基于sb的可写数据
- ROPI段移动后,对ROPI中符号的引用要作相应的调整
6.3.3 支持可读写段位置无关(RWPI)的ATPCS
如果一个程序中所有的可读可写都是位置无关的,则称程序遵守支持可读可写段位置无关(RWPI)的APTCS。使用支持可读写段位置无关(RWPI)的ATPCS可以避免必须将程序存放到特定的位置。这时R9通常作为静态基址寄存器,记作sb。可重入的子程序可以在内存中同时有多个实例。各个实例拥有独立的可读写段。在生成一个新的实例时,sb指向该实例的可读写段。RWPI段中的符号的计算方法:连接器首先计算出该符号相对于RWPI段中某特定位置的偏移量,通常该特定位置选为RWPI段的第一个字节处,在程序运行时,将该偏移量加到sb上即可生成该符号的地址。
6.3.4 支持ARM程序和Thumb程序混合使用的ATPCS
在编译或汇编时,使用/interwork告诉编译器(或汇编器)生成的目标代码遵守支持ARM程序和Thumb程序混合使用的ATPCS,用在以下场合:
- 程序中存在ARM程序调用Thumb程序的情况
- 程序中存在Thumb程序调用ARM程序的情况
- 需要连接器来进行ARM状态和Thumb状态切换的情况
- 在下述情况下,使用选项/nointerwork
- 程序中不包含Thumb程序
- 用户自己进行ARM状态和Thumb状态切换
6.3.5 处理浮点运算的ATPCS
ATPCS支持VFP和FPA体系两种不同的浮点硬件体系和指令集。两种体系对应的代码不兼容。