ARM常用伪指令分析
ARM伪指令:为了方便程序员使用,编译器设计的指令,这个指令ARM核无法识别,需要编译器对它翻译成ARM核所识别的指令
-LDR R1,=0xabcdef分析
总结:
编译器在编译的时候,将ldr r1,=0xabcdef翻译成了ldr r1,[pc,#0x0004]这一条读内存的指令,根据PC的值加上偏移量算出0xabcdef这个数据在内存的地址,然后使用ldr指令读取这个地址的数据
-LDR R1,=label分析
总结:
LDR r1,=label指令表示将lable的地址写入r1,label的地址由指定的代码段运行地址来决定
编译器做法:
1.首先根据指定的代码段开始的地址,,算出label标签对应的地址值
2.然后将这个表示的地址值放在一个位置
3.生成内存访问指令,根据pc+固定偏移量,找到标签对应地址存放的位置
注意:
当代码编译结束的时候,标签表示的地址值(根据指定的代码段地址)已经编译死存放在程序文件中了
-LDR R1,label分析
(LDR R1,label表示读取label表示的地址对应的数据)
-ADR R1,label分析
(ADR R1,label指令表示根据当前的pc的值+/-偏移量,动态获取当前label所表示的内存地址)
-如何判别代码在实际内存中运行的地址?
ADR R0,_start可以知道,因为他是根据pc的值,动态获取
LDR R0,=_start无法知道,这条指令不论在哪里运行,R0的值都是固定的(取决于指定的链接地址)
ATPCS标准
ATPCS:ARM-Thumb的程序调用标准
规定了一些子程序间调用的基本规则,这些规则包括子程序调用过程中寄存器的使用规则,数据栈的使用规则,参数的使用规则,有了这些规则之后,单独编译的C语言程序就可以和汇编语言相互调用
-寄存器的使用规则
1.子程序间通过寄存器R0~R3来传递参数(可记为:a0~a3)
(注:被调用的子程序在返回前无需恢复寄存器R0~R3的内容)
2.子程序间通过寄存器R4~R11来保存局部变量(可记为:v1~v8)
(注:如果在子程序中使用了寄存器的值,则子程序进入时必须保存这些寄存器的值,在返回前必须回复这些寄存器的值,在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量)
3.寄存器R12用作过程调用中间临时寄存器,记作IP
(注:常用于子程序之间的链接代码段)
4.寄存器R13用作堆栈指针,记作SP
(注:在子程序中寄存器R13不能用作其他用途,寄存器SP在进入子程序时的值和退出子程序的值必须相等)
5.寄存器R14称为连接寄存器,记作LR,它用于保存子程序的返回地址
6.寄存器R15称为程序计数器,记作PC,它不能用作其他用途
-参数传递规则
函数间传递参数的时候,当参数个数不超过4个时,可以使用寄存器R0~R3来传递参数,当参数超过4个时,使用堆栈来传递参数
在传递参数时,将所有参数看作是存放在连续的内存单元的子数据,然后,依次将各字数据传递到寄存器R0,R1,R2和R3中,如果参数多于4个,则将剩余的字数据传递到数据栈中,入栈的顺序与参数传递顺序相反,即最后一个字数据先入栈
-子程序结果返回规则
1.结果为一个32位整数时,可以通过寄存器R0返回
2.结果为一个64位整数时,可以通过寄存器R0和R1返回
3.结果为一个浮点数时,可以通过浮点计算部件的寄存器f0、d0或s0来返回
4.结果为复合型浮点数(如复数)时,可以通过寄存器f0~fn或d0~dn来返回
-栈帧分析
栈桢概念:
1.栈桢是一块因函数运行而临时开辟的空间
2.每调用一个函数就会创建一个独立栈桢
3.栈桢中存放的是函数中的必要信息,如局部变量,函数传参,返回值等
4.当函数运行完毕栈桢会销毁
上图描述的是ARM的栈桢布局方式,main stack frame为调用函数的栈桢,func stack frame为当前函数(被调用者)的栈桢,栈底在高地址,栈向下增长
图中FP就是栈基址,它指向函数的栈桢起始地址;SP则是函数的栈指针,它指向栈顶的位置
ARM压栈的顺序依次为当前函数指针PC、返回指针LR、栈指针SP,传入参数个数及指针、本地变量和临时变量