所有的程序,最终编译为机器(汇编)代码,运行起来,本质都是操作CPU的寄存器和内存,了解了寄存器和内存的操作,就能知道程序具体做了什么
汇编是依赖机器架构的,不同的CPU的指令不同
iOS汇编
- 真机:amr64,GNU 汇编
- 模拟器:x86,AT&T 汇编
寄存器
CPU运行时用于临时存储的数据,都会放到寄存器中
通用寄存器(数据地址寄存器)
主要用户存储临时数据,例如加法运算,地址操作等
- 64bit 的:0x ~ x28,有29个寄存器
- 32bit 的:w0 ~ w28(属于 x0 ~ x28的低32bit)
其中 x0 ~ x7通常用来存放函数的参数,超过的食用堆栈来存放
x0 通常用来存放函数的返回值
通过 lldb 存取寄存器
1 | # 读取所有寄存器地址的值 |
再xcode查看寄存器的值
PC寄存器(program counter)
PC寄存器为指令指针寄存器,用于存放当前CPU要读取的指令的地址,PC寄存器指到哪,CPU执行到哪
SP寄存器(stack pointer registers)
是一个指向堆栈顶部的寄存器,通常用于为方法执行开辟空间和回收空间,进入方法入栈,离开方法出栈,例如下面
1 | Test`main: |
CPSR状态寄存器(current program status register)
CPSR是状态寄存器,用于存放程序运行中一些状态标识。其他寄存器是用来存放数据的,而CPSR寄存器是按位起作用的,它的每一位都有专门的含义,通常存放比较结果,溢出等信息,下面为C语言判断语句的
1 | if (a == 10) { |
对应的汇编代码
1 | ... |
高速缓存
由于CPU读取指令需要从内存中读取,而内存的读写速度要远远低于寄存器的速度,为了拟补这里的时间差,通常CPU还会提供一个速度介于内存和寄存器的高速缓存,再程序运行前,会先将要执行的指令和数据提前读取到高速缓存中,而CPU从高速缓存中读取指令来执行
指令
汇编使用;
分号开头表示注释
函数定义
声名 test 函数
1 | ; global 用于声明函数为公开的,可以被外部调用 |
mov:赋值操作
mov指令只允许操作寄存器的,不允许直接操作内存
1 | ; 把 0x8 赋值给寄存器 x0 |
add: 加法运算
add只能对寄存器做加法,内存中的数据必须先放到寄存器中
1 | ; x1 = x1 + x0 |
sub:减法
1 | ; sp的指针往下移 0x20 |
ret指令
返回函数调用出,相当于c语言的return,与bl
和lr
配合使用,本质是将lr寄存器的值赋值给pc寄存器
1 | ret |
cmp:比较两个数(减法)
比较结果会被放在 cpsr(程序状态寄存器里面),cpsr使用标识位控制
1 | mov x0, #0x3 |
b跳转指令
相当于 C 语言的 goto
1 | ; 直接跳到 mycode |
b
指令可以带条件,通常跟 cmp 配合使用
- eq: 相等
- ne:不相等
- gt:大于
- lt:小于
- ge:大于或等于
- le:小于或等于
1 | mov x0, #0x1 |
bl指令
可以带返回的跳转指令,跳转执行完成后会回到调用的地方继续往下执行,相当于函数调用
1 | privatecode: |
bl 会将吓一跳指令的地址存储到 lr(x30)寄存器中
ldr/ldur/ldp(从内存读取数据)
如果偏移地址是正数,用ldr
,如果是负数,用ldur
1 | ; 从x1的地址中读取数据值到 x0 中 |
ldp
,读取一对数据到(2个)寄存器
1 | ; x1偏移0x4的地址分别读取4个字节到 w1 和 w2 |
str/stur/stp(往内存写数据)
寄存器在左边,如果偏移地址是正数,用str
,如果是负数,用stur
1 | ; 把 w0的数据写到 x1的地址 |
stp
: 写入2个寄存器的数据到连续的内存
1 | ; x1偏移0x4的地址分别读取4个字节到 w1 和 w2 |
wzr/xzr(零寄存器)
里面存储的值为0,不能在lldb中读/写,表示清零操作(false, nil, 0)
- wzr(32bit)
- xzr(64bit)
lr寄存器
lr
:存储函数的返回地址,与bl
配合使用(bl执行跳转执行指令ret后,会回到调用的地方,就是根据lr存放的地址)
函数的分类
- 叶子函数:内部不再调用其他函数的函数,不用再开辟栈空间
- 非叶子函数:函数内部会调用其他的函数