到此为止,汇编部分就理解完成了,其中需要重点掌握的是 函数
,因为这与我们日常开发是息息相关的。
下面是所有汇编知识的一个汇总,如果不想看前面文章的,也可以直接查看这里
汇编初识
-
1、汇编概述
使用
助记符代替机器指令
的一种编程语言汇编和机器指令是
一一对应
的关系,拿到二进制就可以反汇编由于汇编和CPU的指令集是对应的,所以汇编不具备移植性
-
2、总线:是由一堆导线的集合
地址总线
:其宽度决定了寻址能力
数据总线
:其宽度决定了CPU数据的吞吐量
控制总线
:其宽度决定了CPU对其他器件的控制能力
-
3、进制
任意进制都是由对应个数的符号组成的,符号可以自定义
-
2/8/16是相对完美的进制,他们之间的关系
3
个二进制 使用一个8进制
标识4
个二进制 使用一个16进制
标识两个16进制可以标识一个字节,即8位
-
数量单位
- 1024 = 1k,1024k = 1M,1024M = 1G
-
容量单位
- 1024 = 1KB,1024KB = 1MB,1024MB = 1GB
-
数据的宽度
- 计算机中的数据是有宽度的,超过了就会溢出
-
4、
寄存器
:CPU为了性能,自内部开辟了一小块临时存储区域浮点向量寄存器
:用于浮点数/向量的存储及运算异常状态寄存器
-
通用寄存器
:除了存放数据有时也有特殊的用途ARM64
拥有32个64位
的通用寄存器X0-X30
以及XZR
(零寄存器)为了
兼容32位
,所以arm64位拥有W0-W28
以及WZR 30
个32位寄存器32位寄存器并不是独立存在的
,例如 W0是X0的低32位
-
PC寄存器
:指令指针寄存器PC寄存器里面的
值
保存的就是CPU接下来需要执行的指令地址
改变PC的值可以改变程序的执行流程
mov
指令不能更改PC寄存器的值
,需要通过bl跳转
指令来改变PC寄存器的值
函数本质(重点掌握!!!!!)
-
1、栈:是一种具有特殊的访问方式的存储空间(后进先出,Last in First out,
LIFO
)- ARM64里面对
栈的操作
是16字节对齐
的
- ARM64里面对
-
2、
SP
和FP
寄存器-
SP
寄存器在任意时刻会保存栈顶的地址
-
FP
寄存器也称为x29
寄存器,属于通用寄存器,但是在某些时刻利用它保存栈底的地址
-
-
3、栈的读写指令
读:
ldr
(load register)指令 LDR、LDP写:
str
(store register)指令 STR、STP
-
4、
bl
指令跳转指令:
bl 标号
,表示程序执行到标号处,将下一条指令的地址保存到lr寄存器B
代表着跳转
L
表示lr
(x30)寄存ios_reverse_02器
-
5、
ret
指令- 类似函数的
return
- 让CPU执行lr寄存器所指向的指令
- 类似函数的
6、避免嵌套函数无法回去:需要保护bl(即
lr
寄存器,存放回家的路),保存在当前函数自己的栈空间-
7、函数参数
arm64中,参数是放在
x0-x7
的8个寄存器中如果是浮点数,就会用浮点数寄存器
如果
超过8个
参数就会用栈传递
-
8、函数返回值
一般函数的返回值使用
x0寄存器
保存如果返回值
大于了8个字节
(x0寄存器大小是8个字节),就会利用内存传递
返回值
-
9、函数局部变量
-
局部变量
存储在栈
空间
-
10、函数的嵌套调用:会将
x29、x30
寄存器入栈保护-
11、状态(标志)寄存器 - CPSR
arm64中
cpsr
寄存器(32位
)为状态寄存器-
最高4位(28、29、30、31)为标志位
-
N
标志(负标记位)执行结果为
负数N=1
执行结果
非负数N=0
-
Z
标志(0标记位)结果
为0则Z=1
结果
非0则Z=0
-
C
标志(无符号溢出)加法:
进位 C=1,否则C=0
减法:
借位 C=0,否则C=1
-
V
标志(有符号溢出)正数+正数=
负数,则V=1
正数+负数=
正数,则V=0
-
循环选择指令(需要多实际操作)
1、全局变量和常量
获取
全局变量和常量
时,会出现adrp
和add
两条指令获得一个地址的情况-
ADRP(Address Page)
-
adrp x0,1
将
PC
寄存器的低12位清零
将1的值,左移12位
以上两个结果相加放入
x0
寄存器
-
通过
ADD
指令获取这页内存中的偏移值
2、条件判断
-
CMP
把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,但不存储结果,只是正确的更改标志
(CMP后面跟的是B.LE
,即else的条件) - 一般CMP做完判断后会进行跳转,后面通常会跟上B指令
BL 标号
:跳转到标号处执行B.LT 标号
:比较结果是小于(less than ),执行标号,否则不跳转B.LE 标号
:比较结果是小于等于(less than or equal to),执行标号,否则不跳转B.GT 标号
:比较结果是大于(greater than),执行标号
,否则不跳转B.GE 标号
:比较结果是大于等于
(greater than or equal to),执行标号,否则不跳转B.EQ 标号
:比较结果是等于
,执行标号,否则不跳转B.NE 标号
:比较结果是不等于(not equal),执行标号,否则不跳转B.HI 标号
:比较结果是无符号大于
,执行标号,否则不跳转B.HS 标号
:比较结果是无符号大于等于
,执行标号,否则不跳转
3、循环
do-while
循环:判断条件在后面
,满足条件往外跳for
循环和while
循环很像:判断条件在里面
,不满足就往外跳
4、switch
1、假设
switch
语句的分支比较少时(例如3,少于4的时候没有意义),没有必要使用次结构,相当于if-else
2、各个分支常量的
差值较大
时,编译器会在效率还是内存进行取舍,这时编译器还是会编译成类似于if-else
的结构3、在
分支比较多
的时候,在编译的时候会生成一个表
(跳转表每个地址四个字节)。
OC反汇编(需要多实际操作)
-
1、编译器优化:
1、设置:
BuildSetting->Optimization Level
2、优化原则:对结果没有任何影响的代码会被编译器优化
3、编译器优化,本质是LLVM的优化过程,实际上优化的是汇编代码(可以理解为
汇编指令会减少
)
-
2、指针:
1、指针的
自增自减
和指向的数据类型宽度
有关,是按照指向的数据类型来运算的(即指针的宽度 - 步长
)2、指针的
运算单位
是指向的数据类型的宽度
3、指针可以通过
if-else
比大小,因为类型是可以相互转换的
-
3、[[self alloc] init] 优化过程
在最初的版本(iOS9)中,相当于两次消息发送
objc_msgSend
iOS11版本 是一次消息发送
objc_alloc + objc_msgSend
iOS13.5.1以上版本,已经没有objc_msgSend,而是
objc_alloc_init
-
4、反汇编分析方式:
通过
LLDB
动态调试通过
Hopper + MachOView
静态分析