[关闭]
@oro-oro 2015-10-10T14:58:01.000000Z 字数 3885 阅读 7311

三、ARM 简介

AndroidARM


ARM处理器是精简指令集计算 Reduced Instruction Set Computing (RISC)的一个实例。

1. 寻址方式

1. 立即寻址

  1. mov r0, #1234

相当于:r0=#1234。#开头,表示16进制时,以0x开头,如#0x1f。

2. 寄存器寻址

  1. mov r0, r1

执行后,r0 = r1。

NOP 操作通常为mov r0, r0,对应的HEX为00 00 a0 e1

3. 寄存器移位寻址

寄存器移位寻址支持以下5种移位操作:

  1. mov r0, r1, lsl #2

相当于:r0 = r1<<2 = r1*4

4. 寄存器间接寻址

  1. ldr r0, [r1] // 取值

相当于:r0 = *r1

5. 基址寻址

  1. ldr r0, [r1, #-4]

相当于:r0 = *(r1 - 4)

6. 多寄存器寻址

  1. lmdia r0, {r1, r2, r3, r4}

LDM 是数据加载指令,指令的后缀IA表示,每次执行完成加载操作后,R0寄存器的值自增1个字。
R1=[R0], R2=[R0+#4], R3=[R0+#8], R4=[R0+#12]

字表示一个32位的数值。

7. 堆栈寻址

它需要特定的指令完成:
LMDFA/STMFA, LDMEA/STMEA, LDMFD/SDMFD, LDMED/STMED。

LMD/STM 表示多寄存器寻址,一次可以传送多个寄存器值。
FA/EA/FD/ED ..参考指令集。

  1. stmfd sp!, {r1-r7, lr} @将 r1~r7lr 压栈。多用于保存子程序现场。
  2. ldmfd sp!, {r1~r7, lr} @将 r1~r7, lr 出栈,放入 r1~r7, lr。多用于恢复子程序现场。

8. 块拷贝寻址

可实现连续地址数据从存储器的某一位置拷贝至另一位置。
LDMIA/STMIA, LDMDA/STMDA, LDMIB/STMIB, LDMDB/STMDB。

LDM/SDM 表示多寄存器寻址,一次可以传送多个寄存器值。
IA, DA, IB, DB ..参考指令集。

  1. ldmia r0!, {r1-r3} @ r0指向的区域的值取出来,放到r1-r3中。
  2. stmia r0!, {r1-r3} @ r1-r3的值取出来,放入r0指向的区域。

9. 相对寻址

相对寻址以PC的当前值为基址,与偏移值相加,得到最终的地址。

  1. bl .lc0
  2. ...
  3. .lc0:
  4. ...

bl 直接跳到 .lc0 处。

2. 寄存器(Registers)

寄存器详细可参考 AAPCS §5.1.1 Core registers

ARM 汇编有16个寄存器:
- r0-r3 主要用来传递函数调用第1到第4个参数(a0-a3),更多的参数须通过栈来传递。
- r0-r1 也作为结果寄存器,保存函数返回结果;被调用的子程序在返回前无须恢复这些寄存器的内容。
- r4-r9 为被调保存(callee-save)寄存器,一般保存内部局部变量(local variables)。
- r7 大部分情况用来保存系统调用号(syscall number)。
- r9 某些变体可能当作特殊寄存器。
- r10(SL)被调保存寄存器,Stack Limit。
- r11(FP)被调保存寄存器, 帧指针(Flame Pointer)。
- r12(IP)特殊寄存器,栈寄存器(Intra Procedure)。
- r13(SP)特殊寄存器,栈指针,类似x86_64中的RSP。
- r14(LR)特殊寄存器。Link Register.
- r15(PC)特殊寄存器。Program Counter (like RIP in x86_64 & EIP in x86).

被调保存寄存器(callee-save register)是指,如果这个寄存器被调用/使用之前,需要被保存。

2.1 特殊寄存器

ARM堆栈结构是从高向低压栈的。
因为处理器是32位的ARM,所以每压一次栈,SP就会移动4个字节(32位),也就是sp = sp-4。

2.2 SP、FP 详解

SP 和 FP 都是跟本地数据相关的寄存器。一个是 "Where local data is",另外一个是 "Where the last local data is"。

栈帧(Stack Frame)就是一个函数所在的栈的一部分,所有函数的栈帧串起来就组成了一个完整的栈。
栈帧的两个边界分别由 FP 和 SP 来限定,它们2个指向的是当前函数的栈帧。

考虑 main 函数调用fun1函数的情形,下图是它们使用栈。
观察 func1 的栈帧,它的 SP 和 FP 之间指向的栈帧就是 main 函数的栈帧。
main 函数产生调用时,PC、LR、SP、FP 会在第一时间压栈。

AndroidARM_3-1_FP

2.3 PC与相对取址

ARM 不能像单片机那样,想取某个标签地址,就可以 mov r1,#标签。
因为ARM立即数寻址有限制,最大是4096,再大就只能相对寻址,显然所有的指针都会超过限制,只能间接寻址,所以需要用另一种方式直接算出寻址位置的地址和全局变量位置的相对地址。

ARM7和ARM9都是3级流水线,取指,译指,执行时同时执行的:
1. Fetch(从存储器装载一条指令)
2. Decode(识别将要被执行的指令)
3. Execute(处理指令并将结果写回寄存器)

而R15(PC)总是指向“正在取指”指令,而不是指向“正在执行”的指令或正在“译码”的指令,那么CPU正在译指的指令地址是PC-4(当ARM状态时,每条指令为4字节),CPU正在执行的指令地址是PC-8,也就是说PC所指向的地址和现在所执行的指令地址相差8,即:PC实际值=当前程序执行位置+8。

也就是说:
PC, 总是指向当前正在被取指的指令的地址,
PC-4,总是指向当前正在被译指的指令的地址,
PC-8,总是指向当前的那条指令,即一般说的,正在被执行的指令的地址。
指令的Execute执行阶段,如果用到PC的值,那么PC那一时刻,就是PC=PC+8。

其他细节具体可参考 3.4. 为何ARM7中PC=PC+8

3. 指令集

3.1 分支(Branching)

当程序需要一些循环、过程(procedures)和函数的时候,会用到分支指令。
实现程序跳转的方法,还可以直接给PC寄存器直接赋值实现跳转。

3.3 数据处理(Data Processing)

其他更加详细信息,可参考:
Whirlwind Tour of ARM Assembly
ARM Architecture Reference Manual

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注