ARM64下switch语句

2021-02-03 18:07:52 浏览数 (1)

一·代码

void A(int a){

switch (a){

case 1:

printf("side");

break;

case 2:

printf("other");

break;

default:

printf("nothing");

break;

}

}

二·汇编分析

switchswitch

前三句汇编代码意义参考前面文章

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进行什么样的操作

case4case4

发现疑问:

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 偏移四位读取内存值

x8x8

读到的内存值为:d8 ff ff ff 存放到x10 (负数)

x9 和 case 有某种的联系。未知

三·旧版本Xcode

不知道是因为Xcode版本不同的原因,旧版本的xcode编译出来的汇编会有一些不同

不一样的地方

mov subs 操作用了cmp替换

四·总结

switchswitch

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是通过表来储存的,运行效率比较高

0 人点赞