一:寄存器:
1.查看寄存器的方式,xcode连接真机,断点后,输入register read可查看arm64所有的寄存器.
有:
x0~x7:传递子程序的参数和返回值,使用时不需要保存,多余的参数用堆栈传递,64位 的返回结果保存在x0中,更多参数用堆栈传递。
x8:用于保存子程序的返回地址,使用时不需要保存。
x9~x15:临时寄存器,也叫可变寄存器,子程序使用时不需要保存。
x16~x17:子程序内部调用寄存器(IPx),使用时不需要保存,尽量不要使用。
x18:平台寄存器,它的使用与平台相关,尽量不要使用。
x19~x28:临时寄存器,子程序使用时必须保存。
x29:帧指针寄存器(FP),用于连接栈帧,使用时必须保存。
x30:链接寄存器(LR),用于保存子程序的返回地址。
x31:堆栈指针寄存器(SP),用于指向每个函数的栈顶。
PC:记录当前CPU当前指令的哪一条指令,存储当前CPU正在执行的指令地址,类似IP
CPSR寄存器:状态标识寄存器,每一位都存着0或1,下面有两张图,说明CPSR寄存器
2.一个寄存器x0代表使用64位空间,而w0代表使用32的空间,X和W代表操作数
二:指令:常用的指令,如果想看到比较全的指令,请百度搜索一下,资料很多
MOV指令:
MOV X1,X0 ;将寄存器R0的值传送到寄存器X1
MOV X1,X2,#0X3 ;将寄存器X2的值与立即数0x3相加后传送到寄存器X1
ADD指令:
ADD X0,X1,X2 ;X0=X1+X2
SUB指令:
SUB X0,X1,X2 ;X0=X1-X2
SUB X0,X1 #256 ;X0=X1-256
SUB X0,X2,X3 LSL#1 ;X0=X2-(X3<<1)
CMP指令:
CMP X0,X1 ;将寄存器x0的值和x1的值相减,并将结果放入cpsr寄存器中
CMP X0,#100 ;将x0的值减去100,放入cpsr中,cpsr寄存器有两位表示(lessthan,zero), 如果结果为小于0,则lessthan这一位标识为1,zero位标识为0
LDR指令:也就是从内存read
LDR X0,[R1] ;将存储地址的R1对应的内存中的值取出并放入X0
LDR X0,[R1,#0x8] ;将R1寄存器中地址加上偏移地址8所对应地址的值取出8个字节存入X0
LDUR指令:和LDR的作用一样,指示偏移地址为正时用LDR,为负时用LDUR
LDUR X0,[R1,#-0x8];
LDP指令:P的意思为pair,为一对
LDP x29, x30, [SP, #0x6] ;栈顶指针偏移6个字节地址后,从这开始,依此取16个字节的值,前8个字节给X29,后8个字节给X30
STR指令:存储指令
STR X8, [X9] ;将X8的值存入X9寄存器所存储的地址的内存中去
STR X8, [X9,#0x8] ;0x8代表偏移地址,和LDR的作用一样
STUR:相对应LDUR,同样是偏移地址为负数使用
STR X8,[X9,#-0x8] ;将X8的值存到X9存储地址-8的位置上去
STP:相对LDP,将一对内容存入内存中
STP x29, x30, [sp, #0x08] ;将X29,X30的值存入偏移8的栈中
B:跳转指令,如条件判断跳转符合条件的指令执行,可以看做是if,else,通常与CMP配合使用
条件域:和B组合在一起,如BEQ(如果相等,则跳转到指令对应的地址)
EQ:相等 NE:不相等 GT:大于 GE:大于等于 LT:小于 LE:小于等于
BL指令:函数调用指令,使用此指令会跳转函数,并将当前下一句指令地址存入LR寄存器
WZR/XZR:零寄存器,里面存储的是0,W开头代表32bit,X开头代表64bit
ORR指令:或
EOR指令:异或
RET指令:子程序返回指令,返回地址默认保存在LR(X30)
三:堆栈平衡:
函数调用过程中,会开辟栈空间供局部变量使用,使用后,需要还原栈空间,FP,指令地址等等
不同的函数的堆栈平衡方式有些不同
1.叶子函数:函数内没有调用其他函数
2.非叶子函数:函数内调用了其他函数
//非叶子函数
test(1,2);
void test(int a,int b)
{
int x = 5;
printf("a+b+x=%d",a+b+x);
}
以下代码为执行后的汇编代码
0x100dd1f08 <+0>: sub sp, sp, #0x30 ; 开辟0x30个栈空间
0x100dd1f0c <+4>: stp x29, x30, [sp, #0x20] ;将原fp地址和返回地址入栈
0x100dd1f10 <+8>: add x29, sp, #0x20 ; 将当前fp寄存器指向sp+0x20的位置
0x100dd1f14 <+12>: stur w0, [x29, #-0x4] ;将寄存器w0=1入栈
0x100dd1f18 <+16>: stur w1, [x29, #-0x8] ;将寄存器w1=2入栈
0x100dd1f1c <+20>: mov w8, #0x5 ;w8寄存器存入5的值
0x100dd1f20 <+24>: stur w8, [x29, #-0xc] ;将5入栈
-> 0x100dd1f24 <+28>: ldur w8, [x29, #-0x4] ;取出栈中fp-0x4的值放入w8寄存器
0x100dd1f28 <+32>: ldur w9, [x29, #-0x8] ;取出栈中fp-0x8的值放入w9寄存器
0x100dd1f2c <+36>: add w8, w8, w9 ;将1+2的结果放入w8
0x100dd1f30 <+40>: ldur w9, [x29, #-0xc] ;取出栈中5的值
0x100dd1f34 <+44>: add w10, w8, w9 ;将w8的结果和5相加,放入w10
0x100dd1f38 <+48>: adrp x0, 1
0x100dd1f3c <+52>: add x0, x0, #0x608 ; =0x608
0x100dd1f40 <+56>: mov x9, sp ;将sp的地址放入x9
0x100dd1f44 <+60>: mov x8, x10 ;x10的值放入x8
0x100dd1f48 <+64>: str x8, [x9] ;将a+b+x的结果入栈
0x100dd1f4c <+68>: bl 0x100dd2554 ; 调用printf函数
0x100dd1f50 <+72>: ldp x29, x30, [sp, #0x20] ;将之前入栈的fp和返回地址还原
0x100dd1f54 <+76>: add sp, sp, #0x30 ; 将栈空间释放
0x100dd1f58 <+80>: ret ;返回到调用函数的下一句指令