《iOS底层原理文章汇总》
1.cmp(Compare)比较指令
CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。
一般CMP做完判断后会进行跳转,后面通常会跟上B指令!
- BL 标号:跳转到标号处执行
- B.LT 标号:比价结果是小于(less than),执行标号,否则不跳转
- B.LE 标号:比较结果是小于等于(less than or qeual to),执行标号,否则不跳转
- B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
- B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
- B.EQ 标号:比较结果是等于(equal to),执行标号,否则不跳转
- B.NE 标号:比较结果是不等于(not equal to),执行标号,否则不跳转
- B.LS 标号:比较结果是无符号小于等于,执行标号,否则不跳转
- B.LO 标号:比较结果是无符号小于,执行标号,否则不跳转
- B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
- B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转
2.Switch
1、假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用此结构,相当于if。
2、各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构。
3、在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节)。
I.当switch中的case大于三个时,此时从将不会产生挨个cmp的现象,会将比较结果存入表中
0x102f7e110 <+28>: ubfx x9, x9, #0, #32
将x9寄存器的高32位清零,低32位保留,为1
cmp x9, #0x4
将x9寄存器中的值和4进行比较
b.hi 0x1008e6178
无符号大于跳0x1008e6178
0x1008e6120 <+44>: adrp x8, 0
0x1008e6124 <+48>: add x8, x8, #0x190 ; =0x190
x8等于0x1008e6190
0x1008e6128 <+52>: ldr x11, [sp]
0x1008e612c <+56>: ldrsw x10, [x8, x11, lsl #2]
从sp寄存器中取出值1存放在x11中
x11中的值1左移2位得到4
x8寄存器的地址+4个字节得到-24(0xffffffe8)作为地址
存入x10中0xffffffffffffffe8
(lldb) p *(int *)(0x1008e6190 + 4)
(int) $3 = -24
0x1008e6130 <+60>: add x9, x8, x10
x8 + x10 = x9 的地址为0x00000001008e6178,跳入x9
0x1008e6178 <+132>: adrp x0, 1
0x1008e617c <+136>: add x0, x0, #0xf85 ; =0xf85
跳入到default中,得到x0寄存器的地址0x1008e7f85,通过MacOView查看0x1008e7f85的值为E595A5E4B99FE4B88DE5819A
4个汉字“啥也不做”占用12个字节
输出default中的内容,“啥也不做”
1.前面相减的被减数是case中的最小值4
2.用最大值和最小值之间的差值8-4=4进行比较
3.5-4(最小值)=1 和 8-4=4进行比较,若大于,比如a=9,则在最小值和最大值的区间外,会大于8,进入default
为什么是无符号大于b.hi呢?
举例,若case为1 - 4 = -3 ,和4比较
有符号比较,-3小于4,满足条件,进入case中显然不符
无符号比较,-3一个超大值,大于4,跳入default,确保不在区间范围内的数跳入default中
为了case的值一定在范围区间内,汇编代码往下走,x8寄存器的地址始终在代码结束后的4个字节,即代码后有一张表,表里面的负数个数为最大值8-最小值4+1(default) = 5个
差值里面的每一个数都给一个偏移值形成一张表,跳转的时候用x8的值直接加上偏移值,每个case都是连续的,表里面的值也是连续的,表里面地址偏移值连续
x8 + case4: -24
x8 +4 case5: default -36
x8 +8 case6: -48
x8 +12 case7: -56
x8 +16 case8: -64
偏移值=(case-最小值)左移两位,即*4,为负数
x8寄存器中的地址值+偏移值地址的字节数得到x9的地址,
x8+负数等于是x8之前的地址,x8已经是汇编代码的最底部往后了,x8是表的开始,偏移表
br直接跳入x9
A.先将参数减去最小case,无符号大于比较
B.通过表拿到地址,地址运行时才知道虚拟地址,如果存地址仍要加上aslr,不如直接用表头加上偏移值算出,地址也过长
II.case很大,需要连续,相差太大会cmp比较相当于if,else结构
3.编译器优化
选择none不优化的时候,main函数中的不起作用的变量还在汇编代码中
若选择Fastest,Smallest则 int a = 1,b = 2会被编译器LLVM优化掉,删掉之后对程序的结果没有影响,编译器都会优化掉
写一个函数,sum直接被优化掉
4.指针:指针的自增自减和执行的数据类型的宽度有关
指针的运算单位是执行的数据类型的宽度
void func(){
int * a;
a = (int *)100;
a++;
printf("%lu",a);
//输出 104
}
void func0(){
int ** a;
a = (int **)100;
a++;
printf("%d\n",a);
//输出 108
}
void func1(){
int * a;
a = (int *)100;
int * b;
b = (int *)200;
printf("%d\n",a-b);
//输出 -25
}
void func2(){
int * a;
a = (int *)100;
int * b;
b = (int *)200;
if(a>b){
printf("a>b\n");
}else{
printf("a
5.指针反汇编
sp+8栈内存空间中存放的是sp+4的地址
void func0(){
int arr[5] = {1,2,3,4,5};
int * a = arr;
for (int i = 0; i
Bad Address报错,从内存sp+0x8中取值0赋值给x8,从x8即0这个地址中取值赋值给w9,报错
二级指针寻址反汇编
两次中括号寻址lar,指针的指针
二级指针在寻址,