ARM常用指令(1)
加载与存储指令
基于基地址的寻址模式
- 基地址寻址
| 1 | ldr x0, [x1] | 
- 基地址偏移量寻址
| 1 | ldr x0, [x1, #4] | 
- 基地址扩张寻址
| 1 | ldr x0, [x1, x2] | 
UXTW表示从寄存器中提取32位数据,高位补0,无符号数。相同的extend还包括UXTB,UXTH。
SXTW表示从寄存器中提取32位数据,高位补0,有符号数。相同的extend还包括SXTB,SXTH。
变基寻址模式
- 前变基模式
| 1 | ldr x0, [x1, #4]! | 
- 后变基模式
| 1 | ldr x0, [x1], #4 | 
LDR伪指令
ldr既可以是普通的加载指令,也可以在第二个参数前加上“=”后,表示大范围加载地址的伪指令。注:无str伪指令
| 1 | ldr x0, =label // 此时x0中即为label的值 | 
‘[]’方括号表示从括号中参数的内存地址中读取或者存储数据,‘!’感叹号表示更新存放地址的寄存器,也即写回和更新寄存器
ldr和str的变种
| 指令 | |
|---|---|
| ldr | 普通数据加载指令,4字节与8字节数据,以W或者X寄存器区分 | 
| ldrsw | 有符号数据加载指令,word | 
| ldrb | 数据加载指令,byte | 
| ldrsb | 有符号数据加载指令,byte | 
| ldrh | 数据加载指令,half-word | 
| ldrsh | 有符号数据加载指令,half-word | 
| strb | 数据存储指令,byte | 
| strh | 数据存储指令,halfword | 
多字节加载与存储指令
在A32中通常使用LDM,STM指令实现多字节内存加载和存储,但是在A64架构中取消了LDM和STM指令,取而代之的是LDP(Load Pair)和STP(Store Pair)指令。LDP和STP支持基地址偏移量寻址、前变基模式和后变基模式三种寻址方式。
- 基地址偏移量寻址 - 1 
 2- ldp x0, x1, [x2] 
 stp x0, x1, [x2]
- 前变基寻址 - 1 
 2- ldp x0, x1, [x2, #4]! 
 stp x0, x1, [x2]
- 后编辑寻址 - 1 
 2- ldp x0, x1, [x2], #4 
 stp x0, x1, [x2], #4
| 1 | // 以上常用指令简易测试汇编代码如下 | 
入栈与出栈
在程序中栈通常有以下用途:
- 临时存储数据,如临时变量等
- 传递参数。ArmV8平台,如果函数参数不超过8个,那么将使用x0~x7通用寄存器传递参数,当传递参数超过8个时,就需要用到栈了。
一般情况,栈是从高地址向低地址生长的数据结构,起始地址称为栈底,最后一个入栈元素地址称之为栈顶,指向栈顶的指针称之为栈指针,SP(Stack Point)。
A32提供了push和pop指令进行入栈出栈操作,在A64指令集中移除了push和pop,使用ldp和stp取代。
| 1 | .arch armv8-a | 
MOV指令
mov指令通常用于寄存器之间的搬移和立即数搬移。
而能搬运的立即数只有以下两种:
- 16位立即数
- 16位立即数左移16位、32位或者48位的立即数
此时mov指令等同于movz指令
| 1 | movz x0, 0x12bc, LSL #16 | 
mov指令还可以用来搬运bitmask,此时等同orr指令
| 1 | orr x0, XZR, #0xffff0000ffff | 
可以通过反汇编验证以上结论:
| 1 | main.s: | 
通常一个cpp文件要变成熟悉的可执行文件a.out,需要经过以下四个步骤

调用aarch64-linux-android-g++对main.s进行汇编过程:
| 1 | aarch64-linux-android-g++ -c main.s -o main.o | 
调用aarch64-linux-android-objdump -s -d -M no-aliases test.o对main.o进行反汇编
| 1 | aarch64-linux-android-objdump -s -d -M no-aliases main.o | 
可以看到mov指令变成了movz和orr指令