ARM64汇编
寄存器
通用寄存器
- 64bit: x0 - x28
29个 通用寄存器,8位(bit)=1字节(Byte),每个寄存器64bit能存放8个字节
注:
1位表示1个2进制位
一位十六进制位=4个二进制位
32位=8位16进制 0x00000000
64位=16个16进制位0x0000000000000000
一个字节包含8个二进制位,一个字节可以由2个十六进制表示
- 32bit: w0 - w28 (属于x0 - x28的低32bit)
- x0 - x7 通常拿来存放函数的参数,更多的参数使用堆栈来传递
- x0 通常拿来存放函数的返回值
程序计数器
- pc (Program Counter)
用来记录CUP当前执行的是哪一条指令
存储着当前CPU正在执行的指令的地址
类似8086汇编的ip寄存器
链接寄存器
- sp (Stack Pointer) 栈顶指针
- fp (Frame Pointer) 也就是x29 栈底指针
堆栈指针
- lr (Link Register) 也就是x30
存储着函数的返回地址
程序状态寄存器
- cpsr (Current Program Status Register)
- spsr (Saved Program Status Register) , 异常状态下使用
cpsr
31 | 30 | 29 | 28 | 27 ~ 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
N | Z | C | V | 保留 | I | F | T | M4 | M3 | M2 | M1 | M0 |
N: 带符号的数进行运算时,N=1表示运行结果为负;N=0表示运行结果为正或0
Z:Z=1表示运算结果为0,Z=0表示运行结果非零
C: 可以有4中方法设置C位的值:
- 加法运算(包括比较指令CMN): 当运算结果产生了进位时(无符号数溢出),C=1,否则C=0。
- 减法运算(包括比较指令CMP):当运算符产生了错位(无符号数溢出),C=0,否则C=1。
- 对于包含移位操作的非加/减运算指令,C为移出值的最后移位。
- 对于其他的非加/减运算指令,C的值通常不改变。
V:可以有2种方法设置V的值:
- 对于加/减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出。
- 对于其他的非加/减运算指令,V的值通常不改变。
0-7: 控制位,软件开发基本用不到
指令
ret
- 函数返回
- 将
lr(x30)
的值赋值给pc寄存器
mov
MOV {条件} {s} 目的寄存器, 源操作数
MOV指令可完成从另一个寄存器,被移位的寄存器或将一个立即数加载到目的寄存器。其实S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
1
2
3
4;指令示例
MOV x1, x0 ; 将寄存器x0的值传送到寄存器x1
MOV x2, x1 ; 将寄存器x1的值传送到x2
MOV x1, x0, LSL#3 ; 将寄存器x0的值左移3位后传送到x1[暂不确定是否在arm汇编可用]add
ADD {条件} {s} 目的寄存器, 操作数1, 操作数2
ADD指令用于把两个操作数相加,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
1
2
3
4;指令示例
ADD x0, x1, x2 ; x0 = x1 + x2
ADD x0, x1, #0x11 ; x0 = x1 + 0x11
ADD x0, x2, x3, LSL#1 ; x0 = x2 + (x3 << 1) [暂不确定是否在arm汇编可用]sub
SUB {条件} {s} 目的寄存器, 操作数1, 操作数2
ADD指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
1
2
3
4;指令示例
SUB x0, x1, x2 ; x0 = x1 - x2
SUB x0, x1, #0x11 ; x0 = x1 - 0x11
SUB x0, x2, x3, LSL#1 ; x0 = x2 - (x3 << 1)[暂不确定是否在arm汇编可用]cmp
CMP {条件} 操作数1, 操作数2
CMP指令用于把一个寄存器的内容和另一个寄存器的内容进行比较,同时更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位标识的是操作数1余操作数2的关系(大、小、相等),例如,当操作数1大与操作数2,则此后的有GT后缀的指令将可以执行。关于后缀查看下面的条件域。
1
2
3;指令示例
CMP x1, x0 ; 将寄存器x1与寄存器x0的值相减,并根据结果设置CPSR的标志位
ADD x1, #100 ; 将寄存器x1与立即数100的相减,并根据结果设置CPSR的标志位b
B{条件} 目标地址
B指令是最简单的跳转指令。一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算。它是24位有符号数,左移两位后有符号扩展为32位,标识的有效偏移为26位(前后32MB的地址空间)。
1
2
3
4
5;指令示例
B Label ; 程序无条件跳转到标号Label处执行
CMP x1, #0 ; 比较x1和0的大小,将结果放入CPSR寄存器中
BEQ Label ; B+条件(见下面条件域,中间无空格) 当CPSR寄存器中的z条件码置位时,程序跳转到标号Label处执行cmp
CMP {条件} 操作数1, 操作数2
CMP指令用于把一个寄存器的内容和另一个寄存器的内容进行比较,同事更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位标识的是操作数1余操作数2的关系(大、小、相等),例如,当操作数1大与操作数2,则此后的有GT后缀的指令将可以执行。关于后缀查看下面的条件域。
1
2
3;指令示例
CMP x1, x0 ; 将寄存器x1与寄存器x0的值相减,并根据结果设置CPSR的标志位
ADD x1, #100 ; 将寄存器x1与立即数100的相减,并根据结果设置CPSR的标志位bl (带返回的跳转,类似函数调用)
BL{条件} 目标地址
BL指令是另一个跳转指令,但跳转之前,会在寄存器x14中保存PC的当前内容,因此,可以通过将x14的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。改指令是实现之程序调用的一个基本当常用的手段。
1
2
3
4
5
6
7
8
9
10;指令示例
_test:
...
bl somecode ; 程序无条件跳转到标号Label处执行时,同事将当前的PC值保存到x14中
...
ret
comecode:
...
ret ; 程序返回bl之后的代码继续执行orr 逻辑或运算
1 | orr w8, wzr, #0x3 ; 将wzr 和#0x3或运算,结果#0x3赋值给w8 |
条件域
- EQ: equal 相等
- NE: not equal 不相等
- GT: great than大于
- GE: great equal 大于等于
- LT: less than 小于
- LE: less equal 小于等于
内存操作
load 从内存读取数据
- ldr、ldur(ldr 通常是正偏移,ldur通常是负偏移)
- ldp (p是pair,一对的简称) : 从内存读取数据,放到一对寄存器中
1
2ldr x0, [x1] ; 将x1的值作为地址的内存中的数据传送到x0中
ldp w0, w1, [x1, #0x10] ; 取x1+#0x10的地址对应的内存数据,前4个字节存储到w0,后4个自己存储到w1store 往内存中写入数据 (只能从寄存器写入,不能直接写入立即数)
- str、stur
- stp
1
2str w0, [x1] ; 将w0的数据,存入x1中地址对应的内存
stp w0, w1 [x1] ; 将w0和w1的数据,存入x1中地址对应的内存- 零寄存器 里面存储的值是0
- wzr (32bit)
- xzr (64bit)
寻址方式
立即数寻址
立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式叫做立即寻址。例:
1 | ADD x0, x0, #1 ; x0 <- x0 + 1 |
寄存器寻址
寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式
1 | ADD x0, x1, x2 ; x0 <- x1 + x2 |
寄存器间接寻址
寄存器间接寻址就是以寄存器中的值为操作数的地址,而操作数本身放在存储器中
1 | ADD x0, x1, [x2] ; x0 <- x1 + x2存储的地址指向的内存中的数据 |
基地址变址寻址
将寄存器的内容与指令中给出的偏移量相加,从而得到一个操作数的有效地址。
1 | LDR x0, [x1, #4] ; x0 <- [x1+4] |
多寄存器寻址
采用多寄存器寻址,一条指令可以完成多个寄存器值的传送。这种寻址方式可以用一条指令完成传送最多16个通用寄存器的值。
1 | LDMIA x0, {x1, x2, x3, x4} ; x1 <- [x0] |
相对寻址
与基址变址寻址方式相似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号为偏移量,将两者相加之和得到操作数的有效地址。以下程序段完成子程序的调用和返回,跳转指令BL采用了相对寻址方式:
1 | BL NEXT ; 跳转到子程序NEXT处执行 |
堆栈寻址
堆栈寻址堆栈是一种数据结构,按先进后出( First hi Last Out , FILO )的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。当堆栈指针指向址后压入堆栈的数据时,称为满堆栈( Ful ! StaCk ) ,而当堆栈指针指向卜一个将要放入数据的空位置时,称为空堆栈( EmPty Stack )。同时,根据堆栈的生成方式,又可以分为递增堆栈( Ascending Stack )和递减堆栈( Decending Stack ) ,当堆栈由低地址向高地址生成 l 讨,称为递增堆栈,当堆栈由高地址向低地址生成 11 - 4 ,称为递减堆栈。这样就有四种类型的堆栈工作方式, ARM 微处理器支持这四种类型的堆栈工作方式,即:
满递增堆栈:堆栈指针指向最后压入的数据,且由低地为卜向高地为 L 生成。
满递减堆栈:堆栈指针指向最后压入的数据,且由高地址向低地址生成。
空递增堆栈:堆栈指针指向卜一个将要放入数据的空位置,且由低地址向高地址生成。
空递减堆栈:堆栈指针指向卜一个将要放入数据的空位置,且由高地址向低地址生成。
在Xcode中编写汇编代码
内嵌
1 | - (void)someFunction() { |
外部建立
新建xxx.s汇编文件
1 | ; 注释 |
新建xxx.h头文件
1 | void test(); |
函数的堆栈
函数的类型
- 叶子函数 (内部不再调用其他函数)
1
2
3
4
5; 叶子函数汇编代码
sub sp, sp, #16 ; 栈顶指针减16个字节,开辟16个字节的栈空间
...
add sp, sp, #16 ; 栈顶指针加16个字节,释放空间,恢复栈平衡- 非叶子函数 (内部会调用其他函数)
1
2
3
4
5
6
7
8
9
10
11; 非叶子函数汇编代码
sub sp, sp #32 ; 栈顶指针-32个字节,开辟栈空间
stp x29, x30, [sp, #16] ; 将x29,x30(栈底指针和堆栈指针)存储到sp栈顶指针+16的位置
add x29, sp, #16 ; 栈底指针sp+16的位置(也就是除去存储栈底指针和堆栈指针剩余的位置)
...
bl _func
...
ldp x29, x30, [sp, #16] ; 从内存中恢复栈底和堆栈指针
add sp, sp #32 ; 释放空间
ret
C语言编译成汇编文件
1 | xcrun -sdk iphoneos clang -arch arm64 -S CTest.c -o CTest.s |