寄存器
寄存器是CPU的组成成分, 存放着指令、数据和地址
,速度比内存块
分为通用寄存器
、状况寄存器
和浮点寄存器
通用寄存器
ARM64有31个通用寄存器, 每个寄存器可以存储64位数据(8个字节)
运用 X0-X30
表明64位
的数
运用 W0-W30
时,访问这些寄存器的低32位
, 会将高32位清0。
-
X0-X7
传递函数的参数, 如果有更多的参数需要运用栈来传递, X0 用来存储函数的回来值
-
SP(Stack Pointer)
栈指针寄存器, 指向栈的顶部, WSP表明栈指针的最低32位
-
FP(Frame Pointer)
即X29, 栈指针寄存器, 指向栈的底部
-
LR(Link Register)
X30 链接寄存器, 存储着函数调用完成时的
回来地址
用来存储函数调用栈盯梢, 程序在崩溃时能够将函数调用栈打印便是借助LR寄存器来完成的
-
PC(Program Counter)
保存下一条指令的内存地址, 通常在调试状况下看到的 PC 值都是当时
断点处的地址
状况寄存器(CPSR)
保存指令运行成果的信息
依据运算的成果来设置状况寄存器的标志位
CPU内部进行置位, 不能用程序赋值
28-31位(最终四位
0011 == 6)分别代表 V C Z N
-
N(Negative)
符号整数进行运算时, N=1表明成果为负数, N=0表明成果>=0
-
Z(Zero)
Z=1表明运算成果为0, Z=0表明成果为非0
-
C(Carry)
加减法运算是否发生进位
-
V(Overflow)
加减法运算时发生符号位溢出
浮点寄存器(FPU)
用于浮点数存储和运算, V0-V31
常用指令
算法指令
-
ADD 加
ADD X0,X1,X2: X0=X1+X2
-
SUB 减
SUB X0,X1,X2: X0==X1-X2
-
MUL 乘
MUL X0,X1,X2: X0=X1*X2
-
SDIV
有符号数
除法运算SDIV X0,X1,X2: X0=X1/X2
-
UDIV
无符号
除 -
CMP X28,X0
X28,X0
相减
,更新CPSR
中条件标志位 -
CMN CMN X28,X0
X28,X0
相加
,更新CPSR
中条件标志位 -
ADDS/SUBS
带
S
的指令运算成果会影响 CPSR中的条件标志位
条件跳转
-
B.cond
B.cond label 若cond为真,则跳转到 label
-
CBNZ
CBNZ Xn,label 若Xn!=0,则跳转到label
-
CBZ
CBZ Xn,label 若Xn==0, 则跳转到label
无条件跳转
-
B
B label 无条件跳转
-
BL
BL label 无条件跳转,
回来地址保存到LR寄存器中
-
BLR
BLR Xn 无条件跳转到Xn寄存器的地址,回来地址保存到LR寄存器中
-
BR
BR Xn 无条件跳转到Xn寄存器的地址
-
RET
回来到LR中存储地址
逻辑指令
-
AND
AND X0,X1,X2: X0=X1&X2
-
EOR
X0=X1^X2
-
ORR
X0=X1|X2
赋值指令
MOV
MOV X19,X1 X19=X1
地址偏移
ADR
ADR Xn,label Xn=PC+label
Load/Store指令
LDR
Xn,addr Load
内存地址addr -> Xn
STR
Xn,addr
Xn -> 内存地址addr
LDP
Xn1,Xn2,addr
内存地址addr -> Xn1和Xn2
P(Pair)表明一对,一起操作两个寄存器
STP
Xn1,Xn2,addr
Xn1和Xn2 -> addr
LDUR
Xn,[base, #XXX]
addr(base+#XXX) -> Xn
STUR
Xn,[base, #XXX]
Xn -> base+#XXX的内存地址
扩展含义
B 无符号8bit
SB 有符号8bit
H: 无符号116bit
SH 有符号16位
W 无符号32位
SW 有符号32位
函数调用汇编
界说一个最简略的函数调用
int addTwoValue(int a, int b) {
return a + b;
}
int main() {
return addTwoValue(1, 2);
}
查看汇编源码
main 函数
ZZDemo`main:
0x1027ba1e8 <+0>: sub sp, sp, #0x20 ; 开辟0x30大小的栈空间
0x1027ba1ec <+4>: stp x29, x30, [sp, #0x10] ;保存当时函数的x29(FP)和X30(LR)到栈中
0x1027ba1f0 <+8>: add x29, sp, #0x10 ; 举高栈底,切换到子函数栈空间
0x1027ba1f4 <+12>: stur wzr, [x29, #-0x4]
0x1027ba1f8 <+16>: mov w0, #0x1 ; 第一个参数1保存到w0
0x1027ba1fc <+20>: mov w1, #0x2 ; 第二个参数2保存到w1
-> 0x1027ba200 <+24>: bl 0x1027ba1c8 ;调用 addTwoValue函数
0x1027ba204 <+28>: ldp x29, x30, [sp, #0x10] ;从栈中康复当时函数的x29(FP)和X30(LR)
0x1027ba208 <+32>: add sp, sp, #0x20 ; 回收栈空间
0x1027ba20c <+36>: ret
ZZDemo`addTwoValue:
0x100c3e1a8 <+0>: sub sp, sp, #0x10
0x100c3e1ac <+4>: str w0, [sp, #0xc]
0x100c3e1b0 <+8>: str w1, [sp, #0x8]
-> 0x100c3e1b4 <+12>: ldr w8, [sp, #0xc]
0x100c3e1b8 <+16>: ldr w9, [sp, #0x8]
0x100c3e1bc <+20>: add w0, w8, w9
0x100c3e1c0 <+24>: add sp, sp, #0x10
0x100c3e1c4 <+28>: ret
一般函数执行流程如下
- 分配栈空间
- 如果有子函数调用, 保存当时函数的形参到栈中
- 如果有子函数调用, 保存当时函数的FP和LR
- 举高栈底, 切换到子函数栈
- 设置子函数的实参
- 调用子函数,回来成果
- 康复当时函数形参
- 康复当时函数的FP和LR
- 其他操作
- 回收栈空间
- 回来
Objective-C汇编
OC中办法调用是通过消息机制来完成的
@implementation AppDelegate
- (void)printValue1:(NSString*)value1 value2:(NSString*)value2 {
NSLog(@"%@, %@", value1, value2);
}
@end
int main() {
AppDelegate *delegate = [[AppDelegate alloc] init];
[delegate printValue1:@"111" value2:@"222"];
}
断点到函数处执行
register read
x0 = 0x000000028261c0e0
x1 = 0x00000001040d3109 "printValue1:value2:"
x2 = 0x00000001040d4070 @"111"
x3 = 0x00000001040d4090 @"222"
x4 = 0x0000000000000004
x5 = 0x0000000007600000
x6 = 0x0000000000000000
x7 = 0x0000000000000000
po $x0
<AppDelegate: 0x28261c0e0>
在 OC 中
x0
寄存器保存了对象自身
x1
保存了办法名
,
x2-x7
保存了函数的其他参数