一·代码
void A(int a){
switch (a){
case 1:
printf("side");
break;
case 2:
printf("other");
break;
default:
printf("nothing");
break;
}
}
二·汇编分析
前三句汇编代码意义参考前面文章
ldur w0,[x29,#-0x4]
从寄存器w0取出数据
cmp w8,#0x1
;w8-0x1保存到w8里
str w8 ,[sp,#0x8]
;w8再次入栈
b.eq 0x104016218
;判断w0是否相等于1,是跳转执行相应命令adrp x0,1 add x0,x0,#0xf7d
,否执行下一句
b 0x104016208 :ldr w8 #0x2
重复上面的步骤
执行哪一句,取决于main函数调用A时候传入的参数
此时我们多添加几句case,继续研究编译器会对多个case进行什么样的操作
发现疑问:
subs w8,w8,#0x1
w0 - 0x1
cmp x9,#0x3
x9-0x3
str x9,[sp]
sp赋值给x9入栈
无论我传入的参数的几,都会执行减一,减三
也就是说如果不是大于4,说明不是default 执行b.hi 0x102f4a220
adrp x8, 0
此时x8保存的是w0-1的结果是subs x8,x8,#0x1
把x8的值赋值给x9
add x8,x8 , #0x238
取出一个地址放到x8里
拿到x8偏移值,口算x8地址:0x102f4a238 后三位 待会会用到
ldr x11,[sp]
ldrsw x10,[x8,x11,lsl #2]
x8 x9 左移2位赋值给x10 = x8 4
利用lldb调试读取内存 0x102f4a238 偏移四位读取内存值
读到的内存值为:d8 ff ff ff 存放到x10 (负数)
x9 和 case 有某种的联系。未知
三·旧版本Xcode
不知道是因为Xcode版本不同的原因,旧版本的xcode编译出来的汇编会有一些不同
不一样的地方
mov subs 操作用了cmp替换
四·总结
1.拉伸栈空间,保护x29 x30寄存器
2.subs减去case个数的最小值赋值给x9 - 1
3.cmp x9 #0x3 相当于减去case1 2 3 4
4.把x9-1 -3的值入栈保存
5.然后执行b.hi 说明先switch语句先判断default
6.进入到switch语句核心adrp add ldr ldrsw
7.通过指针偏移计算出switch头的偏移值 0x10007a238 保存在x8里
8.使用lldb x 0x10007a238 可以发现,x8里保存的是switch语句的case表
9.x11寄存器保存的内容会影响去case表里的哪一段负数fffffa8 ffffb8 fffffc8
10.合成真实case地址值 add x9 x8 x10
11.跳转到该代码执行,如果传入的switch的参数是1 那么x9=0
12.如果x9=0 取a8 ff ff ff , 如果x9=1 取b8 ff ff ff 以此类推 也就是为什么ldrsw 需要左移2位
13.计算x9 0xff - a8 =0x57 1=0x58 再用x8-0x58得到case的执行地址
14.switch是通过表来储存的,运行效率比较高