1、通用寄存器
1.1 arm寄存器
包括31 个通用寄存器,包括 PC(程序计数器)在内,都是 32 位的寄存器。
- r0 ~ r3 主要用于子程序间传递参数
- r4 ~ r11 主要用于保存局部变量,但在 Thumb 程序中,通常只能使用 r4~r7 来保存局部变量
- r12 用作子程序间scratch 寄存器,即 ip 寄存器
- r13 通常用做栈指针,即 sp
- r14 寄存器又被称为连接寄存器(lr),用于保存子程序以及中断的返回地址
- r15 用作程序计数器(pc),由于 ARM 采用了流水线机制,当正确读取了 PC 的值后,该值为当前指令地址加 8 个字节,即 PC 指向当前指令的下两条指令地址。
CPSR和SPSR都是程序状态寄存器,其中SPSR是用来保存中断前的CPSR中的值,以便在中断返回之后恢复处理器程序状态。
1.2 arm64寄存器
31 个通用寄存器,包括 PC(程序计数器)在内,每个寄存器可以存取一个 64 位大小的数。
当使用 x0 - x30
访问时,是一个 64位的数;当使用 w0 - w30
访问时,是一个 32 位的数,访问的是寄存器的 低 32位。
- x0 - x7:这 8 个寄存器主要用来存储传递参数 。如果参数超过 8 个,则会通过栈来传递 ;x0 也用来存放上文方法的返回值。
- FP(x29):保存栈帧地址(栈底指针),指针内容是父函数的栈底地址(即跟父函数的FP寄存器内容一样),这样就可以栈回溯找到调用链上每个函数的调用栈。
- LR(x30):通常称x30为程序链接寄存器,因为这个寄存器会记录着当前函数返回到父函数时的下一条指令或者异常返回的地址。例如我们遇到 Crash 要获取函数的调用栈,其本质就是不断的向上递归每一个 x30 寄存器的记录内容来找到上层调用方。
- SP:保存栈指针,使用 SP/WSP来进行对SP寄存器的访问。指向当前方法栈的顶部。
- PC:程序计数器,俗称PC指针,总是指向即将要执行的下一条指令,在arm64中,软件是不能改写PC寄存器的。
- CPSR:状态寄存器。
2、汇编条件码列表
操作码 | 条件码助记符 | 标志 | 含义 |
---|---|---|---|
0000 | EQ | Z=1 | 相等 |
0001 | NE(Not Equal) | Z=0 | 不相等 |
0010 | CS/HS(Carry Set/High or Same) | C=1 | 无符号数大于或等于 |
0011 | CC/LO(Carry Clear/LOwer) | C=0 | 无符号数小于 |
0100 | MI(MInus) | N=1 | 负数 |
0101 | PL(PLus) | N=0 | 正数或零 |
0110 | VS(oVerflow set) | V=1 | 溢出 |
0111 | VC(oVerflow clear) | V=0 | 没有溢出 |
1000 | HI(High) | C=1,Z=0 | 无符号数大于 |
1001 | LS(Lower or Same) | C=0,Z=1 | 无符号数小于或等于 |
1010 | GE(Greater or Equal) | N=V | 有符号数大于或等于 |
1011 | LT(Less Than) | N!=V | 有符号数小于 |
1100 | GT(Greater Than) | Z=0,N=V | 有符号数大于 |
1101 | LE(Less or Equal) | Z=1,N!=V | 有符号数小于或等于 |
1110 | AL | 任何 | 无条件执行(默认) |
1111 | NV | 任何 | 从不执行 |
3、汇编指令
- mov: 将立即数传送到寄存器/内存单元,或互传内存单元和寄存器两者中的数据。
// 将立即数0x1移动到寄存器x0/rdi中
mov x0, #0x1 / mov $0x1, %rdi
- add: 将某一寄存器的值和另一寄存器的值相加并将结果保存在另一寄存器中,如:
//将寄存器 x0 的值和常量 1 相加后保存在寄存器 x0 中
add x0, x0, #1
//将寄存器 x1 和 x2 的值相加后保存到寄存器 x0 中
add x0, x1, x2
//将寄存器 x1 的值加上寄存器 x2 的值作为地址,再取该内存地址的内容放入寄存器 x0 中
add x0, x1, [x2]
- sub: 将某一寄存器的值和另一寄存器的值相减并将结果保存在另一寄存器中,如:
//将寄存器 x1 和 x2 的值相减后保存到寄存器 x0 中
sub x0, x1, x2
- mul: 将某一寄存器的值和另一个寄存器的值相乘并将结果保存在另一寄存器中,如:
//将寄存器 x1 和 x2 的值相乘后结果保存到寄存器 x0 中
mul x0, x1, x2
- sdiv: (有符号数,对应udiv: 无符号数)将某一寄存器的值和另一个寄存器的值相除并将结果保存在另一寄存器中,如:
//将寄存器 x1 和 x2 的值相除后结果保存到寄存器 x0 中
sdiv x0, x1, x2
- and: 将某一寄存器的值和另一寄存器的值按位与并将结果保存到另一寄存器中,如:
//将寄存器 x0 的值和常量 0xf 按位与后保存到寄存器 x0 中
and x0, x0, #0xf
- orr: 将某一寄存器的值和另一寄存器的值按位或并将结果保存到另一寄存器中,如:
//将寄存器 x0 的值和常量 9 按位或后保存到寄存器 x0 中
orr x0, x0, #9
- eor: 将某一寄存器的值和另一寄存器的值按位异或并将结果保存到另一寄存器中,如:
//将寄存器 x0 的值和常量 0xf 按位异或后保存到寄存器 x0 中
eor x0, x0, #0xf
- str: (store register) 将寄存器中的值写入到内存中,如:
//将寄存器 w9 中的值保存到栈内存 [sp + 0x8] 处
str w9, [sp, #0x8]
- strb: (store register byte) 将寄存器中的值写入到内存中(只存储一个字节),如:
//将寄存器 w8 中的低 1 字节的值保存到栈内存 [sp + 7] 处
strb w8, [sp, #7]
- ldr: (load register) 将内存中的值读取到寄存器中,如:
//将寄存器 x1 的值作为地址,取该内存地址的值放入寄存器 x0 中
ldr x0, [x1]
//将栈内存 [sp + 0x8] 处的值读取到 w8 寄存器中
ldr w8, [sp, #0x8]
//将寄存器 x1 的值作为内存地址,取内该存地址的值放入寄存器 x0 中, 再将寄存器 x1 的值加上 4 放入寄存器 x1 中
ldr x0, [x1], #4
//将寄存器 x1 和寄存器 x2 的值相加作为地址,取该内存地址的值放入寄存器 x0 中
ldr x0, [x1, x2]
- ldrsb: (load register byte) 将内存中的值(只读取一个字节)读取到寄存器中,如:
// 将栈内存 [sp + 7] 出的 低 1 字节的值读取到寄存器 w8 中
ldrsb w8, [sp, #7]
- stur: 同str将寄存器中的值写入到内存中(一般用于负地址运算中),如:
//将寄存器 w10 中的值保存到栈内存 [x29 - 0x04] 处
stur w10, [x29, #-0x4]
- ldur: 同ldr将内存中的值读取到寄存器中(一般用于负地址运算中),如:
//将栈内存 [x29 - 0x04] 处的值读取到 w8 寄存器中
ldur w8, [x29, #-0x4]
- stp: 入栈指令(str的变种指令,可以同时操作两个寄存器),如:
//将 x29, x30 的值存入 sp 偏移 16 个字节的位置
stp x29, x30, [sp, #0x10]
- ldp: 出栈指令(ldr的变种指令,可以同时操作两个寄存器),如:
//将 sp 偏移 16 个字节的值取出来,存入寄存器 x29 和寄存器 x30
ldp x29, x30, [sp, #0x10]
- cbz: 比较操作数是否为0,若为0则跳转(只能跳到后面的指令);
//若w24 (x24寄存器低32位) 中的数据为0,则跳转到0x1b6b798f4处执行 (Note1)。
//Note1: 跳转地址需要在cbz/cbnz指令之后的4~130字节内。
cbz w24, 0x1b6b798f4
- cbnz: 操作数不为0,则跳转(只能跳到后面的指令);
//若w27中的数据不为0,则跳转到0x1b73c9e2c处执行
cbnz w27, 0x1b73c9e2c
- cmp: 比较指令,影响程序状态寄存器 CPSR ;
//通过判断w8中的值与0x3的差值是否为0,即ZF (Tip2) 是否为1,来比较两者是否相等。
//Tip2: ZF是零标志位寄存器。它记录相关指令执行后,其结果是否为0。若结果为0,则ZF=1;若结果为1,则ZF=0。
cmp w8, #0x3
- cset: 比较指令,满足条件,则并置 1,否则置 0,如:
//将寄存器 w8 的值和常量 2 进行比较
cmp w8, #2
//如果是大于(grater than),则将寄存器 w8 的值设置为 1,否则设置为 0
cset w8, gt
-
brk: 可以理解为跳转指令特殊的一种
-
LSL: 逻辑左移
-
LSR: 逻辑右移
-
ASR: 算术右移
-
ROR: 循环右移
-
adrp: 用来定位数据段中的数据用, 因为aslr会导致代码及数据的地址随机化, 用adrp来根据 pc做辅助定位
//执行adrp时,先将操作数270332 (十六进制为0x41ffc) 左移12位得到0x41ffc000,
//再将pc (0x1b703f074) 低12清零得到 0x1b703f000,最后两者相加得到0x1f903b000赋值给x20。通过控制台查看x20中的内容:
//x19 = 0x000000016ef25fb0
//x20 = 0x00000001f903b000
//x21 = 0x0000000281454380
0x1b703f074 <+44>: adrp x20, 270332
0x1b703f078 <+48>: ldr x0, [x20, #0x7a0]
- b: (branch)跳转到某地址(无返回), 不会改变lr (x30)寄存器的值;一般是本方法内的跳转,如while循环,if else等 ,如:
//直接跳转到标签 ‘LLB0_1’ 处开始执行
b LBB0_1
- bl: 跳转到某地址(有返回),先将下一指令地址(即函数返回地址)保存到寄存器lr(x30)中,再进行跳转 ;一般用于不同方法直接的调用 ,如:
//当程序执行完bl指令时,lr中的内容应该是是bl下一条指令的地址,即0x102b7d8a8。通过控制台查看lr中的内容如下:
//fp = 0x000000016d2853c0
//lr = 0x0000000102b7d8a8
//sp = 0x000000016d285350
0x102b7d8a4 <+160>: bl 0x102bda450 ; symbol stub for: objc_msgSend
0x102b7d8a8 <+164>: adrp x0, 107
- blr: 跳转到某寄存器(的值)指向的地址(有返回),先将下一指令地址(即函数返回地址)保存到寄存器lr(x30)中,再进行跳转 ;如:
//先将下一指令地址(‘x20’指向的函数调用后的返回地址)保存到寄存器 ‘lr’ 中,然后再调用 ‘x20’ 指向的函数
blr x20
-
br: 跳转到某寄存器(的值)指向的地址(无返回), 不会改变 lr (x30)寄存器的值。
-
ret: 子程序(函数调用)返回指令,返回地址已默认保存在寄存器 lr (x30) 中
-
mvn: 该指令与mov唯一不同的是:需要对操作数进行按位取反。
-
adr: load a program-relative or register-relative address into a register. 是一条伪指令,遇到该指令时汇编器会生成一条add或sub指令,来计算pc和操作数的和或者差,最后将得到的结果,放在寄存器中。
//生成加法指令,即x0 = 0x1022c3b08 + 0x2fc48 = 0x1022f3750
0x1022c3b08 <+108>: adr x0, #0x2fc48 ; _MergedGlobals + 16
0x1022c3b0c <+112>: nop
//生成减法指令,即x2 = 0x1022c3b10 - 0x390 = 0x1022c3780
0x1022c3b10 <+116>: adr x2, #-0x390 ; initializeAvailabilityCheck
0x1022c3b14 <+120>: nop
- tbz: test branch zero. 测试位为0,则跳转。
//即w24第6位,若为0,则跳转到0x19307005c执行
tbz w24, #0x6, 0x19307005c
- tbnz: test branch no zero. 测试位不为0,则跳转。
//即w24第6位,若不为0,则跳转到0x19307005c执行
tbnz w24, #0x6, 0x19307005c
-
mrs: 用于将程序状态寄存器的内容传送到通用寄存器中
-
ldrsw: 读取内存地址的值给寄存器。
//将以x8寄存器为基地址加上x9寄存器向左偏移两个单位的值后取其地址的值给x9寄存器。
ldrsw x9 ,[x8 ,x9 ,lsl #2]
-
MOVW: 把 16 位立即数放到寄存器的底16位,高16位清0
-
MOVT: 把 16 位立即数放到寄存器的高16位,低 16位不影响
-
BIC: (Bit Clear)位清零指令
// 将寄存器Rn的值与第2个源操作数shifter_operand的值的反码按位做“逻辑与”操作,结果保存到Rd中。
BIC <Rd>,<Rn>,<shifter_operand>
//清除R0中的位0、1和3,保持其余的不变。
BIC R0, R0, #0x1011